blob: 20ae77cb5244a258bec1de34730772d4f2f2d33f [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"
12#include "GrBufferAllocPool.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000013#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080014#include "GrGeometryProcessor.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000015#include "GrGpu.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"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000019#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000020#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000021#include "SkTLazy.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000022#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080023#include "gl/GrGLProcessor.h"
24#include "gl/GrGLSL.h"
25#include "gl/GrGLGeometryProcessor.h"
26#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070031// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
34 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
37};
38
39struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000040 SkPoint fPos;
41 SkPoint fOffset;
42 SkPoint fOuterRadii;
43 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000044};
45
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000046struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkPoint fPos;
48 SkPoint fOuterOffset;
49 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000050};
51
commit-bot@chromium.org81312832013-03-22 18:34:09 +000052inline bool circle_stays_circle(const SkMatrix& m) {
53 return m.isSimilarity();
54}
55
56}
57
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000058///////////////////////////////////////////////////////////////////////////////
59
60/**
bsalomonce1c8862014-12-15 07:11:22 -080061 * The output of this effect is a modulation of the input color and coverage for a circle. It
62 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
63 * with origin at the circle center. Two vertex attributes are used:
64 * vec2f : position in device space of the bounding geometry vertices
65 * vec4f : (p.xy, outerRad, innerRad)
66 * p is the position in the normalized space.
67 * outerRad is the outerRadius in device space.
68 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000069 */
70
joshualitt249af152014-09-15 11:41:13 -070071class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072public:
joshualittd27f73e2014-12-29 07:43:36 -080073 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
74 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000075 }
76
joshualitt71c92602015-01-14 08:12:47 -080077 const Attribute* inPosition() const { return fInPosition; }
78 const Attribute* inCircleEdge() const { return fInCircleEdge; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079 virtual ~CircleEdgeEffect() {}
80
mtklein72c9faa2015-01-09 10:06:39 -080081 const char* name() const SK_OVERRIDE { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000082
83 inline bool isStroked() const { return fStroke; }
84
joshualittb0a8a372014-09-23 09:50:21 -070085 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000086 public:
joshualitteb2a6762014-12-04 11:35:33 -080087 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080088 const GrBatchTracker&)
89 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000090
robertphillips46d36f02015-01-18 08:14:14 -080091 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
joshualitt2dd1ae02014-12-03 06:24:10 -080092 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080093 GrGLGPBuilder* pb = args.fPB;
94 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080095 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
96
joshualittabb52a12015-01-13 15:02:10 -080097 // emit attributes
98 vsBuilder->emitAttributes(ce);
99
joshualitt74077b92014-10-24 11:26:03 -0700100 GrGLVertToFrag v(kVec4f_GrSLType);
101 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800102 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000103
joshualitt9b989322014-12-15 14:16:27 -0800104 // Setup pass through color
105 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
106 &fColorUniform);
107
joshualittabb52a12015-01-13 15:02:10 -0800108 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800109 this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800110
111 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800112 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800113 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);;
joshualitt4973d9d2014-11-08 09:24:25 -0800114
joshualittc369e7c2014-10-22 10:56:26 -0700115 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700116 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800117 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800118 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800119 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
120 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700121 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000123
joshualitt2dd1ae02014-12-03 06:24:10 -0800124 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000125 }
126
robertphillips46d36f02015-01-18 08:14:14 -0800127 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800128 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800129 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700130 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800131 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800132 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800133 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800134 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
135 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800136 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000137 }
138
joshualitt9b989322014-12-15 14:16:27 -0800139 virtual void setData(const GrGLProgramDataManager& pdman,
140 const GrPrimitiveProcessor& gp,
141 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800142 this->setUniformViewMatrix(pdman, gp.viewMatrix());
143
joshualitt9b989322014-12-15 14:16:27 -0800144 const BatchTracker& local = bt.cast<BatchTracker>();
145 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
146 GrGLfloat c[4];
147 GrColorToRGBAFloat(local.fColor, c);
148 pdman.set4fv(fColorUniform, 1, c);
149 fColor = local.fColor;
150 }
151 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000152
153 private:
joshualitt9b989322014-12-15 14:16:27 -0800154 GrColor fColor;
155 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700156 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000157 };
158
joshualitteb2a6762014-12-04 11:35:33 -0800159 virtual void getGLProcessorKey(const GrBatchTracker& bt,
160 const GrGLCaps& caps,
161 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
162 GLProcessor::GenKey(*this, bt, caps, b);
163 }
164
joshualittabb52a12015-01-13 15:02:10 -0800165 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
166 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800167 return SkNEW_ARGS(GLProcessor, (*this, bt));
168 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000169
joshualitt4d8da812015-01-28 12:53:54 -0800170 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800171 BatchTracker* local = bt->cast<BatchTracker>();
172 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800173 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800174 }
175
joshualitt290c09b2014-12-19 13:45:20 -0800176 bool onCanMakeEqual(const GrBatchTracker& m,
177 const GrGeometryProcessor& that,
178 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800179 const BatchTracker& mine = m.cast<BatchTracker>();
180 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800181 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
182 that, theirs.fUsesLocalCoords) &&
183 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800184 theirs.fInputColorType, theirs.fColor);
185 }
186
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000187private:
joshualittd27f73e2014-12-29 07:43:36 -0800188 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800189 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800190 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800191 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
192 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800193 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000194 fStroke = stroke;
195 }
196
mtklein72c9faa2015-01-09 10:06:39 -0800197 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700198 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000199 return cee.fStroke == fStroke;
200 }
201
mtklein72c9faa2015-01-09 10:06:39 -0800202 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800203 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700204 }
205
joshualitt9b989322014-12-15 14:16:27 -0800206 struct BatchTracker {
207 GrGPInput fInputColorType;
208 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800209 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800210 };
211
joshualitt71c92602015-01-14 08:12:47 -0800212 const Attribute* fInPosition;
213 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214 bool fStroke;
215
joshualittb0a8a372014-09-23 09:50:21 -0700216 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217
joshualitt249af152014-09-15 11:41:13 -0700218 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219};
220
joshualittb0a8a372014-09-23 09:50:21 -0700221GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000222
joshualittb0a8a372014-09-23 09:50:21 -0700223GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
224 GrContext* context,
225 const GrDrawTargetCaps&,
226 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800227 return CircleEdgeEffect::Create(GrRandomColor(random),
228 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800229 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230}
231
232///////////////////////////////////////////////////////////////////////////////
233
234/**
235 * 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 +0000236 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
237 * in both x and y directions.
238 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000239 * 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 +0000240 */
241
joshualitt249af152014-09-15 11:41:13 -0700242class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243public:
joshualittd27f73e2014-12-29 07:43:36 -0800244 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
245 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246 }
247
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 virtual ~EllipseEdgeEffect() {}
249
mtklein72c9faa2015-01-09 10:06:39 -0800250 const char* name() const SK_OVERRIDE { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800251
joshualitt71c92602015-01-14 08:12:47 -0800252 const Attribute* inPosition() const { return fInPosition; }
253 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
254 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700255
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256 inline bool isStroked() const { return fStroke; }
257
joshualittb0a8a372014-09-23 09:50:21 -0700258 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000259 public:
joshualitteb2a6762014-12-04 11:35:33 -0800260 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800261 const GrBatchTracker&)
262 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263
robertphillips46d36f02015-01-18 08:14:14 -0800264 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
joshualitt2dd1ae02014-12-03 06:24:10 -0800265 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800266 GrGLGPBuilder* pb = args.fPB;
267 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800268 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000269
joshualittabb52a12015-01-13 15:02:10 -0800270 // emit attributes
271 vsBuilder->emitAttributes(ee);
272
joshualitt74077b92014-10-24 11:26:03 -0700273 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
274 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700275 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800276 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000277
joshualitt74077b92014-10-24 11:26:03 -0700278 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
279 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
280 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800281 ee.inEllipseRadii()->fName);
282
joshualitt9b989322014-12-15 14:16:27 -0800283 // Setup pass through color
284 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
285 &fColorUniform);
286
joshualittabb52a12015-01-13 15:02:10 -0800287 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800288 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800289
290 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800291 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800292 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800293
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000294 // for outer curve
joshualittc369e7c2014-10-22 10:56:26 -0700295 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700296 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
297 ellipseRadii.fsIn());
298 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
299 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
300 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
301
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000302 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700303 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
304 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
305 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000306
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000307 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800308 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700309 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
310 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
311 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
312 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
313 ellipseRadii.fsIn());
314 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
315 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000316 }
317
joshualitt2dd1ae02014-12-03 06:24:10 -0800318 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000319 }
320
robertphillips46d36f02015-01-18 08:14:14 -0800321 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800322 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800323 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700324 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800325 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800326 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800327 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800328 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
329 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800330 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000331 }
332
joshualitt9b989322014-12-15 14:16:27 -0800333 virtual void setData(const GrGLProgramDataManager& pdman,
334 const GrPrimitiveProcessor& gp,
335 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800336 this->setUniformViewMatrix(pdman, gp.viewMatrix());
337
joshualitt9b989322014-12-15 14:16:27 -0800338 const BatchTracker& local = bt.cast<BatchTracker>();
339 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
340 GrGLfloat c[4];
341 GrColorToRGBAFloat(local.fColor, c);
342 pdman.set4fv(fColorUniform, 1, c);
343 fColor = local.fColor;
344 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000345 }
346
347 private:
joshualitt9b989322014-12-15 14:16:27 -0800348 GrColor fColor;
349 UniformHandle fColorUniform;
350
joshualitt249af152014-09-15 11:41:13 -0700351 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 };
353
joshualitteb2a6762014-12-04 11:35:33 -0800354 virtual void getGLProcessorKey(const GrBatchTracker& bt,
355 const GrGLCaps& caps,
356 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
357 GLProcessor::GenKey(*this, bt, caps, b);
358 }
359
joshualittabb52a12015-01-13 15:02:10 -0800360 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
361 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800362 return SkNEW_ARGS(GLProcessor, (*this, bt));
363 }
364
joshualitt4d8da812015-01-28 12:53:54 -0800365 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800366 BatchTracker* local = bt->cast<BatchTracker>();
367 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800368 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800369 }
370
joshualitt290c09b2014-12-19 13:45:20 -0800371 bool onCanMakeEqual(const GrBatchTracker& m,
372 const GrGeometryProcessor& that,
373 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800374 const BatchTracker& mine = m.cast<BatchTracker>();
375 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800376 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
377 that, theirs.fUsesLocalCoords) &&
378 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800379 theirs.fInputColorType, theirs.fColor);
380 }
381
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000382private:
joshualittd27f73e2014-12-29 07:43:36 -0800383 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800384 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800385 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800386 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
387 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800388 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800389 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800390 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000391 fStroke = stroke;
392 }
393
mtklein72c9faa2015-01-09 10:06:39 -0800394 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700395 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000396 return eee.fStroke == fStroke;
397 }
398
mtklein72c9faa2015-01-09 10:06:39 -0800399 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800400 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700401 }
402
joshualitt9b989322014-12-15 14:16:27 -0800403 struct BatchTracker {
404 GrGPInput fInputColorType;
405 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800406 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800407 };
408
joshualitt71c92602015-01-14 08:12:47 -0800409 const Attribute* fInPosition;
410 const Attribute* fInEllipseOffset;
411 const Attribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000412 bool fStroke;
413
joshualittb0a8a372014-09-23 09:50:21 -0700414 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000415
joshualitt249af152014-09-15 11:41:13 -0700416 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000417};
418
joshualittb0a8a372014-09-23 09:50:21 -0700419GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000420
joshualittb0a8a372014-09-23 09:50:21 -0700421GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
422 GrContext* context,
423 const GrDrawTargetCaps&,
424 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800425 return EllipseEdgeEffect::Create(GrRandomColor(random),
426 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800427 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000428}
429
430///////////////////////////////////////////////////////////////////////////////
431
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000432/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000433 * 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 +0000434 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
435 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
436 * using differentials.
437 *
438 * The result is device-independent and can be used with any affine matrix.
439 */
440
joshualitt249af152014-09-15 11:41:13 -0700441class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000442public:
443 enum Mode { kStroke = 0, kHairline, kFill };
444
joshualitt8059eb92014-12-29 15:10:07 -0800445 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
446 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000447 }
448
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000449 virtual ~DIEllipseEdgeEffect() {}
450
mtklein72c9faa2015-01-09 10:06:39 -0800451 const char* name() const SK_OVERRIDE { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000452
joshualitt71c92602015-01-14 08:12:47 -0800453 const Attribute* inPosition() const { return fInPosition; }
454 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
455 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700456
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000457 inline Mode getMode() const { return fMode; }
458
joshualittb0a8a372014-09-23 09:50:21 -0700459 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000460 public:
joshualitteb2a6762014-12-04 11:35:33 -0800461 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800462 const GrBatchTracker&)
463 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000464
robertphillips46d36f02015-01-18 08:14:14 -0800465 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
joshualitt2dd1ae02014-12-03 06:24:10 -0800466 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800467 GrGLGPBuilder* pb = args.fPB;
468 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800469 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000470
joshualittabb52a12015-01-13 15:02:10 -0800471 // emit attributes
472 vsBuilder->emitAttributes(ee);
473
joshualitt74077b92014-10-24 11:26:03 -0700474 GrGLVertToFrag offsets0(kVec2f_GrSLType);
475 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700476 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800477 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700478
479 GrGLVertToFrag offsets1(kVec2f_GrSLType);
480 args.fPB->addVarying("EllipseOffsets1", &offsets1);
481 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800482 ee.inEllipseOffsets1()->fName);
483
joshualitt9b989322014-12-15 14:16:27 -0800484 // Setup pass through color
485 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
486 &fColorUniform);
487
joshualittabb52a12015-01-13 15:02:10 -0800488 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800489 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800490
491 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800492 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800493 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800494
joshualittc369e7c2014-10-22 10:56:26 -0700495 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700496 SkAssertResult(fsBuilder->enableFeature(
497 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000498 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700499 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
500 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
501 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
502 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
503 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
504 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
505 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000506
joshualitt74077b92014-10-24 11:26:03 -0700507 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000508 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700509 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
510 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800511 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000512 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700513 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
514 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000515 } else {
joshualitt74077b92014-10-24 11:26:03 -0700516 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 }
518
519 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800520 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700521 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
522 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
523 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
524 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
525 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
526 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
527 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
528 offsets1.fsIn());
529 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
530 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000531 }
532
joshualitt2dd1ae02014-12-03 06:24:10 -0800533 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534 }
535
robertphillips46d36f02015-01-18 08:14:14 -0800536 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800537 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800538 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700539 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800540 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800541 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800542 uint16_t key = ellipseEffect.getMode();
robertphillips46d36f02015-01-18 08:14:14 -0800543 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
544 key |= ComputePosKey(gp.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800545 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546 }
547
joshualitt9b989322014-12-15 14:16:27 -0800548 virtual void setData(const GrGLProgramDataManager& pdman,
549 const GrPrimitiveProcessor& gp,
550 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800551 this->setUniformViewMatrix(pdman, gp.viewMatrix());
552
joshualitt9b989322014-12-15 14:16:27 -0800553 const BatchTracker& local = bt.cast<BatchTracker>();
554 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
555 GrGLfloat c[4];
556 GrColorToRGBAFloat(local.fColor, c);
557 pdman.set4fv(fColorUniform, 1, c);
558 fColor = local.fColor;
559 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000560 }
561
562 private:
joshualitt9b989322014-12-15 14:16:27 -0800563 GrColor fColor;
564 UniformHandle fColorUniform;
565
joshualitt249af152014-09-15 11:41:13 -0700566 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000567 };
568
joshualitteb2a6762014-12-04 11:35:33 -0800569 virtual void getGLProcessorKey(const GrBatchTracker& bt,
570 const GrGLCaps& caps,
571 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
572 GLProcessor::GenKey(*this, bt, caps, b);
573 }
574
joshualittabb52a12015-01-13 15:02:10 -0800575 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
576 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800577 return SkNEW_ARGS(GLProcessor, (*this, bt));
578 }
579
joshualitt4d8da812015-01-28 12:53:54 -0800580 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800581 BatchTracker* local = bt->cast<BatchTracker>();
582 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800583 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800584 }
585
joshualitt290c09b2014-12-19 13:45:20 -0800586 bool onCanMakeEqual(const GrBatchTracker& m,
587 const GrGeometryProcessor& that,
588 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800589 const BatchTracker& mine = m.cast<BatchTracker>();
590 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800591 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
592 that, theirs.fUsesLocalCoords) &&
593 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800594 theirs.fInputColorType, theirs.fColor);
595 }
596
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000597private:
joshualitt8059eb92014-12-29 15:10:07 -0800598 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
599 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800600 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800601 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
602 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800603 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800604 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800605 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000606 fMode = mode;
607 }
608
mtklein72c9faa2015-01-09 10:06:39 -0800609 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700610 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000611 return eee.fMode == fMode;
612 }
613
mtklein72c9faa2015-01-09 10:06:39 -0800614 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800615 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700616 }
617
joshualitt9b989322014-12-15 14:16:27 -0800618 struct BatchTracker {
619 GrGPInput fInputColorType;
620 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800621 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800622 };
623
joshualitt71c92602015-01-14 08:12:47 -0800624 const Attribute* fInPosition;
625 const Attribute* fInEllipseOffsets0;
626 const Attribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000627 Mode fMode;
628
joshualittb0a8a372014-09-23 09:50:21 -0700629 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000630
joshualitt249af152014-09-15 11:41:13 -0700631 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000632};
633
joshualittb0a8a372014-09-23 09:50:21 -0700634GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000635
joshualittb0a8a372014-09-23 09:50:21 -0700636GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
637 GrContext* context,
638 const GrDrawTargetCaps&,
639 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800640 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
641 GrProcessorUnitTest::TestMatrix(random),
642 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000643}
644
645///////////////////////////////////////////////////////////////////////////////
646
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000647void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000648 SkSafeSetNull(fRRectIndexBuffer);
joshualitt58a65442014-10-22 20:53:24 -0700649 SkSafeSetNull(fStrokeRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000650}
651
joshualitt9853cce2014-11-17 14:22:48 -0800652bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800653 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800654 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800655 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800656 bool useAA,
657 const SkRect& oval,
658 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000659{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000660 bool useCoverageAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -0800661 !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000662
663 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000664 return false;
665 }
666
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000667 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800668 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800669 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000670 // if we have shader derivative support, render as device-independent
671 } else if (target->caps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800672 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800673 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000674 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800675 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800676 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
677 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000678 } else {
679 return false;
680 }
681
682 return true;
683}
684
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000685///////////////////////////////////////////////////////////////////////////////
686
joshualitt76e7fb62015-02-11 08:52:27 -0800687class CircleBatch : public GrBatch {
688public:
689 struct Geometry {
690 GrColor fColor;
691 SkMatrix fViewMatrix;
692 SkScalar fInnerRadius;
693 SkScalar fOuterRadius;
694 bool fStroke;
695 SkRect fDevBounds;
696 };
697
698 static GrBatch* Create(const Geometry& geometry) {
699 return SkNEW_ARGS(CircleBatch, (geometry));
700 }
701
702 const char* name() const SK_OVERRIDE { return "CircleBatch"; }
703
704 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
705 // When this is called on a batch, there is only one geometry bundle
706 out->setKnownFourComponents(fGeoData[0].fColor);
707 }
708
709 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
710 out->setUnknownSingleComponent();
711 }
712
joshualitt76e7fb62015-02-11 08:52:27 -0800713 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
714 // Handle any color overrides
715 if (init.fColorIgnored) {
716 fGeoData[0].fColor = GrColor_ILLEGAL;
717 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
718 fGeoData[0].fColor = init.fOverrideColor;
719 }
720
721 // setup batch properties
722 fBatch.fColorIgnored = init.fColorIgnored;
723 fBatch.fColor = fGeoData[0].fColor;
724 fBatch.fStroke = fGeoData[0].fStroke;
725 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
726 fBatch.fCoverageIgnored = init.fCoverageIgnored;
727 }
728
729 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
730 SkMatrix invert;
731 if (!this->viewMatrix().invert(&invert)) {
732 return;
733 }
734
735 // Setup geometry processor
736 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
737 this->stroke(),
738 invert));
739
740 batchTarget->initDraw(gp, pipeline);
741
742 // TODO this is hacky, but the only way we have to initialize the GP is to use the
743 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
744 // everywhere we can remove this nastiness
745 GrPipelineInfo init;
746 init.fColorIgnored = fBatch.fColorIgnored;
747 init.fOverrideColor = GrColor_ILLEGAL;
748 init.fCoverageIgnored = fBatch.fCoverageIgnored;
749 init.fUsesLocalCoords = this->usesLocalCoords();
750 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
751
752 int instanceCount = fGeoData.count();
753 int vertexCount = kVertsPerCircle * instanceCount;
754 size_t vertexStride = gp->getVertexStride();
755 SkASSERT(vertexStride == sizeof(CircleVertex));
756
757 const GrVertexBuffer* vertexBuffer;
758 int firstVertex;
759
760 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
761 vertexCount,
762 &vertexBuffer,
763 &firstVertex);
764
joshualitt4b31de82015-03-05 14:33:41 -0800765 if (!vertices || !batchTarget->quadIndexBuffer()) {
766 SkDebugf("Could not allocate buffers\n");
767 return;
768 }
769
joshualitt76e7fb62015-02-11 08:52:27 -0800770 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
771
772 for (int i = 0; i < instanceCount; i++) {
773 Geometry& args = fGeoData[i];
774
775 SkScalar innerRadius = args.fInnerRadius;
776 SkScalar outerRadius = args.fOuterRadius;
777
778 const SkRect& bounds = args.fDevBounds;
779
780 // The inner radius in the vertex data must be specified in normalized space.
781 innerRadius = innerRadius / outerRadius;
782 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
783 verts[0].fOffset = SkPoint::Make(-1, -1);
784 verts[0].fOuterRadius = outerRadius;
785 verts[0].fInnerRadius = innerRadius;
786
787 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
788 verts[1].fOffset = SkPoint::Make(-1, 1);
789 verts[1].fOuterRadius = outerRadius;
790 verts[1].fInnerRadius = innerRadius;
791
792 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
793 verts[2].fOffset = SkPoint::Make(1, 1);
794 verts[2].fOuterRadius = outerRadius;
795 verts[2].fInnerRadius = innerRadius;
796
797 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
798 verts[3].fOffset = SkPoint::Make(1, -1);
799 verts[3].fOuterRadius = outerRadius;
800 verts[3].fInnerRadius = innerRadius;
801
802 verts += kVertsPerCircle;
803 }
804
805 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
806
807 GrDrawTarget::DrawInfo drawInfo;
808 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
809 drawInfo.setStartVertex(0);
810 drawInfo.setStartIndex(0);
811 drawInfo.setVerticesPerInstance(kVertsPerCircle);
812 drawInfo.setIndicesPerInstance(kIndicesPerCircle);
813 drawInfo.adjustStartVertex(firstVertex);
814 drawInfo.setVertexBuffer(vertexBuffer);
815 drawInfo.setIndexBuffer(quadIndexBuffer);
816
817 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
818
819 while (instanceCount) {
820 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
821 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
822 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
823
824 batchTarget->draw(drawInfo);
825
826 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
827 instanceCount -= drawInfo.instanceCount();
828 }
829 }
830
831 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
832
833private:
834 CircleBatch(const Geometry& geometry) {
835 this->initClassID<CircleBatch>();
836 fGeoData.push_back(geometry);
837 }
838
839 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
840 CircleBatch* that = t->cast<CircleBatch>();
841
842 // TODO use vertex color to avoid breaking batches
843 if (this->color() != that->color()) {
844 return false;
845 }
846
847 if (this->stroke() != that->stroke()) {
848 return false;
849 }
850
851 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
852 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
853 return false;
854 }
855
856 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
857 return true;
858 }
859
860 GrColor color() const { return fBatch.fColor; }
861 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
862 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
863 bool stroke() const { return fBatch.fStroke; }
864
865 struct BatchTracker {
866 GrColor fColor;
867 bool fStroke;
868 bool fUsesLocalCoords;
869 bool fColorIgnored;
870 bool fCoverageIgnored;
871 };
872
873 static const int kVertsPerCircle = 4;
874 static const int kIndicesPerCircle = 6;
875
joshualitt76e7fb62015-02-11 08:52:27 -0800876 BatchTracker fBatch;
877 SkSTArray<1, Geometry, true> fGeoData;
878};
879
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000880void GrOvalRenderer::drawCircle(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800881 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800882 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800883 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000884 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000885 const SkRect& circle,
joshualitt8059eb92014-12-29 15:10:07 -0800886 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000887 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800888 viewMatrix.mapPoints(&center, 1);
889 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
890 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000891
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000892 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000893 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
894 SkStrokeRec::kHairline_Style == style;
895 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000896
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000897 SkScalar innerRadius = 0.0f;
898 SkScalar outerRadius = radius;
899 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000900 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000901 if (SkScalarNearlyZero(strokeWidth)) {
902 halfWidth = SK_ScalarHalf;
903 } else {
904 halfWidth = SkScalarHalf(strokeWidth);
905 }
906
907 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000908 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000909 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000910 }
911 }
912
bsalomonce1c8862014-12-15 07:11:22 -0800913 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
914 // computation because the computed alpha is zero, rather than 50%, at the radius.
915 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
916 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000917 outerRadius += SK_ScalarHalf;
918 innerRadius -= SK_ScalarHalf;
919
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000920 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000921 center.fX - outerRadius,
922 center.fY - outerRadius,
923 center.fX + outerRadius,
924 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000925 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000926
joshualitt76e7fb62015-02-11 08:52:27 -0800927 CircleBatch::Geometry geometry;
928 geometry.fViewMatrix = viewMatrix;
929 geometry.fColor = color;
930 geometry.fInnerRadius = innerRadius;
931 geometry.fOuterRadius = outerRadius;
932 geometry.fStroke = isStrokeOnly && innerRadius > 0;
933 geometry.fDevBounds = bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000934
joshualitt76e7fb62015-02-11 08:52:27 -0800935 SkAutoTUnref<GrBatch> batch(CircleBatch::Create(geometry));
936 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000937}
938
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000939///////////////////////////////////////////////////////////////////////////////
940
joshualitt76e7fb62015-02-11 08:52:27 -0800941class EllipseBatch : public GrBatch {
942public:
943 struct Geometry {
944 GrColor fColor;
945 SkMatrix fViewMatrix;
946 SkScalar fXRadius;
947 SkScalar fYRadius;
948 SkScalar fInnerXRadius;
949 SkScalar fInnerYRadius;
950 bool fStroke;
951 SkRect fDevBounds;
952 };
953
954 static GrBatch* Create(const Geometry& geometry) {
955 return SkNEW_ARGS(EllipseBatch, (geometry));
956 }
957
958 const char* name() const SK_OVERRIDE { return "EllipseBatch"; }
959
960 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
961 // When this is called on a batch, there is only one geometry bundle
962 out->setKnownFourComponents(fGeoData[0].fColor);
963 }
964 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
965 out->setUnknownSingleComponent();
966 }
967
joshualitt76e7fb62015-02-11 08:52:27 -0800968 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
969 // Handle any color overrides
970 if (init.fColorIgnored) {
971 fGeoData[0].fColor = GrColor_ILLEGAL;
972 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
973 fGeoData[0].fColor = init.fOverrideColor;
974 }
975
976 // setup batch properties
977 fBatch.fColorIgnored = init.fColorIgnored;
978 fBatch.fColor = fGeoData[0].fColor;
979 fBatch.fStroke = fGeoData[0].fStroke;
980 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
981 fBatch.fCoverageIgnored = init.fCoverageIgnored;
982 }
983
984 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
985 SkMatrix invert;
986 if (!this->viewMatrix().invert(&invert)) {
987 return;
988 }
989
990 // Setup geometry processor
991 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
992 this->stroke(),
993 invert));
joshualitt76e7fb62015-02-11 08:52:27 -0800994
995 batchTarget->initDraw(gp, pipeline);
996
997 // TODO this is hacky, but the only way we have to initialize the GP is to use the
998 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
999 // everywhere we can remove this nastiness
1000 GrPipelineInfo init;
1001 init.fColorIgnored = fBatch.fColorIgnored;
1002 init.fOverrideColor = GrColor_ILLEGAL;
1003 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1004 init.fUsesLocalCoords = this->usesLocalCoords();
1005 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1006
1007 int instanceCount = fGeoData.count();
1008 int vertexCount = kVertsPerEllipse * instanceCount;
1009 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001010 SkASSERT(vertexStride == sizeof(EllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001011
1012 const GrVertexBuffer* vertexBuffer;
1013 int firstVertex;
1014
1015 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1016 vertexCount,
1017 &vertexBuffer,
1018 &firstVertex);
1019
joshualitt4b31de82015-03-05 14:33:41 -08001020 if (!vertices || !batchTarget->quadIndexBuffer()) {
1021 SkDebugf("Could not allocate buffers\n");
1022 return;
1023 }
1024
joshualitt76e7fb62015-02-11 08:52:27 -08001025 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1026
1027 for (int i = 0; i < instanceCount; i++) {
1028 Geometry& args = fGeoData[i];
1029
1030 SkScalar xRadius = args.fXRadius;
1031 SkScalar yRadius = args.fYRadius;
1032
1033 // Compute the reciprocals of the radii here to save time in the shader
1034 SkScalar xRadRecip = SkScalarInvert(xRadius);
1035 SkScalar yRadRecip = SkScalarInvert(yRadius);
1036 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1037 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1038
1039 const SkRect& bounds = args.fDevBounds;
1040
1041 // The inner radius in the vertex data must be specified in normalized space.
1042 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1043 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
1044 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1045 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1046
1047 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1048 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1049 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1050 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1051
1052 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1053 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1054 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1055 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1056
1057 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1058 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1059 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1060 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1061
1062 verts += kVertsPerEllipse;
1063 }
1064
1065 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1066
1067 GrDrawTarget::DrawInfo drawInfo;
1068 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1069 drawInfo.setStartVertex(0);
1070 drawInfo.setStartIndex(0);
1071 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1072 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1073 drawInfo.adjustStartVertex(firstVertex);
1074 drawInfo.setVertexBuffer(vertexBuffer);
1075 drawInfo.setIndexBuffer(quadIndexBuffer);
1076
1077 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1078
1079 while (instanceCount) {
1080 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1081 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1082 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1083
1084 batchTarget->draw(drawInfo);
1085
1086 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1087 instanceCount -= drawInfo.instanceCount();
1088 }
1089 }
1090
1091 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1092
1093private:
1094 EllipseBatch(const Geometry& geometry) {
1095 this->initClassID<EllipseBatch>();
1096 fGeoData.push_back(geometry);
1097 }
1098
1099 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1100 EllipseBatch* that = t->cast<EllipseBatch>();
1101
1102 // TODO use vertex color to avoid breaking batches
1103 if (this->color() != that->color()) {
1104 return false;
1105 }
1106
1107 if (this->stroke() != that->stroke()) {
1108 return false;
1109 }
1110
1111 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1112 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1113 return false;
1114 }
1115
1116 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1117 return true;
1118 }
1119
1120 GrColor color() const { return fBatch.fColor; }
1121 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1122 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1123 bool stroke() const { return fBatch.fStroke; }
1124
1125 struct BatchTracker {
1126 GrColor fColor;
1127 bool fStroke;
1128 bool fUsesLocalCoords;
1129 bool fColorIgnored;
1130 bool fCoverageIgnored;
1131 };
1132
1133 static const int kVertsPerEllipse = 4;
1134 static const int kIndicesPerEllipse = 6;
1135
joshualitt76e7fb62015-02-11 08:52:27 -08001136 BatchTracker fBatch;
1137 SkSTArray<1, Geometry, true> fGeoData;
1138};
1139
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001140bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001141 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001142 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001143 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001144 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +00001145 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -08001146 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001147#ifdef SK_DEBUG
1148 {
1149 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001150 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001151 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001152 }
1153#endif
1154
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001155 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001156 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001157 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001158 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1159 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001160 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1161 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1162 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1163 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001164
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001165 // do (potentially) anisotropic mapping of stroke
1166 SkVector scaledStroke;
1167 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001168 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1169 viewMatrix[SkMatrix::kMSkewY]));
1170 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1171 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001172
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001173 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001174 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1175 SkStrokeRec::kHairline_Style == style;
1176 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001177
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001178 SkScalar innerXRadius = 0;
1179 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001180 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001181 if (SkScalarNearlyZero(scaledStroke.length())) {
1182 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1183 } else {
1184 scaledStroke.scale(SK_ScalarHalf);
1185 }
1186
1187 // we only handle thick strokes for near-circular ellipses
1188 if (scaledStroke.length() > SK_ScalarHalf &&
1189 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1190 return false;
1191 }
1192
1193 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1194 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1195 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1196 return false;
1197 }
1198
1199 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001200 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001201 innerXRadius = xRadius - scaledStroke.fX;
1202 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001203 }
1204
1205 xRadius += scaledStroke.fX;
1206 yRadius += scaledStroke.fY;
1207 }
1208
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001209 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001210 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001211 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001212 xRadius += SK_ScalarHalf;
1213 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001214
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001215 SkRect bounds = SkRect::MakeLTRB(
1216 center.fX - xRadius,
1217 center.fY - yRadius,
1218 center.fX + xRadius,
1219 center.fY + yRadius
1220 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001221
joshualitt76e7fb62015-02-11 08:52:27 -08001222 EllipseBatch::Geometry geometry;
1223 geometry.fViewMatrix = viewMatrix;
1224 geometry.fColor = color;
1225 geometry.fXRadius = xRadius;
1226 geometry.fYRadius = yRadius;
1227 geometry.fInnerXRadius = innerXRadius;
1228 geometry.fInnerYRadius = innerYRadius;
1229 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1230 geometry.fDevBounds = bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001231
joshualitt76e7fb62015-02-11 08:52:27 -08001232 SkAutoTUnref<GrBatch> batch(EllipseBatch::Create(geometry));
1233 target->drawBatch(pipelineBuilder, batch, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001234
1235 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001236}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001237
joshualitt76e7fb62015-02-11 08:52:27 -08001238/////////////////////////////////////////////////////////////////////////////////////////////////
1239
1240class DIEllipseBatch : public GrBatch {
1241public:
1242 struct Geometry {
1243 GrColor fColor;
1244 SkMatrix fViewMatrix;
1245 SkScalar fXRadius;
1246 SkScalar fYRadius;
1247 SkScalar fInnerXRadius;
1248 SkScalar fInnerYRadius;
1249 SkScalar fGeoDx;
1250 SkScalar fGeoDy;
1251 DIEllipseEdgeEffect::Mode fMode;
1252 SkRect fDevBounds;
1253 };
1254
1255 static GrBatch* Create(const Geometry& geometry) {
1256 return SkNEW_ARGS(DIEllipseBatch, (geometry));
1257 }
1258
1259 const char* name() const SK_OVERRIDE { return "DIEllipseBatch"; }
1260
1261 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1262 // When this is called on a batch, there is only one geometry bundle
1263 out->setKnownFourComponents(fGeoData[0].fColor);
1264 }
1265 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1266 out->setUnknownSingleComponent();
1267 }
1268
joshualitt76e7fb62015-02-11 08:52:27 -08001269 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1270 // Handle any color overrides
1271 if (init.fColorIgnored) {
1272 fGeoData[0].fColor = GrColor_ILLEGAL;
1273 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1274 fGeoData[0].fColor = init.fOverrideColor;
1275 }
1276
1277 // setup batch properties
1278 fBatch.fColorIgnored = init.fColorIgnored;
1279 fBatch.fColor = fGeoData[0].fColor;
1280 fBatch.fMode = fGeoData[0].fMode;
1281 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1282 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1283 }
1284
1285 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1286 // Setup geometry processor
1287 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1288 this->viewMatrix(),
1289 this->mode()));
1290
joshualitt76e7fb62015-02-11 08:52:27 -08001291 batchTarget->initDraw(gp, pipeline);
1292
1293 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1294 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1295 // everywhere we can remove this nastiness
1296 GrPipelineInfo init;
1297 init.fColorIgnored = fBatch.fColorIgnored;
1298 init.fOverrideColor = GrColor_ILLEGAL;
1299 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1300 init.fUsesLocalCoords = this->usesLocalCoords();
1301 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1302
1303 int instanceCount = fGeoData.count();
1304 int vertexCount = kVertsPerEllipse * instanceCount;
1305 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001306 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001307
1308 const GrVertexBuffer* vertexBuffer;
1309 int firstVertex;
1310
1311 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1312 vertexCount,
1313 &vertexBuffer,
1314 &firstVertex);
1315
joshualitt4b31de82015-03-05 14:33:41 -08001316 if (!vertices || !batchTarget->quadIndexBuffer()) {
1317 SkDebugf("Could not allocate buffers\n");
1318 return;
1319 }
1320
joshualitt76e7fb62015-02-11 08:52:27 -08001321 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
1322
1323 for (int i = 0; i < instanceCount; i++) {
1324 Geometry& args = fGeoData[i];
1325
1326 SkScalar xRadius = args.fXRadius;
1327 SkScalar yRadius = args.fYRadius;
1328
1329 const SkRect& bounds = args.fDevBounds;
1330
1331 // This adjusts the "radius" to include the half-pixel border
1332 SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
1333 SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
1334
1335 SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
1336 SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
1337
1338 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1339 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1340 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1341
1342 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1343 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1344 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1345
1346 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1347 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1348 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1349
1350 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1351 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1352 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1353
1354 verts += kVertsPerEllipse;
1355 }
1356
1357 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1358
1359 GrDrawTarget::DrawInfo drawInfo;
1360 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1361 drawInfo.setStartVertex(0);
1362 drawInfo.setStartIndex(0);
1363 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1364 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1365 drawInfo.adjustStartVertex(firstVertex);
1366 drawInfo.setVertexBuffer(vertexBuffer);
1367 drawInfo.setIndexBuffer(quadIndexBuffer);
1368
1369 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1370
1371 while (instanceCount) {
1372 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1373 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1374 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1375
1376 batchTarget->draw(drawInfo);
1377
1378 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1379 instanceCount -= drawInfo.instanceCount();
1380 }
1381 }
1382
1383 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1384
1385private:
1386 DIEllipseBatch(const Geometry& geometry) {
1387 this->initClassID<DIEllipseBatch>();
1388 fGeoData.push_back(geometry);
1389 }
1390
1391 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1392 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1393
1394 // TODO use vertex color to avoid breaking batches
1395 if (this->color() != that->color()) {
1396 return false;
1397 }
1398
1399 if (this->mode() != that->mode()) {
1400 return false;
1401 }
1402
1403 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1404 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1405 return false;
1406 }
1407
1408 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1409 return true;
1410 }
1411
1412 GrColor color() const { return fBatch.fColor; }
1413 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1414 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1415 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1416
1417 struct BatchTracker {
1418 GrColor fColor;
1419 DIEllipseEdgeEffect::Mode fMode;
1420 bool fUsesLocalCoords;
1421 bool fColorIgnored;
1422 bool fCoverageIgnored;
1423 };
1424
1425 static const int kVertsPerEllipse = 4;
1426 static const int kIndicesPerEllipse = 6;
1427
joshualitt76e7fb62015-02-11 08:52:27 -08001428 BatchTracker fBatch;
1429 SkSTArray<1, Geometry, true> fGeoData;
1430};
1431
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001432bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001433 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001434 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001435 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001436 bool useCoverageAA,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001437 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -08001438 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001439 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001440 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001441 SkScalar yRadius = SkScalarHalf(ellipse.height());
1442
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001443 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001444 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001445 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001446 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001447 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1448
1449 SkScalar innerXRadius = 0;
1450 SkScalar innerYRadius = 0;
1451 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1452 SkScalar strokeWidth = stroke.getWidth();
1453
1454 if (SkScalarNearlyZero(strokeWidth)) {
1455 strokeWidth = SK_ScalarHalf;
1456 } else {
1457 strokeWidth *= SK_ScalarHalf;
1458 }
1459
1460 // we only handle thick strokes for near-circular ellipses
1461 if (strokeWidth > SK_ScalarHalf &&
1462 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1463 return false;
1464 }
1465
1466 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1467 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1468 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1469 return false;
1470 }
1471
1472 // set inner radius (if needed)
1473 if (SkStrokeRec::kStroke_Style == style) {
1474 innerXRadius = xRadius - strokeWidth;
1475 innerYRadius = yRadius - strokeWidth;
1476 }
1477
1478 xRadius += strokeWidth;
1479 yRadius += strokeWidth;
1480 }
1481 if (DIEllipseEdgeEffect::kStroke == mode) {
1482 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1483 DIEllipseEdgeEffect::kFill;
1484 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001485
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001486 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001487 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1488 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1489 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1490 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001491 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
1492 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001493
1494 SkRect bounds = SkRect::MakeLTRB(
1495 center.fX - xRadius - geoDx,
1496 center.fY - yRadius - geoDy,
1497 center.fX + xRadius + geoDx,
1498 center.fY + yRadius + geoDy
1499 );
1500
joshualitt76e7fb62015-02-11 08:52:27 -08001501 DIEllipseBatch::Geometry geometry;
1502 geometry.fViewMatrix = viewMatrix;
1503 geometry.fColor = color;
1504 geometry.fXRadius = xRadius;
1505 geometry.fYRadius = yRadius;
1506 geometry.fInnerXRadius = innerXRadius;
1507 geometry.fInnerYRadius = innerYRadius;
1508 geometry.fGeoDx = geoDx;
1509 geometry.fGeoDy = geoDy;
1510 geometry.fMode = mode;
1511 geometry.fDevBounds = bounds;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001512
joshualitt76e7fb62015-02-11 08:52:27 -08001513 SkAutoTUnref<GrBatch> batch(DIEllipseBatch::Create(geometry));
1514 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001515
1516 return true;
1517}
1518
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001519///////////////////////////////////////////////////////////////////////////////
1520
1521static const uint16_t gRRectIndices[] = {
1522 // corners
1523 0, 1, 5, 0, 5, 4,
1524 2, 3, 7, 2, 7, 6,
1525 8, 9, 13, 8, 13, 12,
1526 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001527
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001528 // edges
1529 1, 2, 6, 1, 6, 5,
1530 4, 5, 9, 4, 9, 8,
1531 6, 7, 11, 6, 11, 10,
1532 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001533
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001534 // center
1535 // we place this at the end so that we can ignore these indices when rendering stroke-only
1536 5, 6, 10, 5, 10, 9
1537};
1538
joshualitt5ead6da2014-10-22 16:00:29 -07001539static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1540static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1541static const int kVertsPerRRect = 16;
1542static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001543
joshualitt5531d512014-12-17 15:50:11 -08001544GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly) {
joshualitt5ead6da2014-10-22 16:00:29 -07001545 if (isStrokeOnly) {
1546 if (NULL == fStrokeRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001547 fStrokeRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1548 kIndicesPerStrokeRRect,
1549 kNumRRectsInIndexBuffer,
1550 kVertsPerRRect);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001551 }
joshualitt5ead6da2014-10-22 16:00:29 -07001552 return fStrokeRRectIndexBuffer;
1553 } else {
1554 if (NULL == fRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001555 fRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1556 kIndicesPerRRect,
1557 kNumRRectsInIndexBuffer,
1558 kVertsPerRRect);
joshualitt5ead6da2014-10-22 16:00:29 -07001559 }
1560 return fRRectIndexBuffer;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001561 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001562}
1563
joshualitt9853cce2014-11-17 14:22:48 -08001564bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001565 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001566 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001567 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001568 bool useAA,
1569 const SkRRect& origOuter,
1570 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001571 bool applyAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08001572 !pipelineBuilder->getRenderTarget()->isMultisampled();
bsalomon6be6f7c2015-02-26 13:05:21 -08001573 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001574 if (!origInner.isEmpty()) {
1575 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001576 if (!viewMatrix.isIdentity()) {
1577 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001578 return false;
1579 }
1580 }
joshualittb0a8a372014-09-23 09:50:21 -07001581 GrPrimitiveEdgeType edgeType = applyAA ?
1582 kInverseFillAA_GrProcessorEdgeType :
1583 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001584 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001585 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1586 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001587 return false;
1588 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001589 arfp.set(pipelineBuilder);
egdaniel8dd688b2015-01-22 10:16:09 -08001590 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001591 }
1592
1593 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001594 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001595 return true;
1596 }
1597
1598 SkASSERT(!origOuter.isEmpty());
1599 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001600 if (!viewMatrix.isIdentity()) {
1601 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001602 return false;
1603 }
1604 }
joshualittb0a8a372014-09-23 09:50:21 -07001605 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001606 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001607 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001608 if (NULL == effect) {
1609 return false;
1610 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001611 if (!arfp.isSet()) {
1612 arfp.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001613 }
joshualittd27f73e2014-12-29 07:43:36 -08001614
1615 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001616 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001617 return false;
1618 }
joshualittd27f73e2014-12-29 07:43:36 -08001619
egdaniel8dd688b2015-01-22 10:16:09 -08001620 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001621 SkRect bounds = outer->getBounds();
1622 if (applyAA) {
1623 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1624 }
egdaniel8dd688b2015-01-22 10:16:09 -08001625 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001626 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001627}
1628
joshualitt76e7fb62015-02-11 08:52:27 -08001629///////////////////////////////////////////////////////////////////////////////////////////////////
1630
1631class RRectCircleRendererBatch : public GrBatch {
1632public:
1633 struct Geometry {
1634 GrColor fColor;
1635 SkMatrix fViewMatrix;
1636 SkScalar fInnerRadius;
1637 SkScalar fOuterRadius;
1638 bool fStroke;
1639 SkRect fDevBounds;
1640 };
1641
1642 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1643 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry, indexBuffer));
1644 }
1645
1646 const char* name() const SK_OVERRIDE { return "RRectCircleBatch"; }
1647
1648 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1649 // When this is called on a batch, there is only one geometry bundle
1650 out->setKnownFourComponents(fGeoData[0].fColor);
1651 }
1652 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1653 out->setUnknownSingleComponent();
1654 }
1655
joshualitt76e7fb62015-02-11 08:52:27 -08001656 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1657 // Handle any color overrides
1658 if (init.fColorIgnored) {
1659 fGeoData[0].fColor = GrColor_ILLEGAL;
1660 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1661 fGeoData[0].fColor = init.fOverrideColor;
1662 }
1663
1664 // setup batch properties
1665 fBatch.fColorIgnored = init.fColorIgnored;
1666 fBatch.fColor = fGeoData[0].fColor;
1667 fBatch.fStroke = fGeoData[0].fStroke;
1668 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1669 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1670 }
1671
1672 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1673 // reset to device coordinates
1674 SkMatrix invert;
1675 if (!this->viewMatrix().invert(&invert)) {
1676 SkDebugf("Failed to invert\n");
1677 return;
1678 }
1679
1680 // Setup geometry processor
1681 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1682 this->stroke(),
1683 invert));
1684
1685 batchTarget->initDraw(gp, pipeline);
1686
1687 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1688 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1689 // everywhere we can remove this nastiness
1690 GrPipelineInfo init;
1691 init.fColorIgnored = fBatch.fColorIgnored;
1692 init.fOverrideColor = GrColor_ILLEGAL;
1693 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1694 init.fUsesLocalCoords = this->usesLocalCoords();
1695 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1696
1697 int instanceCount = fGeoData.count();
1698 int vertexCount = kVertsPerRRect * instanceCount;
1699 size_t vertexStride = gp->getVertexStride();
1700 SkASSERT(vertexStride == sizeof(CircleVertex));
1701
1702 const GrVertexBuffer* vertexBuffer;
1703 int firstVertex;
1704
1705 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1706 vertexCount,
1707 &vertexBuffer,
1708 &firstVertex);
1709
joshualitt4b31de82015-03-05 14:33:41 -08001710 if (!vertices) {
1711 SkDebugf("Could not allocate vertices\n");
1712 return;
1713 }
1714
joshualitt76e7fb62015-02-11 08:52:27 -08001715 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
1716
1717 for (int i = 0; i < instanceCount; i++) {
1718 Geometry& args = fGeoData[i];
1719
1720 SkScalar outerRadius = args.fOuterRadius;
1721
1722 const SkRect& bounds = args.fDevBounds;
1723
1724 SkScalar yCoords[4] = {
1725 bounds.fTop,
1726 bounds.fTop + outerRadius,
1727 bounds.fBottom - outerRadius,
1728 bounds.fBottom
1729 };
1730
1731 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1732 // The inner radius in the vertex data must be specified in normalized space.
1733 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1734 for (int i = 0; i < 4; ++i) {
1735 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1736 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1737 verts->fOuterRadius = outerRadius;
1738 verts->fInnerRadius = innerRadius;
1739 verts++;
1740
1741 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1742 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1743 verts->fOuterRadius = outerRadius;
1744 verts->fInnerRadius = innerRadius;
1745 verts++;
1746
1747 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1748 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1749 verts->fOuterRadius = outerRadius;
1750 verts->fInnerRadius = innerRadius;
1751 verts++;
1752
1753 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1754 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1755 verts->fOuterRadius = outerRadius;
1756 verts->fInnerRadius = innerRadius;
1757 verts++;
1758 }
1759 }
1760
1761 // drop out the middle quad if we're stroked
1762 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1763 SK_ARRAY_COUNT(gRRectIndices);
1764
1765
1766 GrDrawTarget::DrawInfo drawInfo;
1767 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1768 drawInfo.setStartVertex(0);
1769 drawInfo.setStartIndex(0);
1770 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1771 drawInfo.setIndicesPerInstance(indexCnt);
1772 drawInfo.adjustStartVertex(firstVertex);
1773 drawInfo.setVertexBuffer(vertexBuffer);
1774 drawInfo.setIndexBuffer(fIndexBuffer);
1775
1776 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1777
1778 while (instanceCount) {
1779 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1780 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1781 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1782
1783 batchTarget->draw(drawInfo);
1784
1785 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1786 instanceCount -= drawInfo.instanceCount();
1787 }
1788 }
1789
1790 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1791
1792private:
1793 RRectCircleRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
1794 : fIndexBuffer(indexBuffer) {
1795 this->initClassID<RRectCircleRendererBatch>();
1796 fGeoData.push_back(geometry);
1797 }
1798
1799 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1800 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1801
1802 // TODO use vertex color to avoid breaking batches
1803 if (this->color() != that->color()) {
1804 return false;
1805 }
1806
1807 if (this->stroke() != that->stroke()) {
1808 return false;
1809 }
1810
1811 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1812 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1813 return false;
1814 }
1815
1816 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1817 return true;
1818 }
1819
1820 GrColor color() const { return fBatch.fColor; }
1821 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1822 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1823 bool stroke() const { return fBatch.fStroke; }
1824
1825 struct BatchTracker {
1826 GrColor fColor;
1827 bool fStroke;
1828 bool fUsesLocalCoords;
1829 bool fColorIgnored;
1830 bool fCoverageIgnored;
1831 };
1832
joshualitt76e7fb62015-02-11 08:52:27 -08001833 BatchTracker fBatch;
1834 SkSTArray<1, Geometry, true> fGeoData;
1835 const GrIndexBuffer* fIndexBuffer;
1836};
1837
1838class RRectEllipseRendererBatch : public GrBatch {
1839public:
1840 struct Geometry {
1841 GrColor fColor;
1842 SkMatrix fViewMatrix;
1843 SkScalar fXRadius;
1844 SkScalar fYRadius;
1845 SkScalar fInnerXRadius;
1846 SkScalar fInnerYRadius;
1847 bool fStroke;
1848 SkRect fDevBounds;
1849 };
1850
1851 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1852 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry, indexBuffer));
1853 }
1854
1855 const char* name() const SK_OVERRIDE { return "RRectEllipseRendererBatch"; }
1856
1857 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1858 // When this is called on a batch, there is only one geometry bundle
1859 out->setKnownFourComponents(fGeoData[0].fColor);
1860 }
1861 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1862 out->setUnknownSingleComponent();
1863 }
1864
joshualitt76e7fb62015-02-11 08:52:27 -08001865 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1866 // Handle any color overrides
1867 if (init.fColorIgnored) {
1868 fGeoData[0].fColor = GrColor_ILLEGAL;
1869 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1870 fGeoData[0].fColor = init.fOverrideColor;
1871 }
1872
1873 // setup batch properties
1874 fBatch.fColorIgnored = init.fColorIgnored;
1875 fBatch.fColor = fGeoData[0].fColor;
1876 fBatch.fStroke = fGeoData[0].fStroke;
1877 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1878 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1879 }
1880
1881 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1882 // reset to device coordinates
1883 SkMatrix invert;
1884 if (!this->viewMatrix().invert(&invert)) {
1885 SkDebugf("Failed to invert\n");
1886 return;
1887 }
1888
1889 // Setup geometry processor
1890 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1891 this->stroke(),
1892 invert));
1893
1894 batchTarget->initDraw(gp, pipeline);
1895
1896 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1897 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1898 // everywhere we can remove this nastiness
1899 GrPipelineInfo init;
1900 init.fColorIgnored = fBatch.fColorIgnored;
1901 init.fOverrideColor = GrColor_ILLEGAL;
1902 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1903 init.fUsesLocalCoords = this->usesLocalCoords();
1904 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1905
1906 int instanceCount = fGeoData.count();
1907 int vertexCount = kVertsPerRRect * instanceCount;
1908 size_t vertexStride = gp->getVertexStride();
1909 SkASSERT(vertexStride == sizeof(EllipseVertex));
1910
1911 const GrVertexBuffer* vertexBuffer;
1912 int firstVertex;
1913
1914 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1915 vertexCount,
1916 &vertexBuffer,
1917 &firstVertex);
1918
joshualitt4b31de82015-03-05 14:33:41 -08001919 if (!vertices) {
1920 SkDebugf("Could not allocate vertices\n");
1921 return;
1922 }
1923
joshualitt76e7fb62015-02-11 08:52:27 -08001924 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1925
1926 for (int i = 0; i < instanceCount; i++) {
1927 Geometry& args = fGeoData[i];
1928
1929 // Compute the reciprocals of the radii here to save time in the shader
1930 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1931 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1932 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1933 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1934
1935 // Extend the radii out half a pixel to antialias.
1936 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1937 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1938
1939 const SkRect& bounds = args.fDevBounds;
1940
1941 SkScalar yCoords[4] = {
1942 bounds.fTop,
1943 bounds.fTop + yOuterRadius,
1944 bounds.fBottom - yOuterRadius,
1945 bounds.fBottom
1946 };
1947 SkScalar yOuterOffsets[4] = {
1948 yOuterRadius,
1949 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1950 SK_ScalarNearlyZero,
1951 yOuterRadius
1952 };
1953
1954 for (int i = 0; i < 4; ++i) {
1955 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1956 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1957 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1958 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1959 verts++;
1960
1961 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1962 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1963 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1964 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1965 verts++;
1966
1967 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1968 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1969 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1970 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1971 verts++;
1972
1973 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1974 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1975 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1976 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1977 verts++;
1978 }
1979 }
1980
1981 // drop out the middle quad if we're stroked
1982 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1983 SK_ARRAY_COUNT(gRRectIndices);
1984
1985 GrDrawTarget::DrawInfo drawInfo;
1986 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1987 drawInfo.setStartVertex(0);
1988 drawInfo.setStartIndex(0);
1989 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1990 drawInfo.setIndicesPerInstance(indexCnt);
1991 drawInfo.adjustStartVertex(firstVertex);
1992 drawInfo.setVertexBuffer(vertexBuffer);
1993 drawInfo.setIndexBuffer(fIndexBuffer);
1994
1995 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1996
1997 while (instanceCount) {
1998 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1999 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
2000 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
2001
2002 batchTarget->draw(drawInfo);
2003
2004 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
2005 instanceCount -= drawInfo.instanceCount();
2006 }
2007 }
2008
2009 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
2010
2011private:
2012 RRectEllipseRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
2013 : fIndexBuffer(indexBuffer) {
2014 this->initClassID<RRectEllipseRendererBatch>();
2015 fGeoData.push_back(geometry);
2016 }
2017
2018 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
2019 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2020
2021 // TODO use vertex color to avoid breaking batches
2022 if (this->color() != that->color()) {
2023 return false;
2024 }
2025
2026 if (this->stroke() != that->stroke()) {
2027 return false;
2028 }
2029
2030 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
2031 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
2032 return false;
2033 }
2034
2035 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
2036 return true;
2037 }
2038
2039 GrColor color() const { return fBatch.fColor; }
2040 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
2041 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
2042 bool stroke() const { return fBatch.fStroke; }
2043
2044 struct BatchTracker {
2045 GrColor fColor;
2046 bool fStroke;
2047 bool fUsesLocalCoords;
2048 bool fColorIgnored;
2049 bool fCoverageIgnored;
2050 };
2051
joshualitt76e7fb62015-02-11 08:52:27 -08002052 BatchTracker fBatch;
2053 SkSTArray<1, Geometry, true> fGeoData;
2054 const GrIndexBuffer* fIndexBuffer;
2055};
2056
joshualitt9853cce2014-11-17 14:22:48 -08002057bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08002058 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08002059 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08002060 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08002061 bool useAA,
2062 const SkRRect& rrect,
2063 const SkStrokeRec& stroke) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002064 if (rrect.isOval()) {
egdaniel8dd688b2015-01-22 10:16:09 -08002065 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
joshualitt8059eb92014-12-29 15:10:07 -08002066 stroke);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002067 }
2068
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002069 bool useCoverageAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08002070 !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002071
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00002072 // only anti-aliased rrects for now
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002073 if (!useCoverageAA) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00002074 return false;
2075 }
2076
joshualitt8059eb92014-12-29 15:10:07 -08002077 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002078 return false;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002079 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002080
2081 // do any matrix crunching before we reset the draw state for device coords
2082 const SkRect& rrectBounds = rrect.getBounds();
2083 SkRect bounds;
joshualitt8059eb92014-12-29 15:10:07 -08002084 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002085
2086 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002087 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2088 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2089 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2090 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002091
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002092 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002093
2094 // do (potentially) anisotropic mapping of stroke
2095 SkVector scaledStroke;
2096 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002097
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002098 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2099 SkStrokeRec::kHairline_Style == style;
2100 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2101
2102 if (hasStroke) {
2103 if (SkStrokeRec::kHairline_Style == style) {
2104 scaledStroke.set(1, 1);
2105 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002106 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2107 viewMatrix[SkMatrix::kMSkewY]));
2108 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2109 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002110 }
2111
2112 // if half of strokewidth is greater than radius, we don't handle that right now
2113 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
2114 return false;
2115 }
2116 }
2117
2118 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2119 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2120 // patch will have fractional coverage. This only matters when the interior is actually filled.
2121 // We could consider falling back to rect rendering here, since a tiny radius is
2122 // indistinguishable from a square corner.
2123 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002124 return false;
2125 }
2126
joshualitt5531d512014-12-17 15:50:11 -08002127 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002128 if (NULL == indexBuffer) {
tfarina38406c82014-10-31 07:11:12 -07002129 SkDebugf("Failed to create index buffer!\n");
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002130 return false;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002131 }
2132
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002133 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002134 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002135 SkScalar innerRadius = 0.0f;
2136 SkScalar outerRadius = xRadius;
2137 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002138 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002139 if (SkScalarNearlyZero(scaledStroke.fX)) {
2140 halfWidth = SK_ScalarHalf;
2141 } else {
2142 halfWidth = SkScalarHalf(scaledStroke.fX);
2143 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002144
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002145 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002146 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002147 }
2148 outerRadius += halfWidth;
2149 bounds.outset(halfWidth, halfWidth);
2150 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002151
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002152 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002153
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002154 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08002155 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2156 // Second, the outer radius is used to compute the verts of the bounding box that is
2157 // rendered and the outset ensures the box will cover all partially covered by the rrect
2158 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002159 outerRadius += SK_ScalarHalf;
2160 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002161
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002162 // Expand the rect so all the pixels will be captured.
2163 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002164
joshualitt76e7fb62015-02-11 08:52:27 -08002165 RRectCircleRendererBatch::Geometry geometry;
2166 geometry.fViewMatrix = viewMatrix;
2167 geometry.fColor = color;
2168 geometry.fInnerRadius = innerRadius;
2169 geometry.fOuterRadius = outerRadius;
2170 geometry.fStroke = isStrokeOnly;
2171 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002172
joshualitt76e7fb62015-02-11 08:52:27 -08002173 SkAutoTUnref<GrBatch> batch(RRectCircleRendererBatch::Create(geometry, indexBuffer));
2174 target->drawBatch(pipelineBuilder, batch, &bounds);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002175
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002176 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002177 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002178 SkScalar innerXRadius = 0.0f;
2179 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002180 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002181 if (SkScalarNearlyZero(scaledStroke.length())) {
2182 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2183 } else {
2184 scaledStroke.scale(SK_ScalarHalf);
2185 }
2186
2187 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00002188 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002189 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
2190 return false;
2191 }
2192
2193 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2194 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2195 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
2196 return false;
2197 }
2198
2199 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002200 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002201 innerXRadius = xRadius - scaledStroke.fX;
2202 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002203 }
2204
2205 xRadius += scaledStroke.fX;
2206 yRadius += scaledStroke.fY;
2207 bounds.outset(scaledStroke.fX, scaledStroke.fY);
2208 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002209
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002210 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002211
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002212 // Expand the rect so all the pixels will be captured.
2213 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2214
joshualitt76e7fb62015-02-11 08:52:27 -08002215 RRectEllipseRendererBatch::Geometry geometry;
2216 geometry.fViewMatrix = viewMatrix;
2217 geometry.fColor = color;
2218 geometry.fXRadius = xRadius;
2219 geometry.fYRadius = yRadius;
2220 geometry.fInnerXRadius = innerXRadius;
2221 geometry.fInnerYRadius = innerYRadius;
2222 geometry.fStroke = isStrokeOnly;
2223 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002224
joshualitt76e7fb62015-02-11 08:52:27 -08002225 SkAutoTUnref<GrBatch> batch(RRectEllipseRendererBatch::Create(geometry, indexBuffer));
2226 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002227 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002228 return true;
2229}