blob: 38efefa8570071157bbcb72a763ff3c7f5e9fa01 [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"
joshualitt76e7fb62015-02-11 08:52:27 -080013#include "GrBufferAllocPool.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000014#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080015#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080016#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080017#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080018#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070019#include "GrResourceProvider.h"
bsalomon72e3ae42015-04-28 08:08:46 -070020#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000021#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000022#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "SkTLazy.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000024#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080025#include "gl/GrGLProcessor.h"
26#include "gl/GrGLSL.h"
27#include "gl/GrGLGeometryProcessor.h"
28#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000029
joshualitt76e7fb62015-02-11 08:52:27 -080030// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
31
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070033// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000034struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fPos;
36 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000037 SkScalar fOuterRadius;
38 SkScalar fInnerRadius;
39};
40
41struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fPos;
43 SkPoint fOffset;
44 SkPoint fOuterRadii;
45 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000046};
47
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000048struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000049 SkPoint fPos;
50 SkPoint fOuterOffset;
51 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000052};
53
commit-bot@chromium.org81312832013-03-22 18:34:09 +000054inline bool circle_stays_circle(const SkMatrix& m) {
55 return m.isSimilarity();
56}
57
58}
59
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060///////////////////////////////////////////////////////////////////////////////
61
62/**
bsalomonce1c8862014-12-15 07:11:22 -080063 * The output of this effect is a modulation of the input color and coverage for a circle. It
64 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
65 * with origin at the circle center. Two vertex attributes are used:
66 * vec2f : position in device space of the bounding geometry vertices
67 * vec4f : (p.xy, outerRad, innerRad)
68 * p is the position in the normalized space.
69 * outerRad is the outerRadius in device space.
70 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000071 */
72
joshualitt249af152014-09-15 11:41:13 -070073class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074public:
joshualittd27f73e2014-12-29 07:43:36 -080075 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
76 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077 }
78
joshualitt71c92602015-01-14 08:12:47 -080079 const Attribute* inPosition() const { return fInPosition; }
80 const Attribute* inCircleEdge() const { return fInCircleEdge; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000081 virtual ~CircleEdgeEffect() {}
82
mtklein36352bf2015-03-25 18:17:31 -070083 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000084
85 inline bool isStroked() const { return fStroke; }
86
joshualittb0a8a372014-09-23 09:50:21 -070087 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000088 public:
joshualitteb2a6762014-12-04 11:35:33 -080089 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080090 const GrBatchTracker&)
91 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000092
mtklein36352bf2015-03-25 18:17:31 -070093 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080094 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080095 GrGLGPBuilder* pb = args.fPB;
96 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080097 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
98
joshualittabb52a12015-01-13 15:02:10 -080099 // emit attributes
100 vsBuilder->emitAttributes(ce);
101
joshualitt74077b92014-10-24 11:26:03 -0700102 GrGLVertToFrag v(kVec4f_GrSLType);
103 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800104 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105
joshualitt9b989322014-12-15 14:16:27 -0800106 // Setup pass through color
107 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
108 &fColorUniform);
109
joshualittabb52a12015-01-13 15:02:10 -0800110 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800111 this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800112
113 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800114 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700115 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800116
egdaniel29bee0f2015-04-29 11:54:42 -0700117 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700118 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800119 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800120 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800121 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
122 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700123 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000125
joshualitt2dd1ae02014-12-03 06:24:10 -0800126 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000127 }
128
robertphillips46d36f02015-01-18 08:14:14 -0800129 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800130 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700131 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700132 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800133 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800134 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800135 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800136 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
137 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800138 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000139 }
140
joshualitt9b989322014-12-15 14:16:27 -0800141 virtual void setData(const GrGLProgramDataManager& pdman,
142 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700143 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800144 this->setUniformViewMatrix(pdman, gp.viewMatrix());
145
joshualitt9b989322014-12-15 14:16:27 -0800146 const BatchTracker& local = bt.cast<BatchTracker>();
147 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
148 GrGLfloat c[4];
149 GrColorToRGBAFloat(local.fColor, c);
150 pdman.set4fv(fColorUniform, 1, c);
151 fColor = local.fColor;
152 }
153 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000154
155 private:
joshualitt9b989322014-12-15 14:16:27 -0800156 GrColor fColor;
157 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700158 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000159 };
160
joshualitteb2a6762014-12-04 11:35:33 -0800161 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700162 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700163 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800164 GLProcessor::GenKey(*this, bt, caps, b);
165 }
166
joshualittabb52a12015-01-13 15:02:10 -0800167 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700168 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800169 return SkNEW_ARGS(GLProcessor, (*this, bt));
170 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171
mtklein36352bf2015-03-25 18:17:31 -0700172 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800173 BatchTracker* local = bt->cast<BatchTracker>();
174 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800175 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800176 }
177
joshualitt50cb76b2015-04-28 09:17:05 -0700178 bool onCanMakeEqual(const GrBatchTracker& m,
179 const GrGeometryProcessor& that,
180 const GrBatchTracker& t) const override {
181 const BatchTracker& mine = m.cast<BatchTracker>();
182 const BatchTracker& theirs = t.cast<BatchTracker>();
183 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
184 that, theirs.fUsesLocalCoords) &&
185 CanCombineOutput(mine.fInputColorType, mine.fColor,
186 theirs.fInputColorType, theirs.fColor);
187 }
188
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189private:
joshualittd27f73e2014-12-29 07:43:36 -0800190 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800191 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800192 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800193 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
194 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800195 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196 fStroke = stroke;
197 }
198
joshualitt50cb76b2015-04-28 09:17:05 -0700199 bool onIsEqual(const GrGeometryProcessor& other) const override {
200 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
201 return cee.fStroke == fStroke;
202 }
203
204 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
205 out->setUnknownSingleComponent();
206 }
207
joshualitt9b989322014-12-15 14:16:27 -0800208 struct BatchTracker {
209 GrGPInput fInputColorType;
210 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800211 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800212 };
213
joshualitt71c92602015-01-14 08:12:47 -0800214 const Attribute* fInPosition;
215 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216 bool fStroke;
217
joshualittb0a8a372014-09-23 09:50:21 -0700218 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219
joshualitt249af152014-09-15 11:41:13 -0700220 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221};
222
joshualittb0a8a372014-09-23 09:50:21 -0700223GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224
joshualittb0a8a372014-09-23 09:50:21 -0700225GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
226 GrContext* context,
227 const GrDrawTargetCaps&,
228 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800229 return CircleEdgeEffect::Create(GrRandomColor(random),
230 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700231 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236/**
237 * 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 +0000238 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
239 * in both x and y directions.
240 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000241 * 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 +0000242 */
243
joshualitt249af152014-09-15 11:41:13 -0700244class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245public:
joshualittd27f73e2014-12-29 07:43:36 -0800246 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
247 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 }
249
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 virtual ~EllipseEdgeEffect() {}
251
mtklein36352bf2015-03-25 18:17:31 -0700252 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800253
joshualitt71c92602015-01-14 08:12:47 -0800254 const Attribute* inPosition() const { return fInPosition; }
255 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
256 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700257
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000258 inline bool isStroked() const { return fStroke; }
259
joshualittb0a8a372014-09-23 09:50:21 -0700260 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261 public:
joshualitteb2a6762014-12-04 11:35:33 -0800262 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800263 const GrBatchTracker&)
264 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
mtklein36352bf2015-03-25 18:17:31 -0700266 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800267 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800268 GrGLGPBuilder* pb = args.fPB;
269 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800270 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271
joshualittabb52a12015-01-13 15:02:10 -0800272 // emit attributes
273 vsBuilder->emitAttributes(ee);
274
joshualitt74077b92014-10-24 11:26:03 -0700275 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
276 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700277 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800278 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000279
joshualitt74077b92014-10-24 11:26:03 -0700280 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
281 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
282 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800283 ee.inEllipseRadii()->fName);
284
joshualitt9b989322014-12-15 14:16:27 -0800285 // Setup pass through color
286 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
287 &fColorUniform);
288
joshualittabb52a12015-01-13 15:02:10 -0800289 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800290 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800291
292 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800293 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800294 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800295
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000296 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700297 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700298 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
299 ellipseRadii.fsIn());
300 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
301 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
302 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
303
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000304 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700305 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
306 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
307 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000308
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000309 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800310 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700311 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
312 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
313 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
314 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
315 ellipseRadii.fsIn());
316 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
317 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000318 }
319
joshualitt2dd1ae02014-12-03 06:24:10 -0800320 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000321 }
322
robertphillips46d36f02015-01-18 08:14:14 -0800323 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800324 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700325 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700326 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800327 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800328 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800329 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800330 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
331 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800332 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000333 }
334
joshualitt9b989322014-12-15 14:16:27 -0800335 virtual void setData(const GrGLProgramDataManager& pdman,
336 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700337 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800338 this->setUniformViewMatrix(pdman, gp.viewMatrix());
339
joshualitt9b989322014-12-15 14:16:27 -0800340 const BatchTracker& local = bt.cast<BatchTracker>();
341 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
342 GrGLfloat c[4];
343 GrColorToRGBAFloat(local.fColor, c);
344 pdman.set4fv(fColorUniform, 1, c);
345 fColor = local.fColor;
346 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000347 }
348
349 private:
joshualitt9b989322014-12-15 14:16:27 -0800350 GrColor fColor;
351 UniformHandle fColorUniform;
352
joshualitt249af152014-09-15 11:41:13 -0700353 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000354 };
355
joshualitteb2a6762014-12-04 11:35:33 -0800356 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700357 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700358 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800359 GLProcessor::GenKey(*this, bt, caps, b);
360 }
361
joshualittabb52a12015-01-13 15:02:10 -0800362 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700363 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800364 return SkNEW_ARGS(GLProcessor, (*this, bt));
365 }
366
mtklein36352bf2015-03-25 18:17:31 -0700367 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800368 BatchTracker* local = bt->cast<BatchTracker>();
369 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800370 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800371 }
372
joshualitt50cb76b2015-04-28 09:17:05 -0700373 bool onCanMakeEqual(const GrBatchTracker& m,
374 const GrGeometryProcessor& that,
375 const GrBatchTracker& t) const override {
376 const BatchTracker& mine = m.cast<BatchTracker>();
377 const BatchTracker& theirs = t.cast<BatchTracker>();
378 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
379 that, theirs.fUsesLocalCoords) &&
380 CanCombineOutput(mine.fInputColorType, mine.fColor,
381 theirs.fInputColorType, theirs.fColor);
382 }
383
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000384private:
joshualittd27f73e2014-12-29 07:43:36 -0800385 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800386 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800387 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800388 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
389 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800390 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800391 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800392 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000393 fStroke = stroke;
394 }
395
joshualitt50cb76b2015-04-28 09:17:05 -0700396 bool onIsEqual(const GrGeometryProcessor& other) const override {
397 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
398 return eee.fStroke == fStroke;
399 }
400
401 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
402 out->setUnknownSingleComponent();
403 }
404
joshualitt9b989322014-12-15 14:16:27 -0800405 struct BatchTracker {
406 GrGPInput fInputColorType;
407 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800408 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800409 };
410
joshualitt71c92602015-01-14 08:12:47 -0800411 const Attribute* fInPosition;
412 const Attribute* fInEllipseOffset;
413 const Attribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000414 bool fStroke;
415
joshualittb0a8a372014-09-23 09:50:21 -0700416 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000417
joshualitt249af152014-09-15 11:41:13 -0700418 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000419};
420
joshualittb0a8a372014-09-23 09:50:21 -0700421GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000422
joshualittb0a8a372014-09-23 09:50:21 -0700423GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
424 GrContext* context,
425 const GrDrawTargetCaps&,
426 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800427 return EllipseEdgeEffect::Create(GrRandomColor(random),
428 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700429 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000430}
431
432///////////////////////////////////////////////////////////////////////////////
433
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000434/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000435 * 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 +0000436 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
437 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
438 * using differentials.
439 *
440 * The result is device-independent and can be used with any affine matrix.
441 */
442
joshualitt249af152014-09-15 11:41:13 -0700443class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444public:
445 enum Mode { kStroke = 0, kHairline, kFill };
446
joshualitt8059eb92014-12-29 15:10:07 -0800447 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
448 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000449 }
450
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000451 virtual ~DIEllipseEdgeEffect() {}
452
mtklein36352bf2015-03-25 18:17:31 -0700453 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000454
joshualitt71c92602015-01-14 08:12:47 -0800455 const Attribute* inPosition() const { return fInPosition; }
456 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
457 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700458
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000459 inline Mode getMode() const { return fMode; }
460
joshualittb0a8a372014-09-23 09:50:21 -0700461 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462 public:
joshualitteb2a6762014-12-04 11:35:33 -0800463 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800464 const GrBatchTracker&)
465 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000466
mtklein36352bf2015-03-25 18:17:31 -0700467 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800468 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800469 GrGLGPBuilder* pb = args.fPB;
470 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800471 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472
joshualittabb52a12015-01-13 15:02:10 -0800473 // emit attributes
474 vsBuilder->emitAttributes(ee);
475
joshualitt74077b92014-10-24 11:26:03 -0700476 GrGLVertToFrag offsets0(kVec2f_GrSLType);
477 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700478 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800479 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700480
481 GrGLVertToFrag offsets1(kVec2f_GrSLType);
482 args.fPB->addVarying("EllipseOffsets1", &offsets1);
483 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800484 ee.inEllipseOffsets1()->fName);
485
joshualitt9b989322014-12-15 14:16:27 -0800486 // Setup pass through color
487 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
488 &fColorUniform);
489
joshualittabb52a12015-01-13 15:02:10 -0800490 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800491 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800492
493 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800494 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800495 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800496
egdaniel29bee0f2015-04-29 11:54:42 -0700497 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700498 SkAssertResult(fsBuilder->enableFeature(
499 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700501 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
502 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
503 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
504 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
505 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
506 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
507 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000508
joshualitt74077b92014-10-24 11:26:03 -0700509 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000510 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700511 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
512 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800513 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700515 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
516 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 } else {
joshualitt74077b92014-10-24 11:26:03 -0700518 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000519 }
520
521 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800522 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700523 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
524 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
525 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
526 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
527 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
528 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
529 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
530 offsets1.fsIn());
531 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
532 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533 }
534
joshualitt2dd1ae02014-12-03 06:24:10 -0800535 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000536 }
537
robertphillips46d36f02015-01-18 08:14:14 -0800538 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800539 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700540 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700541 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800542 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800543 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800544 uint16_t key = ellipseEffect.getMode();
robertphillips46d36f02015-01-18 08:14:14 -0800545 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
546 key |= ComputePosKey(gp.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800547 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548 }
549
joshualitt9b989322014-12-15 14:16:27 -0800550 virtual void setData(const GrGLProgramDataManager& pdman,
551 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700552 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800553 this->setUniformViewMatrix(pdman, gp.viewMatrix());
554
joshualitt9b989322014-12-15 14:16:27 -0800555 const BatchTracker& local = bt.cast<BatchTracker>();
556 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
557 GrGLfloat c[4];
558 GrColorToRGBAFloat(local.fColor, c);
559 pdman.set4fv(fColorUniform, 1, c);
560 fColor = local.fColor;
561 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000562 }
563
564 private:
joshualitt9b989322014-12-15 14:16:27 -0800565 GrColor fColor;
566 UniformHandle fColorUniform;
567
joshualitt249af152014-09-15 11:41:13 -0700568 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000569 };
570
joshualitteb2a6762014-12-04 11:35:33 -0800571 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700572 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700573 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800574 GLProcessor::GenKey(*this, bt, caps, b);
575 }
576
joshualittabb52a12015-01-13 15:02:10 -0800577 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700578 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800579 return SkNEW_ARGS(GLProcessor, (*this, bt));
580 }
581
mtklein36352bf2015-03-25 18:17:31 -0700582 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800583 BatchTracker* local = bt->cast<BatchTracker>();
584 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800585 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800586 }
587
joshualitt50cb76b2015-04-28 09:17:05 -0700588 bool onCanMakeEqual(const GrBatchTracker& m,
589 const GrGeometryProcessor& that,
590 const GrBatchTracker& t) const override {
591 const BatchTracker& mine = m.cast<BatchTracker>();
592 const BatchTracker& theirs = t.cast<BatchTracker>();
593 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
594 that, theirs.fUsesLocalCoords) &&
595 CanCombineOutput(mine.fInputColorType, mine.fColor,
596 theirs.fInputColorType, theirs.fColor);
597 }
598
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000599private:
joshualitt8059eb92014-12-29 15:10:07 -0800600 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
601 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800602 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800603 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
604 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800605 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800606 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800607 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000608 fMode = mode;
609 }
610
joshualitt50cb76b2015-04-28 09:17:05 -0700611 bool onIsEqual(const GrGeometryProcessor& other) const override {
612 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
613 return eee.fMode == fMode;
614 }
615
616 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
617 out->setUnknownSingleComponent();
618 }
619
joshualitt9b989322014-12-15 14:16:27 -0800620 struct BatchTracker {
621 GrGPInput fInputColorType;
622 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800623 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800624 };
625
joshualitt71c92602015-01-14 08:12:47 -0800626 const Attribute* fInPosition;
627 const Attribute* fInEllipseOffsets0;
628 const Attribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000629 Mode fMode;
630
joshualittb0a8a372014-09-23 09:50:21 -0700631 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000632
joshualitt249af152014-09-15 11:41:13 -0700633 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000634};
635
joshualittb0a8a372014-09-23 09:50:21 -0700636GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000637
joshualittb0a8a372014-09-23 09:50:21 -0700638GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
639 GrContext* context,
640 const GrDrawTargetCaps&,
641 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800642 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700643 GrTest::TestMatrix(random),
joshualitt8059eb92014-12-29 15:10:07 -0800644 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000645}
646
647///////////////////////////////////////////////////////////////////////////////
648
joshualitt9853cce2014-11-17 14:22:48 -0800649bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800650 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800651 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800652 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800653 bool useAA,
654 const SkRect& oval,
655 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000656{
bsalomoned0bcad2015-05-04 10:36:42 -0700657 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000658
659 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000660 return false;
661 }
662
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000663 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800664 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800665 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000666 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700667 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800668 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800669 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000670 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800671 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800672 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
673 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000674 } else {
675 return false;
676 }
677
678 return true;
679}
680
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000681///////////////////////////////////////////////////////////////////////////////
682
joshualitt76e7fb62015-02-11 08:52:27 -0800683class CircleBatch : public GrBatch {
684public:
685 struct Geometry {
686 GrColor fColor;
687 SkMatrix fViewMatrix;
688 SkScalar fInnerRadius;
689 SkScalar fOuterRadius;
690 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700691 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800692 };
693
bsalomoned0bcad2015-05-04 10:36:42 -0700694 static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); }
joshualitt76e7fb62015-02-11 08:52:27 -0800695
mtklein36352bf2015-03-25 18:17:31 -0700696 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800697
mtklein36352bf2015-03-25 18:17:31 -0700698 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800699 // When this is called on a batch, there is only one geometry bundle
700 out->setKnownFourComponents(fGeoData[0].fColor);
701 }
702
mtklein36352bf2015-03-25 18:17:31 -0700703 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800704 out->setUnknownSingleComponent();
705 }
706
mtklein36352bf2015-03-25 18:17:31 -0700707 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800708 // Handle any color overrides
709 if (init.fColorIgnored) {
710 fGeoData[0].fColor = GrColor_ILLEGAL;
711 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
712 fGeoData[0].fColor = init.fOverrideColor;
713 }
714
715 // setup batch properties
716 fBatch.fColorIgnored = init.fColorIgnored;
717 fBatch.fColor = fGeoData[0].fColor;
718 fBatch.fStroke = fGeoData[0].fStroke;
719 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
720 fBatch.fCoverageIgnored = init.fCoverageIgnored;
721 }
722
mtklein36352bf2015-03-25 18:17:31 -0700723 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800724 SkMatrix invert;
725 if (!this->viewMatrix().invert(&invert)) {
726 return;
727 }
728
729 // Setup geometry processor
730 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
731 this->stroke(),
732 invert));
733
734 batchTarget->initDraw(gp, pipeline);
735
736 // TODO this is hacky, but the only way we have to initialize the GP is to use the
737 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
738 // everywhere we can remove this nastiness
739 GrPipelineInfo init;
740 init.fColorIgnored = fBatch.fColorIgnored;
741 init.fOverrideColor = GrColor_ILLEGAL;
742 init.fCoverageIgnored = fBatch.fCoverageIgnored;
743 init.fUsesLocalCoords = this->usesLocalCoords();
744 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
745
746 int instanceCount = fGeoData.count();
747 int vertexCount = kVertsPerCircle * instanceCount;
748 size_t vertexStride = gp->getVertexStride();
749 SkASSERT(vertexStride == sizeof(CircleVertex));
750
bsalomoned0bcad2015-05-04 10:36:42 -0700751 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
752 batchTarget->resourceProvider()->refQuadIndexBuffer());
joshualitt76e7fb62015-02-11 08:52:27 -0800753 const GrVertexBuffer* vertexBuffer;
754 int firstVertex;
755
756 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
757 vertexCount,
758 &vertexBuffer,
759 &firstVertex);
760
bsalomoned0bcad2015-05-04 10:36:42 -0700761 if (!vertices || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -0800762 SkDebugf("Could not allocate buffers\n");
763 return;
764 }
765
joshualitt76e7fb62015-02-11 08:52:27 -0800766 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
767
768 for (int i = 0; i < instanceCount; i++) {
769 Geometry& args = fGeoData[i];
770
771 SkScalar innerRadius = args.fInnerRadius;
772 SkScalar outerRadius = args.fOuterRadius;
773
egdanielbc227142015-04-21 06:28:08 -0700774 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800775
776 // The inner radius in the vertex data must be specified in normalized space.
777 innerRadius = innerRadius / outerRadius;
778 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
779 verts[0].fOffset = SkPoint::Make(-1, -1);
780 verts[0].fOuterRadius = outerRadius;
781 verts[0].fInnerRadius = innerRadius;
782
783 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
784 verts[1].fOffset = SkPoint::Make(-1, 1);
785 verts[1].fOuterRadius = outerRadius;
786 verts[1].fInnerRadius = innerRadius;
787
788 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
789 verts[2].fOffset = SkPoint::Make(1, 1);
790 verts[2].fOuterRadius = outerRadius;
791 verts[2].fInnerRadius = innerRadius;
792
793 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
794 verts[3].fOffset = SkPoint::Make(1, -1);
795 verts[3].fOuterRadius = outerRadius;
796 verts[3].fInnerRadius = innerRadius;
797
798 verts += kVertsPerCircle;
799 }
800
joshualitt76e7fb62015-02-11 08:52:27 -0800801 GrDrawTarget::DrawInfo drawInfo;
802 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
803 drawInfo.setStartVertex(0);
804 drawInfo.setStartIndex(0);
805 drawInfo.setVerticesPerInstance(kVertsPerCircle);
806 drawInfo.setIndicesPerInstance(kIndicesPerCircle);
807 drawInfo.adjustStartVertex(firstVertex);
808 drawInfo.setVertexBuffer(vertexBuffer);
bsalomoned0bcad2015-05-04 10:36:42 -0700809 drawInfo.setIndexBuffer(indexBuffer);
joshualitt76e7fb62015-02-11 08:52:27 -0800810
bsalomoned0bcad2015-05-04 10:36:42 -0700811 int maxInstancesPerDraw = indexBuffer->maxQuads();
joshualitt76e7fb62015-02-11 08:52:27 -0800812
813 while (instanceCount) {
814 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
815 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
816 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
817
818 batchTarget->draw(drawInfo);
819
820 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
821 instanceCount -= drawInfo.instanceCount();
822 }
823 }
824
825 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
826
827private:
828 CircleBatch(const Geometry& geometry) {
829 this->initClassID<CircleBatch>();
830 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700831
832 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800833 }
834
mtklein36352bf2015-03-25 18:17:31 -0700835 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800836 CircleBatch* that = t->cast<CircleBatch>();
837
838 // TODO use vertex color to avoid breaking batches
839 if (this->color() != that->color()) {
840 return false;
841 }
842
843 if (this->stroke() != that->stroke()) {
844 return false;
845 }
846
847 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
848 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
849 return false;
850 }
851
852 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700853 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800854 return true;
855 }
856
857 GrColor color() const { return fBatch.fColor; }
858 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
859 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
860 bool stroke() const { return fBatch.fStroke; }
861
862 struct BatchTracker {
863 GrColor fColor;
864 bool fStroke;
865 bool fUsesLocalCoords;
866 bool fColorIgnored;
867 bool fCoverageIgnored;
868 };
869
870 static const int kVertsPerCircle = 4;
871 static const int kIndicesPerCircle = 6;
872
joshualitt76e7fb62015-02-11 08:52:27 -0800873 BatchTracker fBatch;
874 SkSTArray<1, Geometry, true> fGeoData;
875};
876
joshualitt3e708c52015-04-30 13:49:27 -0700877static GrBatch* create_circle_batch(GrColor color,
878 const SkMatrix& viewMatrix,
879 bool useCoverageAA,
880 const SkRect& circle,
881 const SkStrokeRec& stroke,
882 SkRect* bounds) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000883 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800884 viewMatrix.mapPoints(&center, 1);
885 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
886 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000887
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000888 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000889 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
890 SkStrokeRec::kHairline_Style == style;
891 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000892
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000893 SkScalar innerRadius = 0.0f;
894 SkScalar outerRadius = radius;
895 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000896 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000897 if (SkScalarNearlyZero(strokeWidth)) {
898 halfWidth = SK_ScalarHalf;
899 } else {
900 halfWidth = SkScalarHalf(strokeWidth);
901 }
902
903 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000904 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000905 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000906 }
907 }
908
bsalomonce1c8862014-12-15 07:11:22 -0800909 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
910 // computation because the computed alpha is zero, rather than 50%, at the radius.
911 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
912 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000913 outerRadius += SK_ScalarHalf;
914 innerRadius -= SK_ScalarHalf;
915
joshualitt3e708c52015-04-30 13:49:27 -0700916 bounds->setLTRB(center.fX - outerRadius, center.fY - outerRadius,
917 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000918
joshualitt76e7fb62015-02-11 08:52:27 -0800919 CircleBatch::Geometry geometry;
920 geometry.fViewMatrix = viewMatrix;
921 geometry.fColor = color;
922 geometry.fInnerRadius = innerRadius;
923 geometry.fOuterRadius = outerRadius;
924 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualitt3e708c52015-04-30 13:49:27 -0700925 geometry.fDevBounds = *bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000926
joshualitt3e708c52015-04-30 13:49:27 -0700927 return CircleBatch::Create(geometry);
928}
929
930void GrOvalRenderer::drawCircle(GrDrawTarget* target,
931 GrPipelineBuilder* pipelineBuilder,
932 GrColor color,
933 const SkMatrix& viewMatrix,
934 bool useCoverageAA,
935 const SkRect& circle,
936 const SkStrokeRec& stroke) {
937 SkRect bounds;
938 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
939 stroke, &bounds));
joshualitt99c7c072015-05-01 13:43:30 -0700940 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000941}
942
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000943///////////////////////////////////////////////////////////////////////////////
944
joshualitt76e7fb62015-02-11 08:52:27 -0800945class EllipseBatch : public GrBatch {
946public:
947 struct Geometry {
948 GrColor fColor;
949 SkMatrix fViewMatrix;
950 SkScalar fXRadius;
951 SkScalar fYRadius;
952 SkScalar fInnerXRadius;
953 SkScalar fInnerYRadius;
954 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700955 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800956 };
957
958 static GrBatch* Create(const Geometry& geometry) {
959 return SkNEW_ARGS(EllipseBatch, (geometry));
960 }
961
mtklein36352bf2015-03-25 18:17:31 -0700962 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800963
mtklein36352bf2015-03-25 18:17:31 -0700964 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800965 // When this is called on a batch, there is only one geometry bundle
966 out->setKnownFourComponents(fGeoData[0].fColor);
967 }
mtklein36352bf2015-03-25 18:17:31 -0700968 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800969 out->setUnknownSingleComponent();
970 }
971
mtklein36352bf2015-03-25 18:17:31 -0700972 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800973 // Handle any color overrides
974 if (init.fColorIgnored) {
975 fGeoData[0].fColor = GrColor_ILLEGAL;
976 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
977 fGeoData[0].fColor = init.fOverrideColor;
978 }
979
980 // setup batch properties
981 fBatch.fColorIgnored = init.fColorIgnored;
982 fBatch.fColor = fGeoData[0].fColor;
983 fBatch.fStroke = fGeoData[0].fStroke;
984 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
985 fBatch.fCoverageIgnored = init.fCoverageIgnored;
986 }
987
mtklein36352bf2015-03-25 18:17:31 -0700988 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800989 SkMatrix invert;
990 if (!this->viewMatrix().invert(&invert)) {
991 return;
992 }
993
994 // Setup geometry processor
995 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
996 this->stroke(),
997 invert));
joshualitt76e7fb62015-02-11 08:52:27 -0800998
999 batchTarget->initDraw(gp, pipeline);
1000
1001 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1002 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1003 // everywhere we can remove this nastiness
1004 GrPipelineInfo init;
1005 init.fColorIgnored = fBatch.fColorIgnored;
1006 init.fOverrideColor = GrColor_ILLEGAL;
1007 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1008 init.fUsesLocalCoords = this->usesLocalCoords();
1009 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1010
1011 int instanceCount = fGeoData.count();
1012 int vertexCount = kVertsPerEllipse * instanceCount;
1013 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001014 SkASSERT(vertexStride == sizeof(EllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001015
1016 const GrVertexBuffer* vertexBuffer;
bsalomoned0bcad2015-05-04 10:36:42 -07001017 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1018 batchTarget->resourceProvider()->refQuadIndexBuffer());
joshualitt76e7fb62015-02-11 08:52:27 -08001019 int firstVertex;
1020
1021 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1022 vertexCount,
1023 &vertexBuffer,
1024 &firstVertex);
1025
bsalomoned0bcad2015-05-04 10:36:42 -07001026 if (!vertices || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001027 SkDebugf("Could not allocate buffers\n");
1028 return;
1029 }
1030
joshualitt76e7fb62015-02-11 08:52:27 -08001031 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1032
1033 for (int i = 0; i < instanceCount; i++) {
1034 Geometry& args = fGeoData[i];
1035
1036 SkScalar xRadius = args.fXRadius;
1037 SkScalar yRadius = args.fYRadius;
1038
1039 // Compute the reciprocals of the radii here to save time in the shader
1040 SkScalar xRadRecip = SkScalarInvert(xRadius);
1041 SkScalar yRadRecip = SkScalarInvert(yRadius);
1042 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1043 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1044
egdanielbc227142015-04-21 06:28:08 -07001045 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001046
1047 // The inner radius in the vertex data must be specified in normalized space.
1048 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1049 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
1050 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1051 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1052
1053 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1054 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1055 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1056 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1057
1058 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1059 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1060 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1061 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1062
1063 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1064 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1065 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1066 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1067
1068 verts += kVertsPerEllipse;
1069 }
1070
joshualitt76e7fb62015-02-11 08:52:27 -08001071 GrDrawTarget::DrawInfo drawInfo;
1072 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1073 drawInfo.setStartVertex(0);
1074 drawInfo.setStartIndex(0);
1075 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1076 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1077 drawInfo.adjustStartVertex(firstVertex);
1078 drawInfo.setVertexBuffer(vertexBuffer);
bsalomoned0bcad2015-05-04 10:36:42 -07001079 drawInfo.setIndexBuffer(indexBuffer);
joshualitt76e7fb62015-02-11 08:52:27 -08001080
bsalomoned0bcad2015-05-04 10:36:42 -07001081 int maxInstancesPerDraw = indexBuffer->maxQuads();
joshualitt76e7fb62015-02-11 08:52:27 -08001082
1083 while (instanceCount) {
1084 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1085 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1086 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1087
1088 batchTarget->draw(drawInfo);
1089
1090 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1091 instanceCount -= drawInfo.instanceCount();
1092 }
1093 }
1094
1095 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1096
1097private:
1098 EllipseBatch(const Geometry& geometry) {
1099 this->initClassID<EllipseBatch>();
1100 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001101
1102 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001103 }
1104
mtklein36352bf2015-03-25 18:17:31 -07001105 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001106 EllipseBatch* that = t->cast<EllipseBatch>();
1107
1108 // TODO use vertex color to avoid breaking batches
1109 if (this->color() != that->color()) {
1110 return false;
1111 }
1112
1113 if (this->stroke() != that->stroke()) {
1114 return false;
1115 }
1116
1117 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1118 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1119 return false;
1120 }
1121
1122 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001123 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001124 return true;
1125 }
1126
1127 GrColor color() const { return fBatch.fColor; }
1128 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1129 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1130 bool stroke() const { return fBatch.fStroke; }
1131
1132 struct BatchTracker {
1133 GrColor fColor;
1134 bool fStroke;
1135 bool fUsesLocalCoords;
1136 bool fColorIgnored;
1137 bool fCoverageIgnored;
1138 };
1139
1140 static const int kVertsPerEllipse = 4;
1141 static const int kIndicesPerEllipse = 6;
1142
joshualitt76e7fb62015-02-11 08:52:27 -08001143 BatchTracker fBatch;
1144 SkSTArray<1, Geometry, true> fGeoData;
1145};
1146
joshualitt3e708c52015-04-30 13:49:27 -07001147static GrBatch* create_ellipse_batch(GrColor color,
1148 const SkMatrix& viewMatrix,
1149 bool useCoverageAA,
1150 const SkRect& ellipse,
1151 const SkStrokeRec& stroke,
1152 SkRect* bounds) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001153#ifdef SK_DEBUG
1154 {
1155 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001156 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001157 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001158 }
1159#endif
1160
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001161 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001162 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001163 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001164 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1165 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001166 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1167 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1168 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1169 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001170
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001171 // do (potentially) anisotropic mapping of stroke
1172 SkVector scaledStroke;
1173 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001174 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1175 viewMatrix[SkMatrix::kMSkewY]));
1176 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1177 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001178
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001179 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001180 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1181 SkStrokeRec::kHairline_Style == style;
1182 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001183
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001184 SkScalar innerXRadius = 0;
1185 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001186 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001187 if (SkScalarNearlyZero(scaledStroke.length())) {
1188 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1189 } else {
1190 scaledStroke.scale(SK_ScalarHalf);
1191 }
1192
1193 // we only handle thick strokes for near-circular ellipses
1194 if (scaledStroke.length() > SK_ScalarHalf &&
1195 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001196 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001197 }
1198
1199 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1200 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1201 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001202 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001203 }
1204
1205 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001206 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001207 innerXRadius = xRadius - scaledStroke.fX;
1208 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001209 }
1210
1211 xRadius += scaledStroke.fX;
1212 yRadius += scaledStroke.fY;
1213 }
1214
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001215 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001216 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001217 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001218 xRadius += SK_ScalarHalf;
1219 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001220
joshualitt3e708c52015-04-30 13:49:27 -07001221 bounds->setLTRB(center.fX - xRadius, center.fY - yRadius,
1222 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001223
joshualitt76e7fb62015-02-11 08:52:27 -08001224 EllipseBatch::Geometry geometry;
1225 geometry.fViewMatrix = viewMatrix;
1226 geometry.fColor = color;
1227 geometry.fXRadius = xRadius;
1228 geometry.fYRadius = yRadius;
1229 geometry.fInnerXRadius = innerXRadius;
1230 geometry.fInnerYRadius = innerYRadius;
1231 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualitt3e708c52015-04-30 13:49:27 -07001232 geometry.fDevBounds = *bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001233
joshualitt3e708c52015-04-30 13:49:27 -07001234 return EllipseBatch::Create(geometry);
1235}
1236
1237bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1238 GrPipelineBuilder* pipelineBuilder,
1239 GrColor color,
1240 const SkMatrix& viewMatrix,
1241 bool useCoverageAA,
1242 const SkRect& ellipse,
1243 const SkStrokeRec& stroke) {
1244 SkRect bounds;
1245 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1246 stroke, &bounds));
1247 if (!batch) {
1248 return false;
1249 }
1250
joshualitt99c7c072015-05-01 13:43:30 -07001251 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001252 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001253}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001254
joshualitt76e7fb62015-02-11 08:52:27 -08001255/////////////////////////////////////////////////////////////////////////////////////////////////
1256
1257class DIEllipseBatch : public GrBatch {
1258public:
1259 struct Geometry {
1260 GrColor fColor;
1261 SkMatrix fViewMatrix;
1262 SkScalar fXRadius;
1263 SkScalar fYRadius;
1264 SkScalar fInnerXRadius;
1265 SkScalar fInnerYRadius;
1266 SkScalar fGeoDx;
1267 SkScalar fGeoDy;
1268 DIEllipseEdgeEffect::Mode fMode;
egdaniel9ef1bb12015-04-20 12:28:57 -07001269 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001270 };
1271
joshualitt99c7c072015-05-01 13:43:30 -07001272 static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1273 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
joshualitt76e7fb62015-02-11 08:52:27 -08001274 }
1275
mtklein36352bf2015-03-25 18:17:31 -07001276 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001277
mtklein36352bf2015-03-25 18:17:31 -07001278 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001279 // When this is called on a batch, there is only one geometry bundle
1280 out->setKnownFourComponents(fGeoData[0].fColor);
1281 }
mtklein36352bf2015-03-25 18:17:31 -07001282 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001283 out->setUnknownSingleComponent();
1284 }
1285
mtklein36352bf2015-03-25 18:17:31 -07001286 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001287 // Handle any color overrides
1288 if (init.fColorIgnored) {
1289 fGeoData[0].fColor = GrColor_ILLEGAL;
1290 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1291 fGeoData[0].fColor = init.fOverrideColor;
1292 }
1293
1294 // setup batch properties
1295 fBatch.fColorIgnored = init.fColorIgnored;
1296 fBatch.fColor = fGeoData[0].fColor;
1297 fBatch.fMode = fGeoData[0].fMode;
1298 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1299 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1300 }
1301
mtklein36352bf2015-03-25 18:17:31 -07001302 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001303 // Setup geometry processor
1304 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1305 this->viewMatrix(),
1306 this->mode()));
1307
joshualitt76e7fb62015-02-11 08:52:27 -08001308 batchTarget->initDraw(gp, pipeline);
1309
1310 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1311 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1312 // everywhere we can remove this nastiness
1313 GrPipelineInfo init;
1314 init.fColorIgnored = fBatch.fColorIgnored;
1315 init.fOverrideColor = GrColor_ILLEGAL;
1316 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1317 init.fUsesLocalCoords = this->usesLocalCoords();
1318 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1319
bsalomoned0bcad2015-05-04 10:36:42 -07001320 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1321 batchTarget->resourceProvider()->refQuadIndexBuffer());
1322
joshualitt76e7fb62015-02-11 08:52:27 -08001323 int instanceCount = fGeoData.count();
1324 int vertexCount = kVertsPerEllipse * instanceCount;
1325 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001326 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001327 const GrVertexBuffer* vertexBuffer;
1328 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001329 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1330 vertexCount,
1331 &vertexBuffer,
1332 &firstVertex);
1333
bsalomoned0bcad2015-05-04 10:36:42 -07001334 if (!vertices || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001335 SkDebugf("Could not allocate buffers\n");
1336 return;
1337 }
1338
joshualitt76e7fb62015-02-11 08:52:27 -08001339 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
1340
1341 for (int i = 0; i < instanceCount; i++) {
1342 Geometry& args = fGeoData[i];
1343
1344 SkScalar xRadius = args.fXRadius;
1345 SkScalar yRadius = args.fYRadius;
1346
egdaniel9ef1bb12015-04-20 12:28:57 -07001347 const SkRect& bounds = args.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001348
1349 // This adjusts the "radius" to include the half-pixel border
1350 SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
1351 SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
1352
1353 SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
1354 SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
1355
1356 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1357 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1358 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1359
1360 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1361 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1362 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1363
1364 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1365 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1366 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1367
1368 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1369 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1370 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1371
1372 verts += kVertsPerEllipse;
1373 }
1374
joshualitt76e7fb62015-02-11 08:52:27 -08001375 GrDrawTarget::DrawInfo drawInfo;
1376 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1377 drawInfo.setStartVertex(0);
1378 drawInfo.setStartIndex(0);
1379 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1380 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1381 drawInfo.adjustStartVertex(firstVertex);
1382 drawInfo.setVertexBuffer(vertexBuffer);
bsalomoned0bcad2015-05-04 10:36:42 -07001383 drawInfo.setIndexBuffer(indexBuffer);
joshualitt76e7fb62015-02-11 08:52:27 -08001384
bsalomoned0bcad2015-05-04 10:36:42 -07001385 int maxInstancesPerDraw = indexBuffer->maxQuads();
joshualitt76e7fb62015-02-11 08:52:27 -08001386
1387 while (instanceCount) {
1388 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1389 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1390 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1391
1392 batchTarget->draw(drawInfo);
1393
1394 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1395 instanceCount -= drawInfo.instanceCount();
1396 }
1397 }
1398
1399 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1400
1401private:
joshualitt99c7c072015-05-01 13:43:30 -07001402 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
joshualitt76e7fb62015-02-11 08:52:27 -08001403 this->initClassID<DIEllipseBatch>();
1404 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001405
1406 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001407 }
1408
mtklein36352bf2015-03-25 18:17:31 -07001409 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001410 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1411
1412 // TODO use vertex color to avoid breaking batches
1413 if (this->color() != that->color()) {
1414 return false;
1415 }
1416
1417 if (this->mode() != that->mode()) {
1418 return false;
1419 }
1420
1421 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1422 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1423 return false;
1424 }
1425
1426 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001427 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001428 return true;
1429 }
1430
1431 GrColor color() const { return fBatch.fColor; }
1432 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1433 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1434 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1435
1436 struct BatchTracker {
1437 GrColor fColor;
1438 DIEllipseEdgeEffect::Mode fMode;
1439 bool fUsesLocalCoords;
1440 bool fColorIgnored;
1441 bool fCoverageIgnored;
1442 };
1443
1444 static const int kVertsPerEllipse = 4;
1445 static const int kIndicesPerEllipse = 6;
1446
joshualitt76e7fb62015-02-11 08:52:27 -08001447 BatchTracker fBatch;
1448 SkSTArray<1, Geometry, true> fGeoData;
1449};
1450
joshualitt3e708c52015-04-30 13:49:27 -07001451static GrBatch* create_diellipse_batch(GrColor color,
1452 const SkMatrix& viewMatrix,
1453 bool useCoverageAA,
1454 const SkRect& ellipse,
1455 const SkStrokeRec& stroke,
1456 SkRect* bounds) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001457 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001458 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001459 SkScalar yRadius = SkScalarHalf(ellipse.height());
1460
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001461 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001462 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001463 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001464 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001465 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1466
1467 SkScalar innerXRadius = 0;
1468 SkScalar innerYRadius = 0;
1469 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1470 SkScalar strokeWidth = stroke.getWidth();
1471
1472 if (SkScalarNearlyZero(strokeWidth)) {
1473 strokeWidth = SK_ScalarHalf;
1474 } else {
1475 strokeWidth *= SK_ScalarHalf;
1476 }
1477
1478 // we only handle thick strokes for near-circular ellipses
1479 if (strokeWidth > SK_ScalarHalf &&
1480 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001481 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001482 }
1483
1484 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1485 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1486 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001487 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001488 }
1489
1490 // set inner radius (if needed)
1491 if (SkStrokeRec::kStroke_Style == style) {
1492 innerXRadius = xRadius - strokeWidth;
1493 innerYRadius = yRadius - strokeWidth;
1494 }
1495
1496 xRadius += strokeWidth;
1497 yRadius += strokeWidth;
1498 }
1499 if (DIEllipseEdgeEffect::kStroke == mode) {
1500 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1501 DIEllipseEdgeEffect::kFill;
1502 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001503
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001504 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001505 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1506 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1507 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1508 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001509 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
1510 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001511
joshualitt3e708c52015-04-30 13:49:27 -07001512 bounds->setLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1513 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001514
joshualitt76e7fb62015-02-11 08:52:27 -08001515 DIEllipseBatch::Geometry geometry;
1516 geometry.fViewMatrix = viewMatrix;
1517 geometry.fColor = color;
1518 geometry.fXRadius = xRadius;
1519 geometry.fYRadius = yRadius;
1520 geometry.fInnerXRadius = innerXRadius;
1521 geometry.fInnerYRadius = innerYRadius;
1522 geometry.fGeoDx = geoDx;
1523 geometry.fGeoDy = geoDy;
1524 geometry.fMode = mode;
joshualitt3e708c52015-04-30 13:49:27 -07001525 geometry.fBounds = *bounds;
egdaniel9ef1bb12015-04-20 12:28:57 -07001526
joshualitt3e708c52015-04-30 13:49:27 -07001527 viewMatrix.mapRect(bounds);
joshualitt99c7c072015-05-01 13:43:30 -07001528 return DIEllipseBatch::Create(geometry, *bounds);
joshualitt3e708c52015-04-30 13:49:27 -07001529}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001530
joshualitt3e708c52015-04-30 13:49:27 -07001531bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1532 GrPipelineBuilder* pipelineBuilder,
1533 GrColor color,
1534 const SkMatrix& viewMatrix,
1535 bool useCoverageAA,
1536 const SkRect& ellipse,
1537 const SkStrokeRec& stroke) {
1538 SkRect bounds;
1539 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1540 stroke, &bounds));
1541 if (!batch) {
1542 return false;
1543 }
joshualitt99c7c072015-05-01 13:43:30 -07001544 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001545 return true;
1546}
1547
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001548///////////////////////////////////////////////////////////////////////////////
1549
1550static const uint16_t gRRectIndices[] = {
1551 // corners
1552 0, 1, 5, 0, 5, 4,
1553 2, 3, 7, 2, 7, 6,
1554 8, 9, 13, 8, 13, 12,
1555 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001556
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001557 // edges
1558 1, 2, 6, 1, 6, 5,
1559 4, 5, 9, 4, 9, 8,
1560 6, 7, 11, 6, 11, 10,
1561 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001562
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001563 // center
1564 // we place this at the end so that we can ignore these indices when rendering stroke-only
1565 5, 6, 10, 5, 10, 9
1566};
1567
joshualitt5ead6da2014-10-22 16:00:29 -07001568static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1569static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1570static const int kVertsPerRRect = 16;
1571static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001572
bsalomoned0bcad2015-05-04 10:36:42 -07001573GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1574GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1575static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1576 GrResourceProvider* resourceProvider) {
1577 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1578 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1579 if (strokeOnly) {
1580 return resourceProvider->refOrCreateInstancedIndexBuffer(
1581 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1582 gStrokeRRectOnlyIndexBufferKey);
1583 } else {
1584 return resourceProvider->refOrCreateInstancedIndexBuffer(
1585 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1586 gRRectOnlyIndexBufferKey);
1587
1588 }
1589}
1590
joshualitt9853cce2014-11-17 14:22:48 -08001591bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001592 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001593 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001594 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001595 bool useAA,
1596 const SkRRect& origOuter,
1597 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001598 bool applyAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08001599 !pipelineBuilder->getRenderTarget()->isMultisampled();
bsalomon6be6f7c2015-02-26 13:05:21 -08001600 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001601 if (!origInner.isEmpty()) {
1602 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001603 if (!viewMatrix.isIdentity()) {
1604 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001605 return false;
1606 }
1607 }
joshualittb0a8a372014-09-23 09:50:21 -07001608 GrPrimitiveEdgeType edgeType = applyAA ?
1609 kInverseFillAA_GrProcessorEdgeType :
1610 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001611 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001612 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1613 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001614 return false;
1615 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001616 arfp.set(pipelineBuilder);
egdaniel8dd688b2015-01-22 10:16:09 -08001617 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001618 }
1619
1620 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001621 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001622 return true;
1623 }
1624
1625 SkASSERT(!origOuter.isEmpty());
1626 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001627 if (!viewMatrix.isIdentity()) {
1628 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001629 return false;
1630 }
1631 }
joshualittb0a8a372014-09-23 09:50:21 -07001632 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001633 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001634 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001635 if (NULL == effect) {
1636 return false;
1637 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001638 if (!arfp.isSet()) {
1639 arfp.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001640 }
joshualittd27f73e2014-12-29 07:43:36 -08001641
1642 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001643 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001644 return false;
1645 }
joshualittd27f73e2014-12-29 07:43:36 -08001646
egdaniel8dd688b2015-01-22 10:16:09 -08001647 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001648 SkRect bounds = outer->getBounds();
1649 if (applyAA) {
1650 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1651 }
egdaniel8dd688b2015-01-22 10:16:09 -08001652 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001653 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001654}
1655
joshualitt76e7fb62015-02-11 08:52:27 -08001656///////////////////////////////////////////////////////////////////////////////////////////////////
1657
1658class RRectCircleRendererBatch : public GrBatch {
1659public:
1660 struct Geometry {
1661 GrColor fColor;
1662 SkMatrix fViewMatrix;
1663 SkScalar fInnerRadius;
1664 SkScalar fOuterRadius;
1665 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001666 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001667 };
1668
bsalomoned0bcad2015-05-04 10:36:42 -07001669 static GrBatch* Create(const Geometry& geometry) {
1670 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001671 }
1672
mtklein36352bf2015-03-25 18:17:31 -07001673 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001674
mtklein36352bf2015-03-25 18:17:31 -07001675 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001676 // When this is called on a batch, there is only one geometry bundle
1677 out->setKnownFourComponents(fGeoData[0].fColor);
1678 }
mtklein36352bf2015-03-25 18:17:31 -07001679 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001680 out->setUnknownSingleComponent();
1681 }
1682
mtklein36352bf2015-03-25 18:17:31 -07001683 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001684 // Handle any color overrides
1685 if (init.fColorIgnored) {
1686 fGeoData[0].fColor = GrColor_ILLEGAL;
1687 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1688 fGeoData[0].fColor = init.fOverrideColor;
1689 }
1690
1691 // setup batch properties
1692 fBatch.fColorIgnored = init.fColorIgnored;
1693 fBatch.fColor = fGeoData[0].fColor;
1694 fBatch.fStroke = fGeoData[0].fStroke;
1695 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1696 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1697 }
1698
mtklein36352bf2015-03-25 18:17:31 -07001699 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001700 // reset to device coordinates
1701 SkMatrix invert;
1702 if (!this->viewMatrix().invert(&invert)) {
1703 SkDebugf("Failed to invert\n");
1704 return;
1705 }
1706
1707 // Setup geometry processor
1708 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1709 this->stroke(),
1710 invert));
1711
1712 batchTarget->initDraw(gp, pipeline);
1713
1714 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1715 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1716 // everywhere we can remove this nastiness
1717 GrPipelineInfo init;
1718 init.fColorIgnored = fBatch.fColorIgnored;
1719 init.fOverrideColor = GrColor_ILLEGAL;
1720 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1721 init.fUsesLocalCoords = this->usesLocalCoords();
1722 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1723
1724 int instanceCount = fGeoData.count();
1725 int vertexCount = kVertsPerRRect * instanceCount;
1726 size_t vertexStride = gp->getVertexStride();
1727 SkASSERT(vertexStride == sizeof(CircleVertex));
1728
1729 const GrVertexBuffer* vertexBuffer;
bsalomoned0bcad2015-05-04 10:36:42 -07001730 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1731 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001732 int firstVertex;
1733
1734 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1735 vertexCount,
1736 &vertexBuffer,
1737 &firstVertex);
1738
bsalomoned0bcad2015-05-04 10:36:42 -07001739 if (!vertices || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001740 SkDebugf("Could not allocate vertices\n");
1741 return;
1742 }
1743
joshualitt76e7fb62015-02-11 08:52:27 -08001744 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
1745
1746 for (int i = 0; i < instanceCount; i++) {
1747 Geometry& args = fGeoData[i];
1748
1749 SkScalar outerRadius = args.fOuterRadius;
1750
egdanielbc227142015-04-21 06:28:08 -07001751 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001752
1753 SkScalar yCoords[4] = {
1754 bounds.fTop,
1755 bounds.fTop + outerRadius,
1756 bounds.fBottom - outerRadius,
1757 bounds.fBottom
1758 };
1759
1760 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1761 // The inner radius in the vertex data must be specified in normalized space.
1762 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1763 for (int i = 0; i < 4; ++i) {
1764 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1765 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1766 verts->fOuterRadius = outerRadius;
1767 verts->fInnerRadius = innerRadius;
1768 verts++;
1769
1770 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1771 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1772 verts->fOuterRadius = outerRadius;
1773 verts->fInnerRadius = innerRadius;
1774 verts++;
1775
1776 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1777 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1778 verts->fOuterRadius = outerRadius;
1779 verts->fInnerRadius = innerRadius;
1780 verts++;
1781
1782 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1783 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1784 verts->fOuterRadius = outerRadius;
1785 verts->fInnerRadius = innerRadius;
1786 verts++;
1787 }
1788 }
1789
1790 // drop out the middle quad if we're stroked
1791 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1792 SK_ARRAY_COUNT(gRRectIndices);
1793
joshualitt76e7fb62015-02-11 08:52:27 -08001794 GrDrawTarget::DrawInfo drawInfo;
1795 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1796 drawInfo.setStartVertex(0);
1797 drawInfo.setStartIndex(0);
1798 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1799 drawInfo.setIndicesPerInstance(indexCnt);
1800 drawInfo.adjustStartVertex(firstVertex);
1801 drawInfo.setVertexBuffer(vertexBuffer);
bsalomoned0bcad2015-05-04 10:36:42 -07001802 drawInfo.setIndexBuffer(indexBuffer);
joshualitt76e7fb62015-02-11 08:52:27 -08001803
1804 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1805
1806 while (instanceCount) {
1807 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1808 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1809 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1810
1811 batchTarget->draw(drawInfo);
1812
1813 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1814 instanceCount -= drawInfo.instanceCount();
1815 }
1816 }
1817
1818 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1819
1820private:
bsalomoned0bcad2015-05-04 10:36:42 -07001821 RRectCircleRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001822 this->initClassID<RRectCircleRendererBatch>();
1823 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001824
1825 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001826 }
1827
mtklein36352bf2015-03-25 18:17:31 -07001828 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001829 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1830
1831 // TODO use vertex color to avoid breaking batches
1832 if (this->color() != that->color()) {
1833 return false;
1834 }
1835
1836 if (this->stroke() != that->stroke()) {
1837 return false;
1838 }
1839
1840 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1841 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1842 return false;
1843 }
1844
1845 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001846 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001847 return true;
1848 }
1849
1850 GrColor color() const { return fBatch.fColor; }
1851 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1852 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1853 bool stroke() const { return fBatch.fStroke; }
1854
1855 struct BatchTracker {
1856 GrColor fColor;
1857 bool fStroke;
1858 bool fUsesLocalCoords;
1859 bool fColorIgnored;
1860 bool fCoverageIgnored;
1861 };
1862
joshualitt76e7fb62015-02-11 08:52:27 -08001863 BatchTracker fBatch;
1864 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001865};
1866
1867class RRectEllipseRendererBatch : public GrBatch {
1868public:
1869 struct Geometry {
1870 GrColor fColor;
1871 SkMatrix fViewMatrix;
1872 SkScalar fXRadius;
1873 SkScalar fYRadius;
1874 SkScalar fInnerXRadius;
1875 SkScalar fInnerYRadius;
1876 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001877 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001878 };
1879
bsalomoned0bcad2015-05-04 10:36:42 -07001880 static GrBatch* Create(const Geometry& geometry) {
1881 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001882 }
1883
mtklein36352bf2015-03-25 18:17:31 -07001884 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001885
mtklein36352bf2015-03-25 18:17:31 -07001886 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001887 // When this is called on a batch, there is only one geometry bundle
1888 out->setKnownFourComponents(fGeoData[0].fColor);
1889 }
mtklein36352bf2015-03-25 18:17:31 -07001890 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001891 out->setUnknownSingleComponent();
1892 }
1893
mtklein36352bf2015-03-25 18:17:31 -07001894 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001895 // Handle any color overrides
1896 if (init.fColorIgnored) {
1897 fGeoData[0].fColor = GrColor_ILLEGAL;
1898 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1899 fGeoData[0].fColor = init.fOverrideColor;
1900 }
1901
1902 // setup batch properties
1903 fBatch.fColorIgnored = init.fColorIgnored;
1904 fBatch.fColor = fGeoData[0].fColor;
1905 fBatch.fStroke = fGeoData[0].fStroke;
1906 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1907 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1908 }
1909
mtklein36352bf2015-03-25 18:17:31 -07001910 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001911 // reset to device coordinates
1912 SkMatrix invert;
1913 if (!this->viewMatrix().invert(&invert)) {
1914 SkDebugf("Failed to invert\n");
1915 return;
1916 }
1917
1918 // Setup geometry processor
1919 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1920 this->stroke(),
1921 invert));
1922
1923 batchTarget->initDraw(gp, pipeline);
1924
1925 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1926 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1927 // everywhere we can remove this nastiness
1928 GrPipelineInfo init;
1929 init.fColorIgnored = fBatch.fColorIgnored;
1930 init.fOverrideColor = GrColor_ILLEGAL;
1931 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1932 init.fUsesLocalCoords = this->usesLocalCoords();
1933 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1934
1935 int instanceCount = fGeoData.count();
1936 int vertexCount = kVertsPerRRect * instanceCount;
1937 size_t vertexStride = gp->getVertexStride();
1938 SkASSERT(vertexStride == sizeof(EllipseVertex));
1939
1940 const GrVertexBuffer* vertexBuffer;
bsalomoned0bcad2015-05-04 10:36:42 -07001941 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1942 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001943 int firstVertex;
1944
1945 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1946 vertexCount,
1947 &vertexBuffer,
1948 &firstVertex);
1949
bsalomoned0bcad2015-05-04 10:36:42 -07001950 if (!vertices || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001951 SkDebugf("Could not allocate vertices\n");
1952 return;
1953 }
1954
joshualitt76e7fb62015-02-11 08:52:27 -08001955 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1956
1957 for (int i = 0; i < instanceCount; i++) {
1958 Geometry& args = fGeoData[i];
1959
1960 // Compute the reciprocals of the radii here to save time in the shader
1961 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1962 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1963 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1964 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1965
1966 // Extend the radii out half a pixel to antialias.
1967 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1968 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1969
egdanielbc227142015-04-21 06:28:08 -07001970 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001971
1972 SkScalar yCoords[4] = {
1973 bounds.fTop,
1974 bounds.fTop + yOuterRadius,
1975 bounds.fBottom - yOuterRadius,
1976 bounds.fBottom
1977 };
1978 SkScalar yOuterOffsets[4] = {
1979 yOuterRadius,
1980 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1981 SK_ScalarNearlyZero,
1982 yOuterRadius
1983 };
1984
1985 for (int i = 0; i < 4; ++i) {
1986 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1987 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1988 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1989 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1990 verts++;
1991
1992 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1993 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1994 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1995 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1996 verts++;
1997
1998 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1999 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2000 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2001 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2002 verts++;
2003
2004 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
2005 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2006 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2007 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2008 verts++;
2009 }
2010 }
2011
2012 // drop out the middle quad if we're stroked
2013 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
2014 SK_ARRAY_COUNT(gRRectIndices);
2015
2016 GrDrawTarget::DrawInfo drawInfo;
2017 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
2018 drawInfo.setStartVertex(0);
2019 drawInfo.setStartIndex(0);
2020 drawInfo.setVerticesPerInstance(kVertsPerRRect);
2021 drawInfo.setIndicesPerInstance(indexCnt);
2022 drawInfo.adjustStartVertex(firstVertex);
2023 drawInfo.setVertexBuffer(vertexBuffer);
bsalomoned0bcad2015-05-04 10:36:42 -07002024 drawInfo.setIndexBuffer(indexBuffer);
joshualitt76e7fb62015-02-11 08:52:27 -08002025
2026 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
2027
2028 while (instanceCount) {
2029 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
2030 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
2031 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
2032
2033 batchTarget->draw(drawInfo);
2034
2035 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
2036 instanceCount -= drawInfo.instanceCount();
2037 }
2038 }
2039
2040 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
2041
2042private:
bsalomoned0bcad2015-05-04 10:36:42 -07002043 RRectEllipseRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08002044 this->initClassID<RRectEllipseRendererBatch>();
2045 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07002046
2047 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08002048 }
2049
mtklein36352bf2015-03-25 18:17:31 -07002050 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002051 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2052
2053 // TODO use vertex color to avoid breaking batches
2054 if (this->color() != that->color()) {
2055 return false;
2056 }
2057
2058 if (this->stroke() != that->stroke()) {
2059 return false;
2060 }
2061
2062 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
2063 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
2064 return false;
2065 }
2066
2067 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07002068 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08002069 return true;
2070 }
2071
2072 GrColor color() const { return fBatch.fColor; }
2073 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
2074 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
2075 bool stroke() const { return fBatch.fStroke; }
2076
2077 struct BatchTracker {
2078 GrColor fColor;
2079 bool fStroke;
2080 bool fUsesLocalCoords;
2081 bool fColorIgnored;
2082 bool fCoverageIgnored;
2083 };
2084
joshualitt76e7fb62015-02-11 08:52:27 -08002085 BatchTracker fBatch;
2086 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08002087};
2088
joshualitt3e708c52015-04-30 13:49:27 -07002089static GrBatch* create_rrect_batch(GrColor color,
2090 const SkMatrix& viewMatrix,
2091 const SkRRect& rrect,
2092 const SkStrokeRec& stroke,
bsalomoned0bcad2015-05-04 10:36:42 -07002093 SkRect* bounds) {
joshualitt3e708c52015-04-30 13:49:27 -07002094 SkASSERT(viewMatrix.rectStaysRect());
2095 SkASSERT(rrect.isSimple());
2096 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002097
joshualitt3e708c52015-04-30 13:49:27 -07002098 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002099 // do any matrix crunching before we reset the draw state for device coords
2100 const SkRect& rrectBounds = rrect.getBounds();
joshualitt3e708c52015-04-30 13:49:27 -07002101 viewMatrix.mapRect(bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002102
2103 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002104 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2105 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2106 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2107 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002108
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002109 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002110
2111 // do (potentially) anisotropic mapping of stroke
2112 SkVector scaledStroke;
2113 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002114
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002115 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2116 SkStrokeRec::kHairline_Style == style;
2117 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2118
2119 if (hasStroke) {
2120 if (SkStrokeRec::kHairline_Style == style) {
2121 scaledStroke.set(1, 1);
2122 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002123 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2124 viewMatrix[SkMatrix::kMSkewY]));
2125 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2126 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002127 }
2128
2129 // if half of strokewidth is greater than radius, we don't handle that right now
2130 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07002131 return NULL;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002132 }
2133 }
2134
2135 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2136 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2137 // patch will have fractional coverage. This only matters when the interior is actually filled.
2138 // We could consider falling back to rect rendering here, since a tiny radius is
2139 // indistinguishable from a square corner.
2140 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07002141 return NULL;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002142 }
2143
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002144 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002145 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002146 SkScalar innerRadius = 0.0f;
2147 SkScalar outerRadius = xRadius;
2148 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002149 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002150 if (SkScalarNearlyZero(scaledStroke.fX)) {
2151 halfWidth = SK_ScalarHalf;
2152 } else {
2153 halfWidth = SkScalarHalf(scaledStroke.fX);
2154 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002155
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002156 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002157 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002158 }
2159 outerRadius += halfWidth;
joshualitt3e708c52015-04-30 13:49:27 -07002160 bounds->outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002161 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002162
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002163 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002164
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002165 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08002166 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2167 // Second, the outer radius is used to compute the verts of the bounding box that is
2168 // rendered and the outset ensures the box will cover all partially covered by the rrect
2169 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002170 outerRadius += SK_ScalarHalf;
2171 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002172
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002173 // Expand the rect so all the pixels will be captured.
joshualitt3e708c52015-04-30 13:49:27 -07002174 bounds->outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002175
joshualitt76e7fb62015-02-11 08:52:27 -08002176 RRectCircleRendererBatch::Geometry geometry;
2177 geometry.fViewMatrix = viewMatrix;
2178 geometry.fColor = color;
2179 geometry.fInnerRadius = innerRadius;
2180 geometry.fOuterRadius = outerRadius;
2181 geometry.fStroke = isStrokeOnly;
joshualitt3e708c52015-04-30 13:49:27 -07002182 geometry.fDevBounds = *bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002183
bsalomoned0bcad2015-05-04 10:36:42 -07002184 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002185 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002186 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002187 SkScalar innerXRadius = 0.0f;
2188 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002189 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002190 if (SkScalarNearlyZero(scaledStroke.length())) {
2191 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2192 } else {
2193 scaledStroke.scale(SK_ScalarHalf);
2194 }
2195
2196 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00002197 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002198 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07002199 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002200 }
2201
2202 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2203 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2204 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07002205 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002206 }
2207
2208 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002209 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002210 innerXRadius = xRadius - scaledStroke.fX;
2211 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002212 }
2213
2214 xRadius += scaledStroke.fX;
2215 yRadius += scaledStroke.fY;
joshualitt3e708c52015-04-30 13:49:27 -07002216 bounds->outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002217 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002218
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002219 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002220
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002221 // Expand the rect so all the pixels will be captured.
joshualitt3e708c52015-04-30 13:49:27 -07002222 bounds->outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002223
joshualitt76e7fb62015-02-11 08:52:27 -08002224 RRectEllipseRendererBatch::Geometry geometry;
2225 geometry.fViewMatrix = viewMatrix;
2226 geometry.fColor = color;
2227 geometry.fXRadius = xRadius;
2228 geometry.fYRadius = yRadius;
2229 geometry.fInnerXRadius = innerXRadius;
2230 geometry.fInnerYRadius = innerYRadius;
2231 geometry.fStroke = isStrokeOnly;
joshualitt3e708c52015-04-30 13:49:27 -07002232 geometry.fDevBounds = *bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002233
bsalomoned0bcad2015-05-04 10:36:42 -07002234 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002235 }
joshualitt3e708c52015-04-30 13:49:27 -07002236}
2237
2238bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2239 GrPipelineBuilder* pipelineBuilder,
2240 GrColor color,
2241 const SkMatrix& viewMatrix,
2242 bool useAA,
2243 const SkRRect& rrect,
2244 const SkStrokeRec& stroke) {
2245 if (rrect.isOval()) {
2246 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2247 stroke);
2248 }
2249
2250 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
2251
2252 // only anti-aliased rrects for now
2253 if (!useCoverageAA) {
2254 return false;
2255 }
2256
2257 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2258 return false;
2259 }
2260
2261 SkRect bounds;
bsalomoned0bcad2015-05-04 10:36:42 -07002262 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke, &bounds));
joshualitt3e708c52015-04-30 13:49:27 -07002263 if (!batch) {
2264 return false;
2265 }
2266
joshualitt99c7c072015-05-01 13:43:30 -07002267 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002268 return true;
2269}
joshualitt3e708c52015-04-30 13:49:27 -07002270
2271///////////////////////////////////////////////////////////////////////////////////////////////////
2272
2273#ifdef GR_TEST_UTILS
2274
2275static SkStrokeRec random_strokerec(SkRandom* random) {
2276 SkStrokeRec::InitStyle style =
2277 SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
2278 SkStrokeRec rec(style);
2279 bool strokeAndFill = random->nextBool();
2280 SkScalar strokeWidth = random->nextBool() ? 0.f : 1.f;
2281 rec.setStrokeStyle(strokeWidth, strokeAndFill);
2282 return rec;
2283}
2284
2285BATCH_TEST_DEFINE(CircleBatch) {
2286 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2287 GrColor color = GrRandomColor(random);
2288 bool useCoverageAA = random->nextBool();
2289 SkRect circle = GrTest::TestRect(random);
2290 SkRect bounds; // unused
2291 return create_circle_batch(color, viewMatrix, useCoverageAA, circle, random_strokerec(random),
2292 &bounds);
2293}
2294
2295BATCH_TEST_DEFINE(EllipseBatch) {
2296 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2297 GrColor color = GrRandomColor(random);
2298 bool useCoverageAA = random->nextBool();
2299 SkRect ellipse = GrTest::TestRect(random);
2300 SkRect bounds; // unused
2301 return create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
2302 random_strokerec(random), &bounds);
2303}
2304
2305BATCH_TEST_DEFINE(DIEllipseBatch) {
2306 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2307 GrColor color = GrRandomColor(random);
2308 bool useCoverageAA = random->nextBool();
2309 SkRect ellipse = GrTest::TestRect(random);
2310 SkRect bounds; // unused
2311 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
2312 random_strokerec(random), &bounds);
2313}
2314
2315BATCH_TEST_DEFINE(RRectBatch) {
2316 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2317 GrColor color = GrRandomColor(random);
2318 const SkRRect& rrect = GrTest::TestRRectSimple(random);
2319
joshualitt3e708c52015-04-30 13:49:27 -07002320 SkRect bounds;
bsalomoned0bcad2015-05-04 10:36:42 -07002321 return create_rrect_batch(color, viewMatrix, rrect, random_strokerec(random), &bounds);
joshualitt3e708c52015-04-30 13:49:27 -07002322}
2323
2324#endif