blob: d1891fed315b2e580b3b23316e75dc41ad935a3b [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; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000080 virtual ~CircleEdgeEffect() {}
81
mtklein36352bf2015-03-25 18:17:31 -070082 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000083
84 inline bool isStroked() const { return fStroke; }
85
joshualittb0a8a372014-09-23 09:50:21 -070086 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000087 public:
joshualitteb2a6762014-12-04 11:35:33 -080088 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080089 const GrBatchTracker&)
90 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000091
mtklein36352bf2015-03-25 18:17:31 -070092 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080093 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080094 GrGLGPBuilder* pb = args.fPB;
95 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080096 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
97
joshualittabb52a12015-01-13 15:02:10 -080098 // emit attributes
99 vsBuilder->emitAttributes(ce);
100
joshualitt74077b92014-10-24 11:26:03 -0700101 GrGLVertToFrag v(kVec4f_GrSLType);
102 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800103 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104
joshualitt9b989322014-12-15 14:16:27 -0800105 // Setup pass through color
106 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
107 &fColorUniform);
108
joshualittabb52a12015-01-13 15:02:10 -0800109 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800110 this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800111
112 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800113 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700114 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800115
egdaniel29bee0f2015-04-29 11:54:42 -0700116 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700117 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800118 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800119 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800120 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
121 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700122 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000123 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000124
joshualitt2dd1ae02014-12-03 06:24:10 -0800125 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000126 }
127
robertphillips46d36f02015-01-18 08:14:14 -0800128 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800129 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700130 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700131 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800132 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800133 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800134 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800135 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
136 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800137 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000138 }
139
joshualitt9b989322014-12-15 14:16:27 -0800140 virtual void setData(const GrGLProgramDataManager& pdman,
141 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700142 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800143 this->setUniformViewMatrix(pdman, gp.viewMatrix());
144
joshualitt9b989322014-12-15 14:16:27 -0800145 const BatchTracker& local = bt.cast<BatchTracker>();
146 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
147 GrGLfloat c[4];
148 GrColorToRGBAFloat(local.fColor, c);
149 pdman.set4fv(fColorUniform, 1, c);
150 fColor = local.fColor;
151 }
152 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000153
154 private:
joshualitt9b989322014-12-15 14:16:27 -0800155 GrColor fColor;
156 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700157 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000158 };
159
joshualitteb2a6762014-12-04 11:35:33 -0800160 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700161 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700162 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800163 GLProcessor::GenKey(*this, bt, caps, b);
164 }
165
joshualittabb52a12015-01-13 15:02:10 -0800166 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700167 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800168 return SkNEW_ARGS(GLProcessor, (*this, bt));
169 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000170
mtklein36352bf2015-03-25 18:17:31 -0700171 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800172 BatchTracker* local = bt->cast<BatchTracker>();
173 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800174 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800175 }
176
joshualitt50cb76b2015-04-28 09:17:05 -0700177 bool onCanMakeEqual(const GrBatchTracker& m,
178 const GrGeometryProcessor& that,
179 const GrBatchTracker& t) const override {
180 const BatchTracker& mine = m.cast<BatchTracker>();
181 const BatchTracker& theirs = t.cast<BatchTracker>();
182 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
183 that, theirs.fUsesLocalCoords) &&
184 CanCombineOutput(mine.fInputColorType, mine.fColor,
185 theirs.fInputColorType, theirs.fColor);
186 }
187
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000188private:
joshualittd27f73e2014-12-29 07:43:36 -0800189 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800190 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800191 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800192 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
193 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800194 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000195 fStroke = stroke;
196 }
197
joshualitt50cb76b2015-04-28 09:17:05 -0700198 bool onIsEqual(const GrGeometryProcessor& other) const override {
199 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
200 return cee.fStroke == fStroke;
201 }
202
203 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
204 out->setUnknownSingleComponent();
205 }
206
joshualitt9b989322014-12-15 14:16:27 -0800207 struct BatchTracker {
208 GrGPInput fInputColorType;
209 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800210 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800211 };
212
joshualitt71c92602015-01-14 08:12:47 -0800213 const Attribute* fInPosition;
214 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000215 bool fStroke;
216
joshualittb0a8a372014-09-23 09:50:21 -0700217 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000218
joshualitt249af152014-09-15 11:41:13 -0700219 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220};
221
joshualittb0a8a372014-09-23 09:50:21 -0700222GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223
joshualittb0a8a372014-09-23 09:50:21 -0700224GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
225 GrContext* context,
226 const GrDrawTargetCaps&,
227 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800228 return CircleEdgeEffect::Create(GrRandomColor(random),
229 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700230 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231}
232
233///////////////////////////////////////////////////////////////////////////////
234
235/**
236 * 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 +0000237 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
238 * in both x and y directions.
239 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000240 * 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 +0000241 */
242
joshualitt249af152014-09-15 11:41:13 -0700243class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000244public:
joshualittd27f73e2014-12-29 07:43:36 -0800245 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
246 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247 }
248
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249 virtual ~EllipseEdgeEffect() {}
250
mtklein36352bf2015-03-25 18:17:31 -0700251 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800252
joshualitt71c92602015-01-14 08:12:47 -0800253 const Attribute* inPosition() const { return fInPosition; }
254 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
255 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700256
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000257 inline bool isStroked() const { return fStroke; }
258
joshualittb0a8a372014-09-23 09:50:21 -0700259 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000260 public:
joshualitteb2a6762014-12-04 11:35:33 -0800261 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800262 const GrBatchTracker&)
263 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264
mtklein36352bf2015-03-25 18:17:31 -0700265 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800266 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800267 GrGLGPBuilder* pb = args.fPB;
268 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800269 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000270
joshualittabb52a12015-01-13 15:02:10 -0800271 // emit attributes
272 vsBuilder->emitAttributes(ee);
273
joshualitt74077b92014-10-24 11:26:03 -0700274 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
275 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700276 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800277 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000278
joshualitt74077b92014-10-24 11:26:03 -0700279 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
280 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
281 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800282 ee.inEllipseRadii()->fName);
283
joshualitt9b989322014-12-15 14:16:27 -0800284 // Setup pass through color
285 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
286 &fColorUniform);
287
joshualittabb52a12015-01-13 15:02:10 -0800288 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800289 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800290
291 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800292 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800293 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800294
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000295 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700296 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700297 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
298 ellipseRadii.fsIn());
299 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
300 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
301 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
302
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000303 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700304 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
305 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
306 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000307
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000308 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800309 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700310 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
311 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
312 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
313 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
314 ellipseRadii.fsIn());
315 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
316 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000317 }
318
joshualitt2dd1ae02014-12-03 06:24:10 -0800319 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000320 }
321
robertphillips46d36f02015-01-18 08:14:14 -0800322 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800323 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700324 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700325 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800326 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800327 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800328 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800329 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
330 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800331 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000332 }
333
joshualitt9b989322014-12-15 14:16:27 -0800334 virtual void setData(const GrGLProgramDataManager& pdman,
335 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700336 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800337 this->setUniformViewMatrix(pdman, gp.viewMatrix());
338
joshualitt9b989322014-12-15 14:16:27 -0800339 const BatchTracker& local = bt.cast<BatchTracker>();
340 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
341 GrGLfloat c[4];
342 GrColorToRGBAFloat(local.fColor, c);
343 pdman.set4fv(fColorUniform, 1, c);
344 fColor = local.fColor;
345 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 }
347
348 private:
joshualitt9b989322014-12-15 14:16:27 -0800349 GrColor fColor;
350 UniformHandle fColorUniform;
351
joshualitt249af152014-09-15 11:41:13 -0700352 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000353 };
354
joshualitteb2a6762014-12-04 11:35:33 -0800355 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700356 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700357 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800358 GLProcessor::GenKey(*this, bt, caps, b);
359 }
360
joshualittabb52a12015-01-13 15:02:10 -0800361 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700362 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800363 return SkNEW_ARGS(GLProcessor, (*this, bt));
364 }
365
mtklein36352bf2015-03-25 18:17:31 -0700366 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800367 BatchTracker* local = bt->cast<BatchTracker>();
368 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800369 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800370 }
371
joshualitt50cb76b2015-04-28 09:17:05 -0700372 bool onCanMakeEqual(const GrBatchTracker& m,
373 const GrGeometryProcessor& that,
374 const GrBatchTracker& t) const override {
375 const BatchTracker& mine = m.cast<BatchTracker>();
376 const BatchTracker& theirs = t.cast<BatchTracker>();
377 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
378 that, theirs.fUsesLocalCoords) &&
379 CanCombineOutput(mine.fInputColorType, mine.fColor,
380 theirs.fInputColorType, theirs.fColor);
381 }
382
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000383private:
joshualittd27f73e2014-12-29 07:43:36 -0800384 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800385 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800386 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800387 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
388 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800389 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800390 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800391 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000392 fStroke = stroke;
393 }
394
joshualitt50cb76b2015-04-28 09:17:05 -0700395 bool onIsEqual(const GrGeometryProcessor& other) const override {
396 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
397 return eee.fStroke == fStroke;
398 }
399
400 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
401 out->setUnknownSingleComponent();
402 }
403
joshualitt9b989322014-12-15 14:16:27 -0800404 struct BatchTracker {
405 GrGPInput fInputColorType;
406 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800407 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800408 };
409
joshualitt71c92602015-01-14 08:12:47 -0800410 const Attribute* fInPosition;
411 const Attribute* fInEllipseOffset;
412 const Attribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000413 bool fStroke;
414
joshualittb0a8a372014-09-23 09:50:21 -0700415 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000416
joshualitt249af152014-09-15 11:41:13 -0700417 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000418};
419
joshualittb0a8a372014-09-23 09:50:21 -0700420GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000421
joshualittb0a8a372014-09-23 09:50:21 -0700422GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
423 GrContext* context,
424 const GrDrawTargetCaps&,
425 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800426 return EllipseEdgeEffect::Create(GrRandomColor(random),
427 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700428 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000429}
430
431///////////////////////////////////////////////////////////////////////////////
432
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000434 * 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 +0000435 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
436 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
437 * using differentials.
438 *
439 * The result is device-independent and can be used with any affine matrix.
440 */
441
joshualitt249af152014-09-15 11:41:13 -0700442class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000443public:
444 enum Mode { kStroke = 0, kHairline, kFill };
445
joshualitt8059eb92014-12-29 15:10:07 -0800446 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
447 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000448 }
449
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000450 virtual ~DIEllipseEdgeEffect() {}
451
mtklein36352bf2015-03-25 18:17:31 -0700452 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000453
joshualitt71c92602015-01-14 08:12:47 -0800454 const Attribute* inPosition() const { return fInPosition; }
455 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
456 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700457
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000458 inline Mode getMode() const { return fMode; }
459
joshualittb0a8a372014-09-23 09:50:21 -0700460 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000461 public:
joshualitteb2a6762014-12-04 11:35:33 -0800462 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800463 const GrBatchTracker&)
464 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000465
mtklein36352bf2015-03-25 18:17:31 -0700466 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800467 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800468 GrGLGPBuilder* pb = args.fPB;
469 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800470 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000471
joshualittabb52a12015-01-13 15:02:10 -0800472 // emit attributes
473 vsBuilder->emitAttributes(ee);
474
joshualitt74077b92014-10-24 11:26:03 -0700475 GrGLVertToFrag offsets0(kVec2f_GrSLType);
476 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700477 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800478 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700479
480 GrGLVertToFrag offsets1(kVec2f_GrSLType);
481 args.fPB->addVarying("EllipseOffsets1", &offsets1);
482 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800483 ee.inEllipseOffsets1()->fName);
484
joshualitt9b989322014-12-15 14:16:27 -0800485 // Setup pass through color
486 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
487 &fColorUniform);
488
joshualittabb52a12015-01-13 15:02:10 -0800489 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800490 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800491
492 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800493 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800494 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800495
egdaniel29bee0f2015-04-29 11:54:42 -0700496 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700497 SkAssertResult(fsBuilder->enableFeature(
498 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000499 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700500 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
501 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
502 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
503 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
504 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
505 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
506 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000507
joshualitt74077b92014-10-24 11:26:03 -0700508 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000509 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700510 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
511 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800512 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700514 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
515 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000516 } else {
joshualitt74077b92014-10-24 11:26:03 -0700517 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518 }
519
520 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800521 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700522 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
523 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
524 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
525 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
526 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
527 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
528 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
529 offsets1.fsIn());
530 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
531 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000532 }
533
joshualitt2dd1ae02014-12-03 06:24:10 -0800534 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000535 }
536
robertphillips46d36f02015-01-18 08:14:14 -0800537 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800538 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700539 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700540 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800541 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800542 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800543 uint16_t key = ellipseEffect.getMode();
robertphillips46d36f02015-01-18 08:14:14 -0800544 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
545 key |= ComputePosKey(gp.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800546 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000547 }
548
joshualitt9b989322014-12-15 14:16:27 -0800549 virtual void setData(const GrGLProgramDataManager& pdman,
550 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700551 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800552 this->setUniformViewMatrix(pdman, gp.viewMatrix());
553
joshualitt9b989322014-12-15 14:16:27 -0800554 const BatchTracker& local = bt.cast<BatchTracker>();
555 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
556 GrGLfloat c[4];
557 GrColorToRGBAFloat(local.fColor, c);
558 pdman.set4fv(fColorUniform, 1, c);
559 fColor = local.fColor;
560 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000561 }
562
563 private:
joshualitt9b989322014-12-15 14:16:27 -0800564 GrColor fColor;
565 UniformHandle fColorUniform;
566
joshualitt249af152014-09-15 11:41:13 -0700567 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000568 };
569
joshualitteb2a6762014-12-04 11:35:33 -0800570 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700571 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700572 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800573 GLProcessor::GenKey(*this, bt, caps, b);
574 }
575
joshualittabb52a12015-01-13 15:02:10 -0800576 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700577 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800578 return SkNEW_ARGS(GLProcessor, (*this, bt));
579 }
580
mtklein36352bf2015-03-25 18:17:31 -0700581 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800582 BatchTracker* local = bt->cast<BatchTracker>();
583 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800584 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800585 }
586
joshualitt50cb76b2015-04-28 09:17:05 -0700587 bool onCanMakeEqual(const GrBatchTracker& m,
588 const GrGeometryProcessor& that,
589 const GrBatchTracker& t) const override {
590 const BatchTracker& mine = m.cast<BatchTracker>();
591 const BatchTracker& theirs = t.cast<BatchTracker>();
592 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
593 that, theirs.fUsesLocalCoords) &&
594 CanCombineOutput(mine.fInputColorType, mine.fColor,
595 theirs.fInputColorType, theirs.fColor);
596 }
597
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000598private:
joshualitt8059eb92014-12-29 15:10:07 -0800599 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
600 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800601 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800602 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
603 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800604 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800605 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800606 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000607 fMode = mode;
608 }
609
joshualitt50cb76b2015-04-28 09:17:05 -0700610 bool onIsEqual(const GrGeometryProcessor& other) const override {
611 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
612 return eee.fMode == fMode;
613 }
614
615 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
616 out->setUnknownSingleComponent();
617 }
618
joshualitt9b989322014-12-15 14:16:27 -0800619 struct BatchTracker {
620 GrGPInput fInputColorType;
621 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800622 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800623 };
624
joshualitt71c92602015-01-14 08:12:47 -0800625 const Attribute* fInPosition;
626 const Attribute* fInEllipseOffsets0;
627 const Attribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000628 Mode fMode;
629
joshualittb0a8a372014-09-23 09:50:21 -0700630 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000631
joshualitt249af152014-09-15 11:41:13 -0700632 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000633};
634
joshualittb0a8a372014-09-23 09:50:21 -0700635GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000636
joshualittb0a8a372014-09-23 09:50:21 -0700637GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
638 GrContext* context,
639 const GrDrawTargetCaps&,
640 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800641 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700642 GrTest::TestMatrix(random),
joshualitt8059eb92014-12-29 15:10:07 -0800643 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000644}
645
646///////////////////////////////////////////////////////////////////////////////
647
joshualitt9853cce2014-11-17 14:22:48 -0800648bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800649 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800650 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800651 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800652 bool useAA,
653 const SkRect& oval,
654 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000655{
bsalomoned0bcad2015-05-04 10:36:42 -0700656 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000657
658 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000659 return false;
660 }
661
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000662 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800663 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800664 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000665 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700666 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800667 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800668 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000669 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800670 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800671 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
672 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000673 } else {
674 return false;
675 }
676
677 return true;
678}
679
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000680///////////////////////////////////////////////////////////////////////////////
681
joshualitt76e7fb62015-02-11 08:52:27 -0800682class CircleBatch : public GrBatch {
683public:
684 struct Geometry {
685 GrColor fColor;
686 SkMatrix fViewMatrix;
687 SkScalar fInnerRadius;
688 SkScalar fOuterRadius;
689 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700690 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800691 };
692
bsalomoned0bcad2015-05-04 10:36:42 -0700693 static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); }
joshualitt76e7fb62015-02-11 08:52:27 -0800694
mtklein36352bf2015-03-25 18:17:31 -0700695 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800696
mtklein36352bf2015-03-25 18:17:31 -0700697 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800698 // When this is called on a batch, there is only one geometry bundle
699 out->setKnownFourComponents(fGeoData[0].fColor);
700 }
701
mtklein36352bf2015-03-25 18:17:31 -0700702 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800703 out->setUnknownSingleComponent();
704 }
705
mtklein36352bf2015-03-25 18:17:31 -0700706 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800707 // Handle any color overrides
708 if (init.fColorIgnored) {
709 fGeoData[0].fColor = GrColor_ILLEGAL;
710 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
711 fGeoData[0].fColor = init.fOverrideColor;
712 }
713
714 // setup batch properties
715 fBatch.fColorIgnored = init.fColorIgnored;
716 fBatch.fColor = fGeoData[0].fColor;
717 fBatch.fStroke = fGeoData[0].fStroke;
718 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
719 fBatch.fCoverageIgnored = init.fCoverageIgnored;
720 }
721
mtklein36352bf2015-03-25 18:17:31 -0700722 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800723 SkMatrix invert;
724 if (!this->viewMatrix().invert(&invert)) {
725 return;
726 }
727
728 // Setup geometry processor
729 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
730 this->stroke(),
731 invert));
732
733 batchTarget->initDraw(gp, pipeline);
734
735 // TODO this is hacky, but the only way we have to initialize the GP is to use the
736 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
737 // everywhere we can remove this nastiness
738 GrPipelineInfo init;
739 init.fColorIgnored = fBatch.fColorIgnored;
740 init.fOverrideColor = GrColor_ILLEGAL;
741 init.fCoverageIgnored = fBatch.fCoverageIgnored;
742 init.fUsesLocalCoords = this->usesLocalCoords();
743 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
744
745 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800746 size_t vertexStride = gp->getVertexStride();
747 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700748 QuadHelper helper;
749 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, vertexStride,
750 instanceCount));
751 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800752 return;
753 }
754
joshualitt76e7fb62015-02-11 08:52:27 -0800755 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700756 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800757
bsalomonb5238a72015-05-05 07:49:49 -0700758 SkScalar innerRadius = geom.fInnerRadius;
759 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800760
bsalomonb5238a72015-05-05 07:49:49 -0700761 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800762
763 // The inner radius in the vertex data must be specified in normalized space.
764 innerRadius = innerRadius / outerRadius;
765 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
766 verts[0].fOffset = SkPoint::Make(-1, -1);
767 verts[0].fOuterRadius = outerRadius;
768 verts[0].fInnerRadius = innerRadius;
769
770 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
771 verts[1].fOffset = SkPoint::Make(-1, 1);
772 verts[1].fOuterRadius = outerRadius;
773 verts[1].fInnerRadius = innerRadius;
774
775 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
776 verts[2].fOffset = SkPoint::Make(1, 1);
777 verts[2].fOuterRadius = outerRadius;
778 verts[2].fInnerRadius = innerRadius;
779
780 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
781 verts[3].fOffset = SkPoint::Make(1, -1);
782 verts[3].fOuterRadius = outerRadius;
783 verts[3].fInnerRadius = innerRadius;
784
bsalomonb5238a72015-05-05 07:49:49 -0700785 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800786 }
bsalomone64eb572015-05-07 11:35:55 -0700787 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -0800788 }
789
790 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
791
792private:
793 CircleBatch(const Geometry& geometry) {
794 this->initClassID<CircleBatch>();
795 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700796
797 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800798 }
799
mtklein36352bf2015-03-25 18:17:31 -0700800 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800801 CircleBatch* that = t->cast<CircleBatch>();
802
803 // TODO use vertex color to avoid breaking batches
804 if (this->color() != that->color()) {
805 return false;
806 }
807
808 if (this->stroke() != that->stroke()) {
809 return false;
810 }
811
812 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
813 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
814 return false;
815 }
816
817 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700818 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800819 return true;
820 }
821
822 GrColor color() const { return fBatch.fColor; }
823 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
824 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
825 bool stroke() const { return fBatch.fStroke; }
826
827 struct BatchTracker {
828 GrColor fColor;
829 bool fStroke;
830 bool fUsesLocalCoords;
831 bool fColorIgnored;
832 bool fCoverageIgnored;
833 };
834
joshualitt76e7fb62015-02-11 08:52:27 -0800835 BatchTracker fBatch;
836 SkSTArray<1, Geometry, true> fGeoData;
837};
838
joshualitt3e708c52015-04-30 13:49:27 -0700839static GrBatch* create_circle_batch(GrColor color,
840 const SkMatrix& viewMatrix,
841 bool useCoverageAA,
842 const SkRect& circle,
joshualittd96a67b2015-05-05 14:09:05 -0700843 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000844 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800845 viewMatrix.mapPoints(&center, 1);
846 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
847 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000848
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000849 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000850 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
851 SkStrokeRec::kHairline_Style == style;
852 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000853
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000854 SkScalar innerRadius = 0.0f;
855 SkScalar outerRadius = radius;
856 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000857 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000858 if (SkScalarNearlyZero(strokeWidth)) {
859 halfWidth = SK_ScalarHalf;
860 } else {
861 halfWidth = SkScalarHalf(strokeWidth);
862 }
863
864 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000865 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000866 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000867 }
868 }
869
bsalomonce1c8862014-12-15 07:11:22 -0800870 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
871 // computation because the computed alpha is zero, rather than 50%, at the radius.
872 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
873 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000874 outerRadius += SK_ScalarHalf;
875 innerRadius -= SK_ScalarHalf;
876
joshualitt76e7fb62015-02-11 08:52:27 -0800877 CircleBatch::Geometry geometry;
878 geometry.fViewMatrix = viewMatrix;
879 geometry.fColor = color;
880 geometry.fInnerRadius = innerRadius;
881 geometry.fOuterRadius = outerRadius;
882 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700883 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
884 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000885
joshualitt3e708c52015-04-30 13:49:27 -0700886 return CircleBatch::Create(geometry);
887}
888
889void GrOvalRenderer::drawCircle(GrDrawTarget* target,
890 GrPipelineBuilder* pipelineBuilder,
891 GrColor color,
892 const SkMatrix& viewMatrix,
893 bool useCoverageAA,
894 const SkRect& circle,
895 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -0700896 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
joshualittd96a67b2015-05-05 14:09:05 -0700897 stroke));
joshualitt99c7c072015-05-01 13:43:30 -0700898 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000899}
900
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000901///////////////////////////////////////////////////////////////////////////////
902
joshualitt76e7fb62015-02-11 08:52:27 -0800903class EllipseBatch : public GrBatch {
904public:
905 struct Geometry {
906 GrColor fColor;
907 SkMatrix fViewMatrix;
908 SkScalar fXRadius;
909 SkScalar fYRadius;
910 SkScalar fInnerXRadius;
911 SkScalar fInnerYRadius;
912 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700913 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800914 };
915
916 static GrBatch* Create(const Geometry& geometry) {
917 return SkNEW_ARGS(EllipseBatch, (geometry));
918 }
919
mtklein36352bf2015-03-25 18:17:31 -0700920 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800921
mtklein36352bf2015-03-25 18:17:31 -0700922 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800923 // When this is called on a batch, there is only one geometry bundle
924 out->setKnownFourComponents(fGeoData[0].fColor);
925 }
mtklein36352bf2015-03-25 18:17:31 -0700926 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800927 out->setUnknownSingleComponent();
928 }
929
mtklein36352bf2015-03-25 18:17:31 -0700930 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800931 // Handle any color overrides
932 if (init.fColorIgnored) {
933 fGeoData[0].fColor = GrColor_ILLEGAL;
934 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
935 fGeoData[0].fColor = init.fOverrideColor;
936 }
937
938 // setup batch properties
939 fBatch.fColorIgnored = init.fColorIgnored;
940 fBatch.fColor = fGeoData[0].fColor;
941 fBatch.fStroke = fGeoData[0].fStroke;
942 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
943 fBatch.fCoverageIgnored = init.fCoverageIgnored;
944 }
945
mtklein36352bf2015-03-25 18:17:31 -0700946 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800947 SkMatrix invert;
948 if (!this->viewMatrix().invert(&invert)) {
949 return;
950 }
951
952 // Setup geometry processor
953 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
954 this->stroke(),
955 invert));
joshualitt76e7fb62015-02-11 08:52:27 -0800956
957 batchTarget->initDraw(gp, pipeline);
958
959 // TODO this is hacky, but the only way we have to initialize the GP is to use the
960 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
961 // everywhere we can remove this nastiness
962 GrPipelineInfo init;
963 init.fColorIgnored = fBatch.fColorIgnored;
964 init.fOverrideColor = GrColor_ILLEGAL;
965 init.fCoverageIgnored = fBatch.fCoverageIgnored;
966 init.fUsesLocalCoords = this->usesLocalCoords();
967 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
968
969 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700970 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800971 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800972 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700973 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
974 helper.init(batchTarget, vertexStride, instanceCount));
975 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800976 return;
977 }
978
bsalomon8415abe2015-05-04 11:41:41 -0700979 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700980 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700981
bsalomonb5238a72015-05-05 07:49:49 -0700982 SkScalar xRadius = geom.fXRadius;
983 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800984
985 // Compute the reciprocals of the radii here to save time in the shader
986 SkScalar xRadRecip = SkScalarInvert(xRadius);
987 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700988 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
989 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800990
bsalomonb5238a72015-05-05 07:49:49 -0700991 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800992
993 // The inner radius in the vertex data must be specified in normalized space.
994 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
995 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
996 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
997 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
998
999 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1000 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1001 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1002 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1003
1004 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1005 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1006 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1007 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1008
1009 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1010 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1011 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1012 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1013
bsalomonb5238a72015-05-05 07:49:49 -07001014 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001015 }
bsalomone64eb572015-05-07 11:35:55 -07001016 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001017 }
1018
1019 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1020
1021private:
1022 EllipseBatch(const Geometry& geometry) {
1023 this->initClassID<EllipseBatch>();
1024 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001025
1026 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001027 }
1028
mtklein36352bf2015-03-25 18:17:31 -07001029 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001030 EllipseBatch* that = t->cast<EllipseBatch>();
1031
1032 // TODO use vertex color to avoid breaking batches
1033 if (this->color() != that->color()) {
1034 return false;
1035 }
1036
1037 if (this->stroke() != that->stroke()) {
1038 return false;
1039 }
1040
1041 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1042 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1043 return false;
1044 }
1045
1046 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001047 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001048 return true;
1049 }
1050
1051 GrColor color() const { return fBatch.fColor; }
1052 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1053 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1054 bool stroke() const { return fBatch.fStroke; }
1055
1056 struct BatchTracker {
1057 GrColor fColor;
1058 bool fStroke;
1059 bool fUsesLocalCoords;
1060 bool fColorIgnored;
1061 bool fCoverageIgnored;
1062 };
1063
joshualitt76e7fb62015-02-11 08:52:27 -08001064 BatchTracker fBatch;
1065 SkSTArray<1, Geometry, true> fGeoData;
1066};
1067
joshualitt3e708c52015-04-30 13:49:27 -07001068static GrBatch* create_ellipse_batch(GrColor color,
1069 const SkMatrix& viewMatrix,
1070 bool useCoverageAA,
1071 const SkRect& ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001072 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001073#ifdef SK_DEBUG
1074 {
1075 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001076 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001077 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001078 }
1079#endif
1080
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001081 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001082 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001083 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001084 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1085 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001086 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1087 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1088 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1089 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001090
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001091 // do (potentially) anisotropic mapping of stroke
1092 SkVector scaledStroke;
1093 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001094 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1095 viewMatrix[SkMatrix::kMSkewY]));
1096 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1097 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001098
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001099 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001100 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1101 SkStrokeRec::kHairline_Style == style;
1102 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001103
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001104 SkScalar innerXRadius = 0;
1105 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001106 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001107 if (SkScalarNearlyZero(scaledStroke.length())) {
1108 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1109 } else {
1110 scaledStroke.scale(SK_ScalarHalf);
1111 }
1112
1113 // we only handle thick strokes for near-circular ellipses
1114 if (scaledStroke.length() > SK_ScalarHalf &&
1115 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001116 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001117 }
1118
1119 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1120 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1121 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001122 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001123 }
1124
1125 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001126 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001127 innerXRadius = xRadius - scaledStroke.fX;
1128 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001129 }
1130
1131 xRadius += scaledStroke.fX;
1132 yRadius += scaledStroke.fY;
1133 }
1134
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001135 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001136 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001137 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001138 xRadius += SK_ScalarHalf;
1139 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001140
joshualitt76e7fb62015-02-11 08:52:27 -08001141 EllipseBatch::Geometry geometry;
1142 geometry.fViewMatrix = viewMatrix;
1143 geometry.fColor = color;
1144 geometry.fXRadius = xRadius;
1145 geometry.fYRadius = yRadius;
1146 geometry.fInnerXRadius = innerXRadius;
1147 geometry.fInnerYRadius = innerYRadius;
1148 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001149 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1150 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001151
joshualitt3e708c52015-04-30 13:49:27 -07001152 return EllipseBatch::Create(geometry);
1153}
1154
1155bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1156 GrPipelineBuilder* pipelineBuilder,
1157 GrColor color,
1158 const SkMatrix& viewMatrix,
1159 bool useCoverageAA,
1160 const SkRect& ellipse,
1161 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001162 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001163 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001164 if (!batch) {
1165 return false;
1166 }
1167
joshualitt99c7c072015-05-01 13:43:30 -07001168 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001169 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001170}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001171
joshualitt76e7fb62015-02-11 08:52:27 -08001172/////////////////////////////////////////////////////////////////////////////////////////////////
1173
1174class DIEllipseBatch : public GrBatch {
1175public:
1176 struct Geometry {
1177 GrColor fColor;
1178 SkMatrix fViewMatrix;
1179 SkScalar fXRadius;
1180 SkScalar fYRadius;
1181 SkScalar fInnerXRadius;
1182 SkScalar fInnerYRadius;
1183 SkScalar fGeoDx;
1184 SkScalar fGeoDy;
1185 DIEllipseEdgeEffect::Mode fMode;
egdaniel9ef1bb12015-04-20 12:28:57 -07001186 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001187 };
1188
joshualitt99c7c072015-05-01 13:43:30 -07001189 static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1190 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
joshualitt76e7fb62015-02-11 08:52:27 -08001191 }
1192
mtklein36352bf2015-03-25 18:17:31 -07001193 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001194
mtklein36352bf2015-03-25 18:17:31 -07001195 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001196 // When this is called on a batch, there is only one geometry bundle
1197 out->setKnownFourComponents(fGeoData[0].fColor);
1198 }
mtklein36352bf2015-03-25 18:17:31 -07001199 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001200 out->setUnknownSingleComponent();
1201 }
1202
mtklein36352bf2015-03-25 18:17:31 -07001203 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001204 // Handle any color overrides
1205 if (init.fColorIgnored) {
1206 fGeoData[0].fColor = GrColor_ILLEGAL;
1207 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1208 fGeoData[0].fColor = init.fOverrideColor;
1209 }
1210
1211 // setup batch properties
1212 fBatch.fColorIgnored = init.fColorIgnored;
1213 fBatch.fColor = fGeoData[0].fColor;
1214 fBatch.fMode = fGeoData[0].fMode;
1215 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1216 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1217 }
1218
mtklein36352bf2015-03-25 18:17:31 -07001219 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001220 // Setup geometry processor
1221 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1222 this->viewMatrix(),
1223 this->mode()));
1224
joshualitt76e7fb62015-02-11 08:52:27 -08001225 batchTarget->initDraw(gp, pipeline);
1226
1227 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1228 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1229 // everywhere we can remove this nastiness
1230 GrPipelineInfo init;
1231 init.fColorIgnored = fBatch.fColorIgnored;
1232 init.fOverrideColor = GrColor_ILLEGAL;
1233 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1234 init.fUsesLocalCoords = this->usesLocalCoords();
1235 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1236
1237 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001238 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001239 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001240 QuadHelper helper;
1241 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1242 helper.init(batchTarget, vertexStride, instanceCount));
1243 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001244 return;
1245 }
1246
joshualitt76e7fb62015-02-11 08:52:27 -08001247 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001248 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001249
bsalomonb5238a72015-05-05 07:49:49 -07001250 SkScalar xRadius = geom.fXRadius;
1251 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001252
bsalomonb5238a72015-05-05 07:49:49 -07001253 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001254
1255 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001256 SkScalar offsetDx = geom.fGeoDx / xRadius;
1257 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001258
reed80ea19c2015-05-12 10:37:34 -07001259 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1260 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001261
1262 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1263 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1264 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1265
1266 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1267 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1268 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1269
1270 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1271 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1272 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1273
1274 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1275 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1276 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1277
bsalomonb5238a72015-05-05 07:49:49 -07001278 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001279 }
bsalomone64eb572015-05-07 11:35:55 -07001280 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001281 }
1282
1283 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1284
1285private:
joshualitt99c7c072015-05-01 13:43:30 -07001286 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
joshualitt76e7fb62015-02-11 08:52:27 -08001287 this->initClassID<DIEllipseBatch>();
1288 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001289
1290 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001291 }
1292
mtklein36352bf2015-03-25 18:17:31 -07001293 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001294 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1295
1296 // TODO use vertex color to avoid breaking batches
1297 if (this->color() != that->color()) {
1298 return false;
1299 }
1300
1301 if (this->mode() != that->mode()) {
1302 return false;
1303 }
1304
joshualittd96a67b2015-05-05 14:09:05 -07001305 // TODO rewrite to allow positioning on CPU
1306 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001307 return false;
1308 }
1309
1310 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001311 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001312 return true;
1313 }
1314
1315 GrColor color() const { return fBatch.fColor; }
1316 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1317 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1318 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1319
1320 struct BatchTracker {
1321 GrColor fColor;
1322 DIEllipseEdgeEffect::Mode fMode;
1323 bool fUsesLocalCoords;
1324 bool fColorIgnored;
1325 bool fCoverageIgnored;
1326 };
1327
joshualitt76e7fb62015-02-11 08:52:27 -08001328 BatchTracker fBatch;
1329 SkSTArray<1, Geometry, true> fGeoData;
1330};
1331
joshualitt3e708c52015-04-30 13:49:27 -07001332static GrBatch* create_diellipse_batch(GrColor color,
1333 const SkMatrix& viewMatrix,
1334 bool useCoverageAA,
1335 const SkRect& ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001336 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001337 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001338 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001339 SkScalar yRadius = SkScalarHalf(ellipse.height());
1340
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001341 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001342 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001343 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001344 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001345 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1346
1347 SkScalar innerXRadius = 0;
1348 SkScalar innerYRadius = 0;
1349 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1350 SkScalar strokeWidth = stroke.getWidth();
1351
1352 if (SkScalarNearlyZero(strokeWidth)) {
1353 strokeWidth = SK_ScalarHalf;
1354 } else {
1355 strokeWidth *= SK_ScalarHalf;
1356 }
1357
1358 // we only handle thick strokes for near-circular ellipses
1359 if (strokeWidth > SK_ScalarHalf &&
1360 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001361 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001362 }
1363
1364 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1365 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1366 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001367 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001368 }
1369
1370 // set inner radius (if needed)
1371 if (SkStrokeRec::kStroke_Style == style) {
1372 innerXRadius = xRadius - strokeWidth;
1373 innerYRadius = yRadius - strokeWidth;
1374 }
1375
1376 xRadius += strokeWidth;
1377 yRadius += strokeWidth;
1378 }
1379 if (DIEllipseEdgeEffect::kStroke == mode) {
1380 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1381 DIEllipseEdgeEffect::kFill;
1382 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001383
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001384 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001385 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1386 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1387 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1388 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001389 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1390 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001391
joshualitt76e7fb62015-02-11 08:52:27 -08001392 DIEllipseBatch::Geometry geometry;
1393 geometry.fViewMatrix = viewMatrix;
1394 geometry.fColor = color;
1395 geometry.fXRadius = xRadius;
1396 geometry.fYRadius = yRadius;
1397 geometry.fInnerXRadius = innerXRadius;
1398 geometry.fInnerYRadius = innerYRadius;
1399 geometry.fGeoDx = geoDx;
1400 geometry.fGeoDy = geoDy;
1401 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001402 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1403 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001404
joshualittd96a67b2015-05-05 14:09:05 -07001405 SkRect devBounds = geometry.fBounds;
1406 viewMatrix.mapRect(&devBounds);
1407 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001408}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001409
joshualitt3e708c52015-04-30 13:49:27 -07001410bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1411 GrPipelineBuilder* pipelineBuilder,
1412 GrColor color,
1413 const SkMatrix& viewMatrix,
1414 bool useCoverageAA,
1415 const SkRect& ellipse,
1416 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001417 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001418 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001419 if (!batch) {
1420 return false;
1421 }
joshualitt99c7c072015-05-01 13:43:30 -07001422 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001423 return true;
1424}
1425
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001426///////////////////////////////////////////////////////////////////////////////
1427
1428static const uint16_t gRRectIndices[] = {
1429 // corners
1430 0, 1, 5, 0, 5, 4,
1431 2, 3, 7, 2, 7, 6,
1432 8, 9, 13, 8, 13, 12,
1433 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001434
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001435 // edges
1436 1, 2, 6, 1, 6, 5,
1437 4, 5, 9, 4, 9, 8,
1438 6, 7, 11, 6, 11, 10,
1439 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001440
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001441 // center
1442 // we place this at the end so that we can ignore these indices when rendering stroke-only
1443 5, 6, 10, 5, 10, 9
1444};
1445
joshualitt5ead6da2014-10-22 16:00:29 -07001446static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1447static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1448static const int kVertsPerRRect = 16;
1449static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001450
bsalomoned0bcad2015-05-04 10:36:42 -07001451GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1452GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1453static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1454 GrResourceProvider* resourceProvider) {
1455 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1456 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1457 if (strokeOnly) {
1458 return resourceProvider->refOrCreateInstancedIndexBuffer(
1459 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1460 gStrokeRRectOnlyIndexBufferKey);
1461 } else {
1462 return resourceProvider->refOrCreateInstancedIndexBuffer(
1463 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1464 gRRectOnlyIndexBufferKey);
1465
1466 }
1467}
1468
joshualitt9853cce2014-11-17 14:22:48 -08001469bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001470 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001471 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001472 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001473 bool useAA,
1474 const SkRRect& origOuter,
1475 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001476 bool applyAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08001477 !pipelineBuilder->getRenderTarget()->isMultisampled();
bsalomon6be6f7c2015-02-26 13:05:21 -08001478 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001479 if (!origInner.isEmpty()) {
1480 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001481 if (!viewMatrix.isIdentity()) {
1482 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001483 return false;
1484 }
1485 }
joshualittb0a8a372014-09-23 09:50:21 -07001486 GrPrimitiveEdgeType edgeType = applyAA ?
1487 kInverseFillAA_GrProcessorEdgeType :
1488 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001489 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001490 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1491 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001492 return false;
1493 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001494 arfp.set(pipelineBuilder);
egdaniel8dd688b2015-01-22 10:16:09 -08001495 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001496 }
1497
1498 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001499 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001500 return true;
1501 }
1502
1503 SkASSERT(!origOuter.isEmpty());
1504 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001505 if (!viewMatrix.isIdentity()) {
1506 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001507 return false;
1508 }
1509 }
joshualittb0a8a372014-09-23 09:50:21 -07001510 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001511 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001512 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001513 if (NULL == effect) {
1514 return false;
1515 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001516 if (!arfp.isSet()) {
1517 arfp.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001518 }
joshualittd27f73e2014-12-29 07:43:36 -08001519
1520 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001521 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001522 return false;
1523 }
joshualittd27f73e2014-12-29 07:43:36 -08001524
egdaniel8dd688b2015-01-22 10:16:09 -08001525 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001526 SkRect bounds = outer->getBounds();
1527 if (applyAA) {
1528 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1529 }
egdaniel8dd688b2015-01-22 10:16:09 -08001530 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001531 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001532}
1533
joshualitt76e7fb62015-02-11 08:52:27 -08001534///////////////////////////////////////////////////////////////////////////////////////////////////
1535
1536class RRectCircleRendererBatch : public GrBatch {
1537public:
1538 struct Geometry {
1539 GrColor fColor;
1540 SkMatrix fViewMatrix;
1541 SkScalar fInnerRadius;
1542 SkScalar fOuterRadius;
1543 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001544 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001545 };
1546
bsalomoned0bcad2015-05-04 10:36:42 -07001547 static GrBatch* Create(const Geometry& geometry) {
1548 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001549 }
1550
mtklein36352bf2015-03-25 18:17:31 -07001551 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001552
mtklein36352bf2015-03-25 18:17:31 -07001553 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001554 // When this is called on a batch, there is only one geometry bundle
1555 out->setKnownFourComponents(fGeoData[0].fColor);
1556 }
mtklein36352bf2015-03-25 18:17:31 -07001557 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001558 out->setUnknownSingleComponent();
1559 }
1560
mtklein36352bf2015-03-25 18:17:31 -07001561 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001562 // Handle any color overrides
1563 if (init.fColorIgnored) {
1564 fGeoData[0].fColor = GrColor_ILLEGAL;
1565 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1566 fGeoData[0].fColor = init.fOverrideColor;
1567 }
1568
1569 // setup batch properties
1570 fBatch.fColorIgnored = init.fColorIgnored;
1571 fBatch.fColor = fGeoData[0].fColor;
1572 fBatch.fStroke = fGeoData[0].fStroke;
1573 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1574 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1575 }
1576
mtklein36352bf2015-03-25 18:17:31 -07001577 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001578 // reset to device coordinates
1579 SkMatrix invert;
1580 if (!this->viewMatrix().invert(&invert)) {
1581 SkDebugf("Failed to invert\n");
1582 return;
1583 }
1584
1585 // Setup geometry processor
1586 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1587 this->stroke(),
1588 invert));
1589
1590 batchTarget->initDraw(gp, pipeline);
1591
1592 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1593 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1594 // everywhere we can remove this nastiness
1595 GrPipelineInfo init;
1596 init.fColorIgnored = fBatch.fColorIgnored;
1597 init.fOverrideColor = GrColor_ILLEGAL;
1598 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1599 init.fUsesLocalCoords = this->usesLocalCoords();
1600 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1601
1602 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001603 size_t vertexStride = gp->getVertexStride();
1604 SkASSERT(vertexStride == sizeof(CircleVertex));
1605
bsalomonb5238a72015-05-05 07:49:49 -07001606 // drop out the middle quad if we're stroked
1607 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001608 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1609 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001610
bsalomonb5238a72015-05-05 07:49:49 -07001611 InstancedHelper helper;
1612 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget,
1613 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1614 indicesPerInstance, instanceCount));
1615 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001616 SkDebugf("Could not allocate vertices\n");
1617 return;
1618 }
1619
joshualitt76e7fb62015-02-11 08:52:27 -08001620 for (int i = 0; i < instanceCount; i++) {
1621 Geometry& args = fGeoData[i];
1622
1623 SkScalar outerRadius = args.fOuterRadius;
1624
egdanielbc227142015-04-21 06:28:08 -07001625 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001626
1627 SkScalar yCoords[4] = {
1628 bounds.fTop,
1629 bounds.fTop + outerRadius,
1630 bounds.fBottom - outerRadius,
1631 bounds.fBottom
1632 };
1633
1634 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1635 // The inner radius in the vertex data must be specified in normalized space.
1636 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1637 for (int i = 0; i < 4; ++i) {
1638 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1639 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1640 verts->fOuterRadius = outerRadius;
1641 verts->fInnerRadius = innerRadius;
1642 verts++;
1643
1644 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1645 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1646 verts->fOuterRadius = outerRadius;
1647 verts->fInnerRadius = innerRadius;
1648 verts++;
1649
1650 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1651 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1652 verts->fOuterRadius = outerRadius;
1653 verts->fInnerRadius = innerRadius;
1654 verts++;
1655
1656 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1657 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1658 verts->fOuterRadius = outerRadius;
1659 verts->fInnerRadius = innerRadius;
1660 verts++;
1661 }
1662 }
1663
bsalomone64eb572015-05-07 11:35:55 -07001664 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001665 }
1666
1667 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1668
1669private:
bsalomoned0bcad2015-05-04 10:36:42 -07001670 RRectCircleRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001671 this->initClassID<RRectCircleRendererBatch>();
1672 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001673
1674 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001675 }
1676
mtklein36352bf2015-03-25 18:17:31 -07001677 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001678 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1679
1680 // TODO use vertex color to avoid breaking batches
1681 if (this->color() != that->color()) {
1682 return false;
1683 }
1684
1685 if (this->stroke() != that->stroke()) {
1686 return false;
1687 }
1688
1689 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1690 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1691 return false;
1692 }
1693
1694 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001695 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001696 return true;
1697 }
1698
1699 GrColor color() const { return fBatch.fColor; }
1700 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1701 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1702 bool stroke() const { return fBatch.fStroke; }
1703
1704 struct BatchTracker {
1705 GrColor fColor;
1706 bool fStroke;
1707 bool fUsesLocalCoords;
1708 bool fColorIgnored;
1709 bool fCoverageIgnored;
1710 };
1711
joshualitt76e7fb62015-02-11 08:52:27 -08001712 BatchTracker fBatch;
1713 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001714};
1715
1716class RRectEllipseRendererBatch : public GrBatch {
1717public:
1718 struct Geometry {
1719 GrColor fColor;
1720 SkMatrix fViewMatrix;
1721 SkScalar fXRadius;
1722 SkScalar fYRadius;
1723 SkScalar fInnerXRadius;
1724 SkScalar fInnerYRadius;
1725 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001726 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001727 };
1728
bsalomoned0bcad2015-05-04 10:36:42 -07001729 static GrBatch* Create(const Geometry& geometry) {
1730 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001731 }
1732
mtklein36352bf2015-03-25 18:17:31 -07001733 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001734
mtklein36352bf2015-03-25 18:17:31 -07001735 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001736 // When this is called on a batch, there is only one geometry bundle
1737 out->setKnownFourComponents(fGeoData[0].fColor);
1738 }
mtklein36352bf2015-03-25 18:17:31 -07001739 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001740 out->setUnknownSingleComponent();
1741 }
1742
mtklein36352bf2015-03-25 18:17:31 -07001743 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001744 // Handle any color overrides
1745 if (init.fColorIgnored) {
1746 fGeoData[0].fColor = GrColor_ILLEGAL;
1747 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1748 fGeoData[0].fColor = init.fOverrideColor;
1749 }
1750
1751 // setup batch properties
1752 fBatch.fColorIgnored = init.fColorIgnored;
1753 fBatch.fColor = fGeoData[0].fColor;
1754 fBatch.fStroke = fGeoData[0].fStroke;
1755 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1756 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1757 }
1758
mtklein36352bf2015-03-25 18:17:31 -07001759 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001760 // reset to device coordinates
1761 SkMatrix invert;
1762 if (!this->viewMatrix().invert(&invert)) {
1763 SkDebugf("Failed to invert\n");
1764 return;
1765 }
1766
1767 // Setup geometry processor
1768 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1769 this->stroke(),
1770 invert));
1771
1772 batchTarget->initDraw(gp, pipeline);
1773
1774 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1775 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1776 // everywhere we can remove this nastiness
1777 GrPipelineInfo init;
1778 init.fColorIgnored = fBatch.fColorIgnored;
1779 init.fOverrideColor = GrColor_ILLEGAL;
1780 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1781 init.fUsesLocalCoords = this->usesLocalCoords();
1782 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1783
1784 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001785 size_t vertexStride = gp->getVertexStride();
1786 SkASSERT(vertexStride == sizeof(EllipseVertex));
1787
bsalomonb5238a72015-05-05 07:49:49 -07001788 // drop out the middle quad if we're stroked
1789 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001790 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1791 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001792
bsalomonb5238a72015-05-05 07:49:49 -07001793 InstancedHelper helper;
1794 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1795 helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1796 kVertsPerRRect, indicesPerInstance, instanceCount));
1797 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001798 SkDebugf("Could not allocate vertices\n");
1799 return;
1800 }
1801
joshualitt76e7fb62015-02-11 08:52:27 -08001802 for (int i = 0; i < instanceCount; i++) {
1803 Geometry& args = fGeoData[i];
1804
1805 // Compute the reciprocals of the radii here to save time in the shader
1806 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1807 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1808 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1809 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1810
1811 // Extend the radii out half a pixel to antialias.
1812 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1813 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1814
egdanielbc227142015-04-21 06:28:08 -07001815 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001816
1817 SkScalar yCoords[4] = {
1818 bounds.fTop,
1819 bounds.fTop + yOuterRadius,
1820 bounds.fBottom - yOuterRadius,
1821 bounds.fBottom
1822 };
1823 SkScalar yOuterOffsets[4] = {
1824 yOuterRadius,
1825 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1826 SK_ScalarNearlyZero,
1827 yOuterRadius
1828 };
1829
1830 for (int i = 0; i < 4; ++i) {
1831 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1832 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1833 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1834 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1835 verts++;
1836
1837 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1838 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1839 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1840 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1841 verts++;
1842
1843 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1844 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1845 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1846 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1847 verts++;
1848
1849 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1850 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1851 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1852 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1853 verts++;
1854 }
1855 }
bsalomone64eb572015-05-07 11:35:55 -07001856 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001857 }
1858
1859 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1860
1861private:
bsalomoned0bcad2015-05-04 10:36:42 -07001862 RRectEllipseRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001863 this->initClassID<RRectEllipseRendererBatch>();
1864 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001865
1866 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001867 }
1868
mtklein36352bf2015-03-25 18:17:31 -07001869 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001870 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1871
1872 // TODO use vertex color to avoid breaking batches
1873 if (this->color() != that->color()) {
1874 return false;
1875 }
1876
1877 if (this->stroke() != that->stroke()) {
1878 return false;
1879 }
1880
1881 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1882 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1883 return false;
1884 }
1885
1886 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001887 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001888 return true;
1889 }
1890
1891 GrColor color() const { return fBatch.fColor; }
1892 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1893 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1894 bool stroke() const { return fBatch.fStroke; }
1895
1896 struct BatchTracker {
1897 GrColor fColor;
1898 bool fStroke;
1899 bool fUsesLocalCoords;
1900 bool fColorIgnored;
1901 bool fCoverageIgnored;
1902 };
1903
joshualitt76e7fb62015-02-11 08:52:27 -08001904 BatchTracker fBatch;
1905 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001906};
1907
joshualitt3e708c52015-04-30 13:49:27 -07001908static GrBatch* create_rrect_batch(GrColor color,
1909 const SkMatrix& viewMatrix,
1910 const SkRRect& rrect,
joshualittd96a67b2015-05-05 14:09:05 -07001911 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001912 SkASSERT(viewMatrix.rectStaysRect());
1913 SkASSERT(rrect.isSimple());
1914 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001915
joshualitt3e708c52015-04-30 13:49:27 -07001916 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001917 // do any matrix crunching before we reset the draw state for device coords
1918 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001919 SkRect bounds;
1920 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001921
1922 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001923 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1924 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1925 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1926 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001927
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001928 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001929
1930 // do (potentially) anisotropic mapping of stroke
1931 SkVector scaledStroke;
1932 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001933
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001934 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1935 SkStrokeRec::kHairline_Style == style;
1936 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1937
1938 if (hasStroke) {
1939 if (SkStrokeRec::kHairline_Style == style) {
1940 scaledStroke.set(1, 1);
1941 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001942 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1943 viewMatrix[SkMatrix::kMSkewY]));
1944 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1945 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001946 }
1947
1948 // if half of strokewidth is greater than radius, we don't handle that right now
1949 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001950 return NULL;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001951 }
1952 }
1953
1954 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1955 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1956 // patch will have fractional coverage. This only matters when the interior is actually filled.
1957 // We could consider falling back to rect rendering here, since a tiny radius is
1958 // indistinguishable from a square corner.
1959 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001960 return NULL;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001961 }
1962
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001963 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001964 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001965 SkScalar innerRadius = 0.0f;
1966 SkScalar outerRadius = xRadius;
1967 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001968 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001969 if (SkScalarNearlyZero(scaledStroke.fX)) {
1970 halfWidth = SK_ScalarHalf;
1971 } else {
1972 halfWidth = SkScalarHalf(scaledStroke.fX);
1973 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001974
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001975 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001976 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001977 }
1978 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001979 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001980 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001981
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001982 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001983
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001984 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001985 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1986 // Second, the outer radius is used to compute the verts of the bounding box that is
1987 // rendered and the outset ensures the box will cover all partially covered by the rrect
1988 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001989 outerRadius += SK_ScalarHalf;
1990 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001991
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001992 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001993 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001994
joshualitt76e7fb62015-02-11 08:52:27 -08001995 RRectCircleRendererBatch::Geometry geometry;
1996 geometry.fViewMatrix = viewMatrix;
1997 geometry.fColor = color;
1998 geometry.fInnerRadius = innerRadius;
1999 geometry.fOuterRadius = outerRadius;
2000 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07002001 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002002
bsalomoned0bcad2015-05-04 10:36:42 -07002003 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002004 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002005 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002006 SkScalar innerXRadius = 0.0f;
2007 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002008 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002009 if (SkScalarNearlyZero(scaledStroke.length())) {
2010 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2011 } else {
2012 scaledStroke.scale(SK_ScalarHalf);
2013 }
2014
2015 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00002016 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002017 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07002018 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002019 }
2020
2021 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2022 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2023 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07002024 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002025 }
2026
2027 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002028 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002029 innerXRadius = xRadius - scaledStroke.fX;
2030 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002031 }
2032
2033 xRadius += scaledStroke.fX;
2034 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07002035 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002036 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002037
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002038 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002039
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002040 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07002041 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002042
joshualitt76e7fb62015-02-11 08:52:27 -08002043 RRectEllipseRendererBatch::Geometry geometry;
2044 geometry.fViewMatrix = viewMatrix;
2045 geometry.fColor = color;
2046 geometry.fXRadius = xRadius;
2047 geometry.fYRadius = yRadius;
2048 geometry.fInnerXRadius = innerXRadius;
2049 geometry.fInnerYRadius = innerYRadius;
2050 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07002051 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002052
bsalomoned0bcad2015-05-04 10:36:42 -07002053 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002054 }
joshualitt3e708c52015-04-30 13:49:27 -07002055}
2056
2057bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2058 GrPipelineBuilder* pipelineBuilder,
2059 GrColor color,
2060 const SkMatrix& viewMatrix,
2061 bool useAA,
2062 const SkRRect& rrect,
2063 const SkStrokeRec& stroke) {
2064 if (rrect.isOval()) {
2065 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2066 stroke);
2067 }
2068
2069 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
2070
2071 // only anti-aliased rrects for now
2072 if (!useCoverageAA) {
2073 return false;
2074 }
2075
2076 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2077 return false;
2078 }
2079
joshualittd96a67b2015-05-05 14:09:05 -07002080 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07002081 if (!batch) {
2082 return false;
2083 }
2084
joshualitt99c7c072015-05-01 13:43:30 -07002085 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002086 return true;
2087}
joshualitt3e708c52015-04-30 13:49:27 -07002088
2089///////////////////////////////////////////////////////////////////////////////////////////////////
2090
2091#ifdef GR_TEST_UTILS
2092
joshualitt3e708c52015-04-30 13:49:27 -07002093BATCH_TEST_DEFINE(CircleBatch) {
2094 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2095 GrColor color = GrRandomColor(random);
2096 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002097 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002098 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2099 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002100}
2101
2102BATCH_TEST_DEFINE(EllipseBatch) {
2103 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2104 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002105 SkRect ellipse = GrTest::TestSquare(random);
2106 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002107 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002108}
2109
2110BATCH_TEST_DEFINE(DIEllipseBatch) {
2111 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2112 GrColor color = GrRandomColor(random);
2113 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002114 SkRect ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002115 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002116 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002117}
2118
2119BATCH_TEST_DEFINE(RRectBatch) {
2120 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2121 GrColor color = GrRandomColor(random);
2122 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002123 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002124}
2125
2126#endif