blob: 0a18b49b0ccad9836971d09610b4dcb70d4a8fef [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
joshualittee2af952014-12-30 09:04:15 -0800108 // setup uniform viewMatrix
109 this->addUniformViewMatrix(pb);
110
joshualittabb52a12015-01-13 15:02:10 -0800111 // Setup position
robertphillips46d36f02015-01-18 08:14:14 -0800112 SetupPosition(vsBuilder, gpArgs, ce.inPosition()->fName,
113 ce.viewMatrix(), this->uViewM());
joshualittabb52a12015-01-13 15:02:10 -0800114
115 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800116 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800117 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);;
joshualitt4973d9d2014-11-08 09:24:25 -0800118
joshualittc369e7c2014-10-22 10:56:26 -0700119 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700120 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800121 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800122 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800123 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
124 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700125 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000126 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000127
joshualitt2dd1ae02014-12-03 06:24:10 -0800128 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000129 }
130
robertphillips46d36f02015-01-18 08:14:14 -0800131 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800132 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800133 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700134 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800135 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800136 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800137 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800138 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
139 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800140 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000141 }
142
joshualitt9b989322014-12-15 14:16:27 -0800143 virtual void setData(const GrGLProgramDataManager& pdman,
144 const GrPrimitiveProcessor& gp,
145 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800146 this->setUniformViewMatrix(pdman, gp.viewMatrix());
147
joshualitt9b989322014-12-15 14:16:27 -0800148 const BatchTracker& local = bt.cast<BatchTracker>();
149 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
150 GrGLfloat c[4];
151 GrColorToRGBAFloat(local.fColor, c);
152 pdman.set4fv(fColorUniform, 1, c);
153 fColor = local.fColor;
154 }
155 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000156
157 private:
joshualitt9b989322014-12-15 14:16:27 -0800158 GrColor fColor;
159 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700160 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000161 };
162
joshualitteb2a6762014-12-04 11:35:33 -0800163 virtual void getGLProcessorKey(const GrBatchTracker& bt,
164 const GrGLCaps& caps,
165 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
166 GLProcessor::GenKey(*this, bt, caps, b);
167 }
168
joshualittabb52a12015-01-13 15:02:10 -0800169 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
170 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800171 return SkNEW_ARGS(GLProcessor, (*this, bt));
172 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173
joshualitt4d8da812015-01-28 12:53:54 -0800174 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800175 BatchTracker* local = bt->cast<BatchTracker>();
176 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800177 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800178 }
179
joshualitt290c09b2014-12-19 13:45:20 -0800180 bool onCanMakeEqual(const GrBatchTracker& m,
181 const GrGeometryProcessor& that,
182 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800183 const BatchTracker& mine = m.cast<BatchTracker>();
184 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800185 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
186 that, theirs.fUsesLocalCoords) &&
187 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800188 theirs.fInputColorType, theirs.fColor);
189 }
190
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000191private:
joshualittd27f73e2014-12-29 07:43:36 -0800192 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800193 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800194 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800195 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
196 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800197 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000198 fStroke = stroke;
199 }
200
mtklein72c9faa2015-01-09 10:06:39 -0800201 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700202 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203 return cee.fStroke == fStroke;
204 }
205
mtklein72c9faa2015-01-09 10:06:39 -0800206 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800207 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700208 }
209
joshualitt9b989322014-12-15 14:16:27 -0800210 struct BatchTracker {
211 GrGPInput fInputColorType;
212 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800213 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800214 };
215
joshualitt71c92602015-01-14 08:12:47 -0800216 const Attribute* fInPosition;
217 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000218 bool fStroke;
219
joshualittb0a8a372014-09-23 09:50:21 -0700220 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221
joshualitt249af152014-09-15 11:41:13 -0700222 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223};
224
joshualittb0a8a372014-09-23 09:50:21 -0700225GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226
joshualittb0a8a372014-09-23 09:50:21 -0700227GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
228 GrContext* context,
229 const GrDrawTargetCaps&,
230 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800231 return CircleEdgeEffect::Create(GrRandomColor(random),
232 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800233 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234}
235
236///////////////////////////////////////////////////////////////////////////////
237
238/**
239 * 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 +0000240 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
241 * in both x and y directions.
242 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000243 * 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 +0000244 */
245
joshualitt249af152014-09-15 11:41:13 -0700246class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247public:
joshualittd27f73e2014-12-29 07:43:36 -0800248 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
249 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 }
251
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000252 virtual ~EllipseEdgeEffect() {}
253
mtklein72c9faa2015-01-09 10:06:39 -0800254 const char* name() const SK_OVERRIDE { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800255
joshualitt71c92602015-01-14 08:12:47 -0800256 const Attribute* inPosition() const { return fInPosition; }
257 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
258 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700259
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000260 inline bool isStroked() const { return fStroke; }
261
joshualittb0a8a372014-09-23 09:50:21 -0700262 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263 public:
joshualitteb2a6762014-12-04 11:35:33 -0800264 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800265 const GrBatchTracker&)
266 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000267
robertphillips46d36f02015-01-18 08:14:14 -0800268 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
joshualitt2dd1ae02014-12-03 06:24:10 -0800269 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800270 GrGLGPBuilder* pb = args.fPB;
271 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800272 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
joshualittabb52a12015-01-13 15:02:10 -0800274 // emit attributes
275 vsBuilder->emitAttributes(ee);
276
joshualitt74077b92014-10-24 11:26:03 -0700277 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
278 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700279 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800280 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000281
joshualitt74077b92014-10-24 11:26:03 -0700282 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
283 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
284 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800285 ee.inEllipseRadii()->fName);
286
joshualitt9b989322014-12-15 14:16:27 -0800287 // Setup pass through color
288 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
289 &fColorUniform);
290
joshualittee2af952014-12-30 09:04:15 -0800291 // setup uniform viewMatrix
292 this->addUniformViewMatrix(pb);
293
joshualittabb52a12015-01-13 15:02:10 -0800294 // Setup position
robertphillips46d36f02015-01-18 08:14:14 -0800295 SetupPosition(vsBuilder, gpArgs, ee.inPosition()->fName,
296 ee.viewMatrix(), this->uViewM());
joshualittabb52a12015-01-13 15:02:10 -0800297
298 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800299 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800300 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800301
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000302 // for outer curve
joshualittc369e7c2014-10-22 10:56:26 -0700303 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700304 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
305 ellipseRadii.fsIn());
306 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
307 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
308 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
309
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000310 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700311 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
312 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
313 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000314
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000315 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800316 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700317 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
318 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
319 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
320 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
321 ellipseRadii.fsIn());
322 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
323 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000324 }
325
joshualitt2dd1ae02014-12-03 06:24:10 -0800326 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000327 }
328
robertphillips46d36f02015-01-18 08:14:14 -0800329 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800330 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800331 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700332 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800333 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800334 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800335 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800336 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
337 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800338 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000339 }
340
joshualitt9b989322014-12-15 14:16:27 -0800341 virtual void setData(const GrGLProgramDataManager& pdman,
342 const GrPrimitiveProcessor& gp,
343 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800344 this->setUniformViewMatrix(pdman, gp.viewMatrix());
345
joshualitt9b989322014-12-15 14:16:27 -0800346 const BatchTracker& local = bt.cast<BatchTracker>();
347 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
348 GrGLfloat c[4];
349 GrColorToRGBAFloat(local.fColor, c);
350 pdman.set4fv(fColorUniform, 1, c);
351 fColor = local.fColor;
352 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000353 }
354
355 private:
joshualitt9b989322014-12-15 14:16:27 -0800356 GrColor fColor;
357 UniformHandle fColorUniform;
358
joshualitt249af152014-09-15 11:41:13 -0700359 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360 };
361
joshualitteb2a6762014-12-04 11:35:33 -0800362 virtual void getGLProcessorKey(const GrBatchTracker& bt,
363 const GrGLCaps& caps,
364 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
365 GLProcessor::GenKey(*this, bt, caps, b);
366 }
367
joshualittabb52a12015-01-13 15:02:10 -0800368 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
369 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800370 return SkNEW_ARGS(GLProcessor, (*this, bt));
371 }
372
joshualitt4d8da812015-01-28 12:53:54 -0800373 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800374 BatchTracker* local = bt->cast<BatchTracker>();
375 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800376 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800377 }
378
joshualitt290c09b2014-12-19 13:45:20 -0800379 bool onCanMakeEqual(const GrBatchTracker& m,
380 const GrGeometryProcessor& that,
381 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800382 const BatchTracker& mine = m.cast<BatchTracker>();
383 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800384 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
385 that, theirs.fUsesLocalCoords) &&
386 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800387 theirs.fInputColorType, theirs.fColor);
388 }
389
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000390private:
joshualittd27f73e2014-12-29 07:43:36 -0800391 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800392 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800393 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800394 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
395 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800396 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800397 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800398 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000399 fStroke = stroke;
400 }
401
mtklein72c9faa2015-01-09 10:06:39 -0800402 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700403 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000404 return eee.fStroke == fStroke;
405 }
406
mtklein72c9faa2015-01-09 10:06:39 -0800407 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800408 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700409 }
410
joshualitt9b989322014-12-15 14:16:27 -0800411 struct BatchTracker {
412 GrGPInput fInputColorType;
413 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800414 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800415 };
416
joshualitt71c92602015-01-14 08:12:47 -0800417 const Attribute* fInPosition;
418 const Attribute* fInEllipseOffset;
419 const Attribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000420 bool fStroke;
421
joshualittb0a8a372014-09-23 09:50:21 -0700422 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000423
joshualitt249af152014-09-15 11:41:13 -0700424 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000425};
426
joshualittb0a8a372014-09-23 09:50:21 -0700427GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000428
joshualittb0a8a372014-09-23 09:50:21 -0700429GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
430 GrContext* context,
431 const GrDrawTargetCaps&,
432 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800433 return EllipseEdgeEffect::Create(GrRandomColor(random),
434 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800435 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000436}
437
438///////////////////////////////////////////////////////////////////////////////
439
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000440/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000441 * 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 +0000442 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
443 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
444 * using differentials.
445 *
446 * The result is device-independent and can be used with any affine matrix.
447 */
448
joshualitt249af152014-09-15 11:41:13 -0700449class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000450public:
451 enum Mode { kStroke = 0, kHairline, kFill };
452
joshualitt8059eb92014-12-29 15:10:07 -0800453 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
454 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000455 }
456
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000457 virtual ~DIEllipseEdgeEffect() {}
458
mtklein72c9faa2015-01-09 10:06:39 -0800459 const char* name() const SK_OVERRIDE { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000460
joshualitt71c92602015-01-14 08:12:47 -0800461 const Attribute* inPosition() const { return fInPosition; }
462 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
463 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700464
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000465 inline Mode getMode() const { return fMode; }
466
joshualittb0a8a372014-09-23 09:50:21 -0700467 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000468 public:
joshualitteb2a6762014-12-04 11:35:33 -0800469 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800470 const GrBatchTracker&)
471 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472
robertphillips46d36f02015-01-18 08:14:14 -0800473 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
joshualitt2dd1ae02014-12-03 06:24:10 -0800474 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800475 GrGLGPBuilder* pb = args.fPB;
476 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800477 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000478
joshualittabb52a12015-01-13 15:02:10 -0800479 // emit attributes
480 vsBuilder->emitAttributes(ee);
481
joshualitt74077b92014-10-24 11:26:03 -0700482 GrGLVertToFrag offsets0(kVec2f_GrSLType);
483 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700484 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800485 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700486
487 GrGLVertToFrag offsets1(kVec2f_GrSLType);
488 args.fPB->addVarying("EllipseOffsets1", &offsets1);
489 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800490 ee.inEllipseOffsets1()->fName);
491
joshualitt9b989322014-12-15 14:16:27 -0800492 // Setup pass through color
493 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
494 &fColorUniform);
495
joshualittee2af952014-12-30 09:04:15 -0800496 // setup uniform viewMatrix
497 this->addUniformViewMatrix(pb);
498
joshualittabb52a12015-01-13 15:02:10 -0800499 // Setup position
robertphillips46d36f02015-01-18 08:14:14 -0800500 SetupPosition(vsBuilder, gpArgs, ee.inPosition()->fName,
501 ee.viewMatrix(), this->uViewM());
joshualittabb52a12015-01-13 15:02:10 -0800502
503 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800504 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800505 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800506
joshualittc369e7c2014-10-22 10:56:26 -0700507 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700508 SkAssertResult(fsBuilder->enableFeature(
509 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000510 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700511 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
512 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
513 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
514 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
515 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
516 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
517 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518
joshualitt74077b92014-10-24 11:26:03 -0700519 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000520 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700521 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
522 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800523 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000524 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700525 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
526 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000527 } else {
joshualitt74077b92014-10-24 11:26:03 -0700528 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000529 }
530
531 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800532 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700533 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
534 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
535 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
536 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
537 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
538 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
539 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
540 offsets1.fsIn());
541 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
542 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000543 }
544
joshualitt2dd1ae02014-12-03 06:24:10 -0800545 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546 }
547
robertphillips46d36f02015-01-18 08:14:14 -0800548 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800549 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800550 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700551 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800552 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800553 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800554 uint16_t key = ellipseEffect.getMode();
robertphillips46d36f02015-01-18 08:14:14 -0800555 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
556 key |= ComputePosKey(gp.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800557 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000558 }
559
joshualitt9b989322014-12-15 14:16:27 -0800560 virtual void setData(const GrGLProgramDataManager& pdman,
561 const GrPrimitiveProcessor& gp,
562 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800563 this->setUniformViewMatrix(pdman, gp.viewMatrix());
564
joshualitt9b989322014-12-15 14:16:27 -0800565 const BatchTracker& local = bt.cast<BatchTracker>();
566 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
567 GrGLfloat c[4];
568 GrColorToRGBAFloat(local.fColor, c);
569 pdman.set4fv(fColorUniform, 1, c);
570 fColor = local.fColor;
571 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000572 }
573
574 private:
joshualitt9b989322014-12-15 14:16:27 -0800575 GrColor fColor;
576 UniformHandle fColorUniform;
577
joshualitt249af152014-09-15 11:41:13 -0700578 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000579 };
580
joshualitteb2a6762014-12-04 11:35:33 -0800581 virtual void getGLProcessorKey(const GrBatchTracker& bt,
582 const GrGLCaps& caps,
583 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
584 GLProcessor::GenKey(*this, bt, caps, b);
585 }
586
joshualittabb52a12015-01-13 15:02:10 -0800587 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
588 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800589 return SkNEW_ARGS(GLProcessor, (*this, bt));
590 }
591
joshualitt4d8da812015-01-28 12:53:54 -0800592 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800593 BatchTracker* local = bt->cast<BatchTracker>();
594 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800595 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800596 }
597
joshualitt290c09b2014-12-19 13:45:20 -0800598 bool onCanMakeEqual(const GrBatchTracker& m,
599 const GrGeometryProcessor& that,
600 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800601 const BatchTracker& mine = m.cast<BatchTracker>();
602 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800603 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
604 that, theirs.fUsesLocalCoords) &&
605 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800606 theirs.fInputColorType, theirs.fColor);
607 }
608
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000609private:
joshualitt8059eb92014-12-29 15:10:07 -0800610 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
611 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800612 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800613 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
614 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800615 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800616 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800617 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000618 fMode = mode;
619 }
620
mtklein72c9faa2015-01-09 10:06:39 -0800621 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700622 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000623 return eee.fMode == fMode;
624 }
625
mtklein72c9faa2015-01-09 10:06:39 -0800626 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800627 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700628 }
629
joshualitt9b989322014-12-15 14:16:27 -0800630 struct BatchTracker {
631 GrGPInput fInputColorType;
632 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800633 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800634 };
635
joshualitt71c92602015-01-14 08:12:47 -0800636 const Attribute* fInPosition;
637 const Attribute* fInEllipseOffsets0;
638 const Attribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000639 Mode fMode;
640
joshualittb0a8a372014-09-23 09:50:21 -0700641 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000642
joshualitt249af152014-09-15 11:41:13 -0700643 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000644};
645
joshualittb0a8a372014-09-23 09:50:21 -0700646GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000647
joshualittb0a8a372014-09-23 09:50:21 -0700648GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
649 GrContext* context,
650 const GrDrawTargetCaps&,
651 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800652 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
653 GrProcessorUnitTest::TestMatrix(random),
654 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000655}
656
657///////////////////////////////////////////////////////////////////////////////
658
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000659void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000660 SkSafeSetNull(fRRectIndexBuffer);
joshualitt58a65442014-10-22 20:53:24 -0700661 SkSafeSetNull(fStrokeRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000662}
663
joshualitt9853cce2014-11-17 14:22:48 -0800664bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800665 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800666 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800667 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800668 bool useAA,
669 const SkRect& oval,
670 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000671{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000672 bool useCoverageAA = useAA &&
egdaniel8dd688b2015-01-22 10:16:09 -0800673 !pipelineBuilder->getRenderTarget()->isMultisampled() &&
674 pipelineBuilder->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000675
676 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000677 return false;
678 }
679
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000680 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800681 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800682 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000683 // if we have shader derivative support, render as device-independent
684 } else if (target->caps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800685 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800686 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000687 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800688 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800689 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
690 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000691 } else {
692 return false;
693 }
694
695 return true;
696}
697
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000698///////////////////////////////////////////////////////////////////////////////
699
joshualitt76e7fb62015-02-11 08:52:27 -0800700class CircleBatch : public GrBatch {
701public:
702 struct Geometry {
703 GrColor fColor;
704 SkMatrix fViewMatrix;
705 SkScalar fInnerRadius;
706 SkScalar fOuterRadius;
707 bool fStroke;
708 SkRect fDevBounds;
709 };
710
711 static GrBatch* Create(const Geometry& geometry) {
712 return SkNEW_ARGS(CircleBatch, (geometry));
713 }
714
715 const char* name() const SK_OVERRIDE { return "CircleBatch"; }
716
717 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
718 // When this is called on a batch, there is only one geometry bundle
719 out->setKnownFourComponents(fGeoData[0].fColor);
720 }
721
722 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
723 out->setUnknownSingleComponent();
724 }
725
726 void initBatchOpt(const GrBatchOpt& batchOpt) {
727 fBatchOpt = batchOpt;
728 }
729
730 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
731 // Handle any color overrides
732 if (init.fColorIgnored) {
733 fGeoData[0].fColor = GrColor_ILLEGAL;
734 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
735 fGeoData[0].fColor = init.fOverrideColor;
736 }
737
738 // setup batch properties
739 fBatch.fColorIgnored = init.fColorIgnored;
740 fBatch.fColor = fGeoData[0].fColor;
741 fBatch.fStroke = fGeoData[0].fStroke;
742 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
743 fBatch.fCoverageIgnored = init.fCoverageIgnored;
744 }
745
746 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
747 SkMatrix invert;
748 if (!this->viewMatrix().invert(&invert)) {
749 return;
750 }
751
752 // Setup geometry processor
753 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
754 this->stroke(),
755 invert));
756
757 batchTarget->initDraw(gp, pipeline);
758
759 // TODO this is hacky, but the only way we have to initialize the GP is to use the
760 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
761 // everywhere we can remove this nastiness
762 GrPipelineInfo init;
763 init.fColorIgnored = fBatch.fColorIgnored;
764 init.fOverrideColor = GrColor_ILLEGAL;
765 init.fCoverageIgnored = fBatch.fCoverageIgnored;
766 init.fUsesLocalCoords = this->usesLocalCoords();
767 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
768
769 int instanceCount = fGeoData.count();
770 int vertexCount = kVertsPerCircle * instanceCount;
771 size_t vertexStride = gp->getVertexStride();
772 SkASSERT(vertexStride == sizeof(CircleVertex));
773
774 const GrVertexBuffer* vertexBuffer;
775 int firstVertex;
776
777 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
778 vertexCount,
779 &vertexBuffer,
780 &firstVertex);
781
782 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
783
784 for (int i = 0; i < instanceCount; i++) {
785 Geometry& args = fGeoData[i];
786
787 SkScalar innerRadius = args.fInnerRadius;
788 SkScalar outerRadius = args.fOuterRadius;
789
790 const SkRect& bounds = args.fDevBounds;
791
792 // The inner radius in the vertex data must be specified in normalized space.
793 innerRadius = innerRadius / outerRadius;
794 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
795 verts[0].fOffset = SkPoint::Make(-1, -1);
796 verts[0].fOuterRadius = outerRadius;
797 verts[0].fInnerRadius = innerRadius;
798
799 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
800 verts[1].fOffset = SkPoint::Make(-1, 1);
801 verts[1].fOuterRadius = outerRadius;
802 verts[1].fInnerRadius = innerRadius;
803
804 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
805 verts[2].fOffset = SkPoint::Make(1, 1);
806 verts[2].fOuterRadius = outerRadius;
807 verts[2].fInnerRadius = innerRadius;
808
809 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
810 verts[3].fOffset = SkPoint::Make(1, -1);
811 verts[3].fOuterRadius = outerRadius;
812 verts[3].fInnerRadius = innerRadius;
813
814 verts += kVertsPerCircle;
815 }
816
817 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
818
819 GrDrawTarget::DrawInfo drawInfo;
820 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
821 drawInfo.setStartVertex(0);
822 drawInfo.setStartIndex(0);
823 drawInfo.setVerticesPerInstance(kVertsPerCircle);
824 drawInfo.setIndicesPerInstance(kIndicesPerCircle);
825 drawInfo.adjustStartVertex(firstVertex);
826 drawInfo.setVertexBuffer(vertexBuffer);
827 drawInfo.setIndexBuffer(quadIndexBuffer);
828
829 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
830
831 while (instanceCount) {
832 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
833 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
834 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
835
836 batchTarget->draw(drawInfo);
837
838 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
839 instanceCount -= drawInfo.instanceCount();
840 }
841 }
842
843 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
844
845private:
846 CircleBatch(const Geometry& geometry) {
847 this->initClassID<CircleBatch>();
848 fGeoData.push_back(geometry);
849 }
850
851 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
852 CircleBatch* that = t->cast<CircleBatch>();
853
854 // TODO use vertex color to avoid breaking batches
855 if (this->color() != that->color()) {
856 return false;
857 }
858
859 if (this->stroke() != that->stroke()) {
860 return false;
861 }
862
863 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
864 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
865 return false;
866 }
867
868 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
869 return true;
870 }
871
872 GrColor color() const { return fBatch.fColor; }
873 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
874 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
875 bool stroke() const { return fBatch.fStroke; }
876
877 struct BatchTracker {
878 GrColor fColor;
879 bool fStroke;
880 bool fUsesLocalCoords;
881 bool fColorIgnored;
882 bool fCoverageIgnored;
883 };
884
885 static const int kVertsPerCircle = 4;
886 static const int kIndicesPerCircle = 6;
887
888 GrBatchOpt fBatchOpt;
889 BatchTracker fBatch;
890 SkSTArray<1, Geometry, true> fGeoData;
891};
892
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000893void GrOvalRenderer::drawCircle(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800894 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800895 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800896 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000897 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000898 const SkRect& circle,
joshualitt8059eb92014-12-29 15:10:07 -0800899 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000900 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800901 viewMatrix.mapPoints(&center, 1);
902 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
903 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000904
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000905 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000906 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
907 SkStrokeRec::kHairline_Style == style;
908 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000909
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000910 SkScalar innerRadius = 0.0f;
911 SkScalar outerRadius = radius;
912 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000913 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000914 if (SkScalarNearlyZero(strokeWidth)) {
915 halfWidth = SK_ScalarHalf;
916 } else {
917 halfWidth = SkScalarHalf(strokeWidth);
918 }
919
920 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000921 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000922 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000923 }
924 }
925
bsalomonce1c8862014-12-15 07:11:22 -0800926 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
927 // computation because the computed alpha is zero, rather than 50%, at the radius.
928 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
929 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000930 outerRadius += SK_ScalarHalf;
931 innerRadius -= SK_ScalarHalf;
932
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000933 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000934 center.fX - outerRadius,
935 center.fY - outerRadius,
936 center.fX + outerRadius,
937 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000938 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000939
joshualitt76e7fb62015-02-11 08:52:27 -0800940 CircleBatch::Geometry geometry;
941 geometry.fViewMatrix = viewMatrix;
942 geometry.fColor = color;
943 geometry.fInnerRadius = innerRadius;
944 geometry.fOuterRadius = outerRadius;
945 geometry.fStroke = isStrokeOnly && innerRadius > 0;
946 geometry.fDevBounds = bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000947
joshualitt76e7fb62015-02-11 08:52:27 -0800948 SkAutoTUnref<GrBatch> batch(CircleBatch::Create(geometry));
949 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000950}
951
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000952///////////////////////////////////////////////////////////////////////////////
953
joshualitt76e7fb62015-02-11 08:52:27 -0800954class EllipseBatch : public GrBatch {
955public:
956 struct Geometry {
957 GrColor fColor;
958 SkMatrix fViewMatrix;
959 SkScalar fXRadius;
960 SkScalar fYRadius;
961 SkScalar fInnerXRadius;
962 SkScalar fInnerYRadius;
963 bool fStroke;
964 SkRect fDevBounds;
965 };
966
967 static GrBatch* Create(const Geometry& geometry) {
968 return SkNEW_ARGS(EllipseBatch, (geometry));
969 }
970
971 const char* name() const SK_OVERRIDE { return "EllipseBatch"; }
972
973 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
974 // When this is called on a batch, there is only one geometry bundle
975 out->setKnownFourComponents(fGeoData[0].fColor);
976 }
977 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
978 out->setUnknownSingleComponent();
979 }
980
981 void initBatchOpt(const GrBatchOpt& batchOpt) {
982 fBatchOpt = batchOpt;
983 }
984
985 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
986 // Handle any color overrides
987 if (init.fColorIgnored) {
988 fGeoData[0].fColor = GrColor_ILLEGAL;
989 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
990 fGeoData[0].fColor = init.fOverrideColor;
991 }
992
993 // setup batch properties
994 fBatch.fColorIgnored = init.fColorIgnored;
995 fBatch.fColor = fGeoData[0].fColor;
996 fBatch.fStroke = fGeoData[0].fStroke;
997 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
998 fBatch.fCoverageIgnored = init.fCoverageIgnored;
999 }
1000
1001 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1002 SkMatrix invert;
1003 if (!this->viewMatrix().invert(&invert)) {
1004 return;
1005 }
1006
1007 // Setup geometry processor
1008 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1009 this->stroke(),
1010 invert));
joshualitt76e7fb62015-02-11 08:52:27 -08001011
1012 batchTarget->initDraw(gp, pipeline);
1013
1014 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1015 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1016 // everywhere we can remove this nastiness
1017 GrPipelineInfo init;
1018 init.fColorIgnored = fBatch.fColorIgnored;
1019 init.fOverrideColor = GrColor_ILLEGAL;
1020 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1021 init.fUsesLocalCoords = this->usesLocalCoords();
1022 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1023
1024 int instanceCount = fGeoData.count();
1025 int vertexCount = kVertsPerEllipse * instanceCount;
1026 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001027 SkASSERT(vertexStride == sizeof(EllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001028
1029 const GrVertexBuffer* vertexBuffer;
1030 int firstVertex;
1031
1032 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1033 vertexCount,
1034 &vertexBuffer,
1035 &firstVertex);
1036
1037 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1038
1039 for (int i = 0; i < instanceCount; i++) {
1040 Geometry& args = fGeoData[i];
1041
1042 SkScalar xRadius = args.fXRadius;
1043 SkScalar yRadius = args.fYRadius;
1044
1045 // Compute the reciprocals of the radii here to save time in the shader
1046 SkScalar xRadRecip = SkScalarInvert(xRadius);
1047 SkScalar yRadRecip = SkScalarInvert(yRadius);
1048 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1049 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1050
1051 const SkRect& bounds = args.fDevBounds;
1052
1053 // The inner radius in the vertex data must be specified in normalized space.
1054 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1055 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
1056 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1057 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1058
1059 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1060 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1061 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1062 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1063
1064 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1065 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1066 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1067 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1068
1069 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1070 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1071 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1072 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1073
1074 verts += kVertsPerEllipse;
1075 }
1076
1077 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1078
1079 GrDrawTarget::DrawInfo drawInfo;
1080 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1081 drawInfo.setStartVertex(0);
1082 drawInfo.setStartIndex(0);
1083 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1084 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1085 drawInfo.adjustStartVertex(firstVertex);
1086 drawInfo.setVertexBuffer(vertexBuffer);
1087 drawInfo.setIndexBuffer(quadIndexBuffer);
1088
1089 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1090
1091 while (instanceCount) {
1092 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1093 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1094 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1095
1096 batchTarget->draw(drawInfo);
1097
1098 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1099 instanceCount -= drawInfo.instanceCount();
1100 }
1101 }
1102
1103 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1104
1105private:
1106 EllipseBatch(const Geometry& geometry) {
1107 this->initClassID<EllipseBatch>();
1108 fGeoData.push_back(geometry);
1109 }
1110
1111 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1112 EllipseBatch* that = t->cast<EllipseBatch>();
1113
1114 // TODO use vertex color to avoid breaking batches
1115 if (this->color() != that->color()) {
1116 return false;
1117 }
1118
1119 if (this->stroke() != that->stroke()) {
1120 return false;
1121 }
1122
1123 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1124 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1125 return false;
1126 }
1127
1128 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1129 return true;
1130 }
1131
1132 GrColor color() const { return fBatch.fColor; }
1133 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1134 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1135 bool stroke() const { return fBatch.fStroke; }
1136
1137 struct BatchTracker {
1138 GrColor fColor;
1139 bool fStroke;
1140 bool fUsesLocalCoords;
1141 bool fColorIgnored;
1142 bool fCoverageIgnored;
1143 };
1144
1145 static const int kVertsPerEllipse = 4;
1146 static const int kIndicesPerEllipse = 6;
1147
1148 GrBatchOpt fBatchOpt;
1149 BatchTracker fBatch;
1150 SkSTArray<1, Geometry, true> fGeoData;
1151};
1152
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001153bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001154 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001155 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001156 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001157 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +00001158 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -08001159 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001160#ifdef SK_DEBUG
1161 {
1162 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001163 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001164 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001165 }
1166#endif
1167
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001168 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001169 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001170 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001171 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1172 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001173 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1174 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1175 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1176 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001177
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001178 // do (potentially) anisotropic mapping of stroke
1179 SkVector scaledStroke;
1180 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001181 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1182 viewMatrix[SkMatrix::kMSkewY]));
1183 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1184 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001185
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001186 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001187 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1188 SkStrokeRec::kHairline_Style == style;
1189 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001190
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001191 SkScalar innerXRadius = 0;
1192 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001193 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001194 if (SkScalarNearlyZero(scaledStroke.length())) {
1195 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1196 } else {
1197 scaledStroke.scale(SK_ScalarHalf);
1198 }
1199
1200 // we only handle thick strokes for near-circular ellipses
1201 if (scaledStroke.length() > SK_ScalarHalf &&
1202 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1203 return false;
1204 }
1205
1206 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1207 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1208 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1209 return false;
1210 }
1211
1212 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001213 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001214 innerXRadius = xRadius - scaledStroke.fX;
1215 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001216 }
1217
1218 xRadius += scaledStroke.fX;
1219 yRadius += scaledStroke.fY;
1220 }
1221
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001222 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001223 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001224 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001225 xRadius += SK_ScalarHalf;
1226 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001227
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001228 SkRect bounds = SkRect::MakeLTRB(
1229 center.fX - xRadius,
1230 center.fY - yRadius,
1231 center.fX + xRadius,
1232 center.fY + yRadius
1233 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001234
joshualitt76e7fb62015-02-11 08:52:27 -08001235 EllipseBatch::Geometry geometry;
1236 geometry.fViewMatrix = viewMatrix;
1237 geometry.fColor = color;
1238 geometry.fXRadius = xRadius;
1239 geometry.fYRadius = yRadius;
1240 geometry.fInnerXRadius = innerXRadius;
1241 geometry.fInnerYRadius = innerYRadius;
1242 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1243 geometry.fDevBounds = bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001244
joshualitt76e7fb62015-02-11 08:52:27 -08001245 SkAutoTUnref<GrBatch> batch(EllipseBatch::Create(geometry));
1246 target->drawBatch(pipelineBuilder, batch, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001247
1248 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001249}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001250
joshualitt76e7fb62015-02-11 08:52:27 -08001251/////////////////////////////////////////////////////////////////////////////////////////////////
1252
1253class DIEllipseBatch : public GrBatch {
1254public:
1255 struct Geometry {
1256 GrColor fColor;
1257 SkMatrix fViewMatrix;
1258 SkScalar fXRadius;
1259 SkScalar fYRadius;
1260 SkScalar fInnerXRadius;
1261 SkScalar fInnerYRadius;
1262 SkScalar fGeoDx;
1263 SkScalar fGeoDy;
1264 DIEllipseEdgeEffect::Mode fMode;
1265 SkRect fDevBounds;
1266 };
1267
1268 static GrBatch* Create(const Geometry& geometry) {
1269 return SkNEW_ARGS(DIEllipseBatch, (geometry));
1270 }
1271
1272 const char* name() const SK_OVERRIDE { return "DIEllipseBatch"; }
1273
1274 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1275 // When this is called on a batch, there is only one geometry bundle
1276 out->setKnownFourComponents(fGeoData[0].fColor);
1277 }
1278 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1279 out->setUnknownSingleComponent();
1280 }
1281
1282 void initBatchOpt(const GrBatchOpt& batchOpt) {
1283 fBatchOpt = batchOpt;
1284 }
1285
1286 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1287 // Handle any color overrides
1288 if (init.fColorIgnored) {
1289 fGeoData[0].fColor = GrColor_ILLEGAL;
1290 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1291 fGeoData[0].fColor = init.fOverrideColor;
1292 }
1293
1294 // setup batch properties
1295 fBatch.fColorIgnored = init.fColorIgnored;
1296 fBatch.fColor = fGeoData[0].fColor;
1297 fBatch.fMode = fGeoData[0].fMode;
1298 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1299 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1300 }
1301
1302 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1303 // Setup geometry processor
1304 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1305 this->viewMatrix(),
1306 this->mode()));
1307
joshualitt76e7fb62015-02-11 08:52:27 -08001308 batchTarget->initDraw(gp, pipeline);
1309
1310 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1311 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1312 // everywhere we can remove this nastiness
1313 GrPipelineInfo init;
1314 init.fColorIgnored = fBatch.fColorIgnored;
1315 init.fOverrideColor = GrColor_ILLEGAL;
1316 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1317 init.fUsesLocalCoords = this->usesLocalCoords();
1318 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1319
1320 int instanceCount = fGeoData.count();
1321 int vertexCount = kVertsPerEllipse * instanceCount;
1322 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001323 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001324
1325 const GrVertexBuffer* vertexBuffer;
1326 int firstVertex;
1327
1328 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1329 vertexCount,
1330 &vertexBuffer,
1331 &firstVertex);
1332
1333 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
1334
1335 for (int i = 0; i < instanceCount; i++) {
1336 Geometry& args = fGeoData[i];
1337
1338 SkScalar xRadius = args.fXRadius;
1339 SkScalar yRadius = args.fYRadius;
1340
1341 const SkRect& bounds = args.fDevBounds;
1342
1343 // This adjusts the "radius" to include the half-pixel border
1344 SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
1345 SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
1346
1347 SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
1348 SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
1349
1350 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1351 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1352 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1353
1354 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1355 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1356 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1357
1358 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1359 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1360 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1361
1362 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1363 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1364 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1365
1366 verts += kVertsPerEllipse;
1367 }
1368
1369 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1370
1371 GrDrawTarget::DrawInfo drawInfo;
1372 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1373 drawInfo.setStartVertex(0);
1374 drawInfo.setStartIndex(0);
1375 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1376 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1377 drawInfo.adjustStartVertex(firstVertex);
1378 drawInfo.setVertexBuffer(vertexBuffer);
1379 drawInfo.setIndexBuffer(quadIndexBuffer);
1380
1381 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1382
1383 while (instanceCount) {
1384 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1385 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1386 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1387
1388 batchTarget->draw(drawInfo);
1389
1390 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1391 instanceCount -= drawInfo.instanceCount();
1392 }
1393 }
1394
1395 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1396
1397private:
1398 DIEllipseBatch(const Geometry& geometry) {
1399 this->initClassID<DIEllipseBatch>();
1400 fGeoData.push_back(geometry);
1401 }
1402
1403 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1404 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1405
1406 // TODO use vertex color to avoid breaking batches
1407 if (this->color() != that->color()) {
1408 return false;
1409 }
1410
1411 if (this->mode() != that->mode()) {
1412 return false;
1413 }
1414
1415 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1416 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1417 return false;
1418 }
1419
1420 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1421 return true;
1422 }
1423
1424 GrColor color() const { return fBatch.fColor; }
1425 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1426 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1427 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1428
1429 struct BatchTracker {
1430 GrColor fColor;
1431 DIEllipseEdgeEffect::Mode fMode;
1432 bool fUsesLocalCoords;
1433 bool fColorIgnored;
1434 bool fCoverageIgnored;
1435 };
1436
1437 static const int kVertsPerEllipse = 4;
1438 static const int kIndicesPerEllipse = 6;
1439
1440 GrBatchOpt fBatchOpt;
1441 BatchTracker fBatch;
1442 SkSTArray<1, Geometry, true> fGeoData;
1443};
1444
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001445bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001446 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001447 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001448 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001449 bool useCoverageAA,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001450 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -08001451 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001452 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001453 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001454 SkScalar yRadius = SkScalarHalf(ellipse.height());
1455
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001456 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001457 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001458 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001459 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001460 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1461
1462 SkScalar innerXRadius = 0;
1463 SkScalar innerYRadius = 0;
1464 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1465 SkScalar strokeWidth = stroke.getWidth();
1466
1467 if (SkScalarNearlyZero(strokeWidth)) {
1468 strokeWidth = SK_ScalarHalf;
1469 } else {
1470 strokeWidth *= SK_ScalarHalf;
1471 }
1472
1473 // we only handle thick strokes for near-circular ellipses
1474 if (strokeWidth > SK_ScalarHalf &&
1475 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1476 return false;
1477 }
1478
1479 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1480 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1481 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1482 return false;
1483 }
1484
1485 // set inner radius (if needed)
1486 if (SkStrokeRec::kStroke_Style == style) {
1487 innerXRadius = xRadius - strokeWidth;
1488 innerYRadius = yRadius - strokeWidth;
1489 }
1490
1491 xRadius += strokeWidth;
1492 yRadius += strokeWidth;
1493 }
1494 if (DIEllipseEdgeEffect::kStroke == mode) {
1495 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1496 DIEllipseEdgeEffect::kFill;
1497 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001498
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001499 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001500 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1501 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1502 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1503 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001504 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
1505 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001506
1507 SkRect bounds = SkRect::MakeLTRB(
1508 center.fX - xRadius - geoDx,
1509 center.fY - yRadius - geoDy,
1510 center.fX + xRadius + geoDx,
1511 center.fY + yRadius + geoDy
1512 );
1513
joshualitt76e7fb62015-02-11 08:52:27 -08001514 DIEllipseBatch::Geometry geometry;
1515 geometry.fViewMatrix = viewMatrix;
1516 geometry.fColor = color;
1517 geometry.fXRadius = xRadius;
1518 geometry.fYRadius = yRadius;
1519 geometry.fInnerXRadius = innerXRadius;
1520 geometry.fInnerYRadius = innerYRadius;
1521 geometry.fGeoDx = geoDx;
1522 geometry.fGeoDy = geoDy;
1523 geometry.fMode = mode;
1524 geometry.fDevBounds = bounds;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001525
joshualitt76e7fb62015-02-11 08:52:27 -08001526 SkAutoTUnref<GrBatch> batch(DIEllipseBatch::Create(geometry));
1527 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001528
1529 return true;
1530}
1531
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001532///////////////////////////////////////////////////////////////////////////////
1533
1534static const uint16_t gRRectIndices[] = {
1535 // corners
1536 0, 1, 5, 0, 5, 4,
1537 2, 3, 7, 2, 7, 6,
1538 8, 9, 13, 8, 13, 12,
1539 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001540
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001541 // edges
1542 1, 2, 6, 1, 6, 5,
1543 4, 5, 9, 4, 9, 8,
1544 6, 7, 11, 6, 11, 10,
1545 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001546
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001547 // center
1548 // we place this at the end so that we can ignore these indices when rendering stroke-only
1549 5, 6, 10, 5, 10, 9
1550};
1551
joshualitt5ead6da2014-10-22 16:00:29 -07001552static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1553static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1554static const int kVertsPerRRect = 16;
1555static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001556
joshualitt5531d512014-12-17 15:50:11 -08001557GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly) {
joshualitt5ead6da2014-10-22 16:00:29 -07001558 if (isStrokeOnly) {
1559 if (NULL == fStrokeRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001560 fStrokeRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1561 kIndicesPerStrokeRRect,
1562 kNumRRectsInIndexBuffer,
1563 kVertsPerRRect);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001564 }
joshualitt5ead6da2014-10-22 16:00:29 -07001565 return fStrokeRRectIndexBuffer;
1566 } else {
1567 if (NULL == fRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001568 fRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1569 kIndicesPerRRect,
1570 kNumRRectsInIndexBuffer,
1571 kVertsPerRRect);
joshualitt5ead6da2014-10-22 16:00:29 -07001572 }
1573 return fRRectIndexBuffer;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001574 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001575}
1576
joshualitt9853cce2014-11-17 14:22:48 -08001577bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001578 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001579 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001580 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001581 bool useAA,
1582 const SkRRect& origOuter,
1583 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001584 bool applyAA = useAA &&
egdaniel8dd688b2015-01-22 10:16:09 -08001585 !pipelineBuilder->getRenderTarget()->isMultisampled() &&
1586 pipelineBuilder->canUseFracCoveragePrimProc(color, *target->caps());
1587 GrPipelineBuilder::AutoRestoreEffects are;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001588 if (!origInner.isEmpty()) {
1589 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001590 if (!viewMatrix.isIdentity()) {
1591 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001592 return false;
1593 }
1594 }
joshualittb0a8a372014-09-23 09:50:21 -07001595 GrPrimitiveEdgeType edgeType = applyAA ?
1596 kInverseFillAA_GrProcessorEdgeType :
1597 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001598 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001599 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1600 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001601 return false;
1602 }
egdaniel8dd688b2015-01-22 10:16:09 -08001603 are.set(pipelineBuilder);
1604 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001605 }
1606
1607 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001608 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001609 return true;
1610 }
1611
1612 SkASSERT(!origOuter.isEmpty());
1613 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001614 if (!viewMatrix.isIdentity()) {
1615 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001616 return false;
1617 }
1618 }
joshualittb0a8a372014-09-23 09:50:21 -07001619 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001620 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001621 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001622 if (NULL == effect) {
1623 return false;
1624 }
1625 if (!are.isSet()) {
egdaniel8dd688b2015-01-22 10:16:09 -08001626 are.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001627 }
joshualittd27f73e2014-12-29 07:43:36 -08001628
1629 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001630 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001631 return false;
1632 }
joshualittd27f73e2014-12-29 07:43:36 -08001633
egdaniel8dd688b2015-01-22 10:16:09 -08001634 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001635 SkRect bounds = outer->getBounds();
1636 if (applyAA) {
1637 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1638 }
egdaniel8dd688b2015-01-22 10:16:09 -08001639 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001640 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001641}
1642
joshualitt76e7fb62015-02-11 08:52:27 -08001643///////////////////////////////////////////////////////////////////////////////////////////////////
1644
1645class RRectCircleRendererBatch : public GrBatch {
1646public:
1647 struct Geometry {
1648 GrColor fColor;
1649 SkMatrix fViewMatrix;
1650 SkScalar fInnerRadius;
1651 SkScalar fOuterRadius;
1652 bool fStroke;
1653 SkRect fDevBounds;
1654 };
1655
1656 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1657 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry, indexBuffer));
1658 }
1659
1660 const char* name() const SK_OVERRIDE { return "RRectCircleBatch"; }
1661
1662 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1663 // When this is called on a batch, there is only one geometry bundle
1664 out->setKnownFourComponents(fGeoData[0].fColor);
1665 }
1666 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1667 out->setUnknownSingleComponent();
1668 }
1669
1670 void initBatchOpt(const GrBatchOpt& batchOpt) {
1671 fBatchOpt = batchOpt;
1672 }
1673
1674 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1675 // Handle any color overrides
1676 if (init.fColorIgnored) {
1677 fGeoData[0].fColor = GrColor_ILLEGAL;
1678 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1679 fGeoData[0].fColor = init.fOverrideColor;
1680 }
1681
1682 // setup batch properties
1683 fBatch.fColorIgnored = init.fColorIgnored;
1684 fBatch.fColor = fGeoData[0].fColor;
1685 fBatch.fStroke = fGeoData[0].fStroke;
1686 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1687 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1688 }
1689
1690 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1691 // reset to device coordinates
1692 SkMatrix invert;
1693 if (!this->viewMatrix().invert(&invert)) {
1694 SkDebugf("Failed to invert\n");
1695 return;
1696 }
1697
1698 // Setup geometry processor
1699 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1700 this->stroke(),
1701 invert));
1702
1703 batchTarget->initDraw(gp, pipeline);
1704
1705 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1706 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1707 // everywhere we can remove this nastiness
1708 GrPipelineInfo init;
1709 init.fColorIgnored = fBatch.fColorIgnored;
1710 init.fOverrideColor = GrColor_ILLEGAL;
1711 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1712 init.fUsesLocalCoords = this->usesLocalCoords();
1713 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1714
1715 int instanceCount = fGeoData.count();
1716 int vertexCount = kVertsPerRRect * instanceCount;
1717 size_t vertexStride = gp->getVertexStride();
1718 SkASSERT(vertexStride == sizeof(CircleVertex));
1719
1720 const GrVertexBuffer* vertexBuffer;
1721 int firstVertex;
1722
1723 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1724 vertexCount,
1725 &vertexBuffer,
1726 &firstVertex);
1727
1728 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
1729
1730 for (int i = 0; i < instanceCount; i++) {
1731 Geometry& args = fGeoData[i];
1732
1733 SkScalar outerRadius = args.fOuterRadius;
1734
1735 const SkRect& bounds = args.fDevBounds;
1736
1737 SkScalar yCoords[4] = {
1738 bounds.fTop,
1739 bounds.fTop + outerRadius,
1740 bounds.fBottom - outerRadius,
1741 bounds.fBottom
1742 };
1743
1744 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1745 // The inner radius in the vertex data must be specified in normalized space.
1746 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1747 for (int i = 0; i < 4; ++i) {
1748 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1749 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1750 verts->fOuterRadius = outerRadius;
1751 verts->fInnerRadius = innerRadius;
1752 verts++;
1753
1754 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1755 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1756 verts->fOuterRadius = outerRadius;
1757 verts->fInnerRadius = innerRadius;
1758 verts++;
1759
1760 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1761 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1762 verts->fOuterRadius = outerRadius;
1763 verts->fInnerRadius = innerRadius;
1764 verts++;
1765
1766 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1767 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1768 verts->fOuterRadius = outerRadius;
1769 verts->fInnerRadius = innerRadius;
1770 verts++;
1771 }
1772 }
1773
1774 // drop out the middle quad if we're stroked
1775 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1776 SK_ARRAY_COUNT(gRRectIndices);
1777
1778
1779 GrDrawTarget::DrawInfo drawInfo;
1780 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1781 drawInfo.setStartVertex(0);
1782 drawInfo.setStartIndex(0);
1783 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1784 drawInfo.setIndicesPerInstance(indexCnt);
1785 drawInfo.adjustStartVertex(firstVertex);
1786 drawInfo.setVertexBuffer(vertexBuffer);
1787 drawInfo.setIndexBuffer(fIndexBuffer);
1788
1789 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1790
1791 while (instanceCount) {
1792 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1793 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1794 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1795
1796 batchTarget->draw(drawInfo);
1797
1798 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1799 instanceCount -= drawInfo.instanceCount();
1800 }
1801 }
1802
1803 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1804
1805private:
1806 RRectCircleRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
1807 : fIndexBuffer(indexBuffer) {
1808 this->initClassID<RRectCircleRendererBatch>();
1809 fGeoData.push_back(geometry);
1810 }
1811
1812 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1813 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1814
1815 // TODO use vertex color to avoid breaking batches
1816 if (this->color() != that->color()) {
1817 return false;
1818 }
1819
1820 if (this->stroke() != that->stroke()) {
1821 return false;
1822 }
1823
1824 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1825 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1826 return false;
1827 }
1828
1829 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1830 return true;
1831 }
1832
1833 GrColor color() const { return fBatch.fColor; }
1834 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1835 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1836 bool stroke() const { return fBatch.fStroke; }
1837
1838 struct BatchTracker {
1839 GrColor fColor;
1840 bool fStroke;
1841 bool fUsesLocalCoords;
1842 bool fColorIgnored;
1843 bool fCoverageIgnored;
1844 };
1845
1846 GrBatchOpt fBatchOpt;
1847 BatchTracker fBatch;
1848 SkSTArray<1, Geometry, true> fGeoData;
1849 const GrIndexBuffer* fIndexBuffer;
1850};
1851
1852class RRectEllipseRendererBatch : public GrBatch {
1853public:
1854 struct Geometry {
1855 GrColor fColor;
1856 SkMatrix fViewMatrix;
1857 SkScalar fXRadius;
1858 SkScalar fYRadius;
1859 SkScalar fInnerXRadius;
1860 SkScalar fInnerYRadius;
1861 bool fStroke;
1862 SkRect fDevBounds;
1863 };
1864
1865 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1866 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry, indexBuffer));
1867 }
1868
1869 const char* name() const SK_OVERRIDE { return "RRectEllipseRendererBatch"; }
1870
1871 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1872 // When this is called on a batch, there is only one geometry bundle
1873 out->setKnownFourComponents(fGeoData[0].fColor);
1874 }
1875 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1876 out->setUnknownSingleComponent();
1877 }
1878
1879 void initBatchOpt(const GrBatchOpt& batchOpt) {
1880 fBatchOpt = batchOpt;
1881 }
1882
1883 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1884 // Handle any color overrides
1885 if (init.fColorIgnored) {
1886 fGeoData[0].fColor = GrColor_ILLEGAL;
1887 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1888 fGeoData[0].fColor = init.fOverrideColor;
1889 }
1890
1891 // setup batch properties
1892 fBatch.fColorIgnored = init.fColorIgnored;
1893 fBatch.fColor = fGeoData[0].fColor;
1894 fBatch.fStroke = fGeoData[0].fStroke;
1895 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1896 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1897 }
1898
1899 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1900 // reset to device coordinates
1901 SkMatrix invert;
1902 if (!this->viewMatrix().invert(&invert)) {
1903 SkDebugf("Failed to invert\n");
1904 return;
1905 }
1906
1907 // Setup geometry processor
1908 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1909 this->stroke(),
1910 invert));
1911
1912 batchTarget->initDraw(gp, pipeline);
1913
1914 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1915 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1916 // everywhere we can remove this nastiness
1917 GrPipelineInfo init;
1918 init.fColorIgnored = fBatch.fColorIgnored;
1919 init.fOverrideColor = GrColor_ILLEGAL;
1920 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1921 init.fUsesLocalCoords = this->usesLocalCoords();
1922 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1923
1924 int instanceCount = fGeoData.count();
1925 int vertexCount = kVertsPerRRect * instanceCount;
1926 size_t vertexStride = gp->getVertexStride();
1927 SkASSERT(vertexStride == sizeof(EllipseVertex));
1928
1929 const GrVertexBuffer* vertexBuffer;
1930 int firstVertex;
1931
1932 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1933 vertexCount,
1934 &vertexBuffer,
1935 &firstVertex);
1936
1937 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1938
1939 for (int i = 0; i < instanceCount; i++) {
1940 Geometry& args = fGeoData[i];
1941
1942 // Compute the reciprocals of the radii here to save time in the shader
1943 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1944 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1945 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1946 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1947
1948 // Extend the radii out half a pixel to antialias.
1949 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1950 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1951
1952 const SkRect& bounds = args.fDevBounds;
1953
1954 SkScalar yCoords[4] = {
1955 bounds.fTop,
1956 bounds.fTop + yOuterRadius,
1957 bounds.fBottom - yOuterRadius,
1958 bounds.fBottom
1959 };
1960 SkScalar yOuterOffsets[4] = {
1961 yOuterRadius,
1962 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1963 SK_ScalarNearlyZero,
1964 yOuterRadius
1965 };
1966
1967 for (int i = 0; i < 4; ++i) {
1968 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1969 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1970 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1971 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1972 verts++;
1973
1974 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1975 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1976 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1977 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1978 verts++;
1979
1980 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1981 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1982 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1983 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1984 verts++;
1985
1986 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1987 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1988 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1989 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1990 verts++;
1991 }
1992 }
1993
1994 // drop out the middle quad if we're stroked
1995 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1996 SK_ARRAY_COUNT(gRRectIndices);
1997
1998 GrDrawTarget::DrawInfo drawInfo;
1999 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
2000 drawInfo.setStartVertex(0);
2001 drawInfo.setStartIndex(0);
2002 drawInfo.setVerticesPerInstance(kVertsPerRRect);
2003 drawInfo.setIndicesPerInstance(indexCnt);
2004 drawInfo.adjustStartVertex(firstVertex);
2005 drawInfo.setVertexBuffer(vertexBuffer);
2006 drawInfo.setIndexBuffer(fIndexBuffer);
2007
2008 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
2009
2010 while (instanceCount) {
2011 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
2012 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
2013 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
2014
2015 batchTarget->draw(drawInfo);
2016
2017 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
2018 instanceCount -= drawInfo.instanceCount();
2019 }
2020 }
2021
2022 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
2023
2024private:
2025 RRectEllipseRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
2026 : fIndexBuffer(indexBuffer) {
2027 this->initClassID<RRectEllipseRendererBatch>();
2028 fGeoData.push_back(geometry);
2029 }
2030
2031 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
2032 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2033
2034 // TODO use vertex color to avoid breaking batches
2035 if (this->color() != that->color()) {
2036 return false;
2037 }
2038
2039 if (this->stroke() != that->stroke()) {
2040 return false;
2041 }
2042
2043 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
2044 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
2045 return false;
2046 }
2047
2048 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
2049 return true;
2050 }
2051
2052 GrColor color() const { return fBatch.fColor; }
2053 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
2054 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
2055 bool stroke() const { return fBatch.fStroke; }
2056
2057 struct BatchTracker {
2058 GrColor fColor;
2059 bool fStroke;
2060 bool fUsesLocalCoords;
2061 bool fColorIgnored;
2062 bool fCoverageIgnored;
2063 };
2064
2065 GrBatchOpt fBatchOpt;
2066 BatchTracker fBatch;
2067 SkSTArray<1, Geometry, true> fGeoData;
2068 const GrIndexBuffer* fIndexBuffer;
2069};
2070
joshualitt9853cce2014-11-17 14:22:48 -08002071bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08002072 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08002073 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08002074 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08002075 bool useAA,
2076 const SkRRect& rrect,
2077 const SkStrokeRec& stroke) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002078 if (rrect.isOval()) {
egdaniel8dd688b2015-01-22 10:16:09 -08002079 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
joshualitt8059eb92014-12-29 15:10:07 -08002080 stroke);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002081 }
2082
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002083 bool useCoverageAA = useAA &&
egdaniel8dd688b2015-01-22 10:16:09 -08002084 !pipelineBuilder->getRenderTarget()->isMultisampled() &&
2085 pipelineBuilder->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002086
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00002087 // only anti-aliased rrects for now
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002088 if (!useCoverageAA) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00002089 return false;
2090 }
2091
joshualitt8059eb92014-12-29 15:10:07 -08002092 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002093 return false;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002094 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002095
2096 // do any matrix crunching before we reset the draw state for device coords
2097 const SkRect& rrectBounds = rrect.getBounds();
2098 SkRect bounds;
joshualitt8059eb92014-12-29 15:10:07 -08002099 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002100
2101 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002102 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2103 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2104 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2105 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002106
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002107 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002108
2109 // do (potentially) anisotropic mapping of stroke
2110 SkVector scaledStroke;
2111 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002112
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002113 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2114 SkStrokeRec::kHairline_Style == style;
2115 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2116
2117 if (hasStroke) {
2118 if (SkStrokeRec::kHairline_Style == style) {
2119 scaledStroke.set(1, 1);
2120 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002121 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2122 viewMatrix[SkMatrix::kMSkewY]));
2123 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2124 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002125 }
2126
2127 // if half of strokewidth is greater than radius, we don't handle that right now
2128 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
2129 return false;
2130 }
2131 }
2132
2133 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2134 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2135 // patch will have fractional coverage. This only matters when the interior is actually filled.
2136 // We could consider falling back to rect rendering here, since a tiny radius is
2137 // indistinguishable from a square corner.
2138 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002139 return false;
2140 }
2141
joshualitt5531d512014-12-17 15:50:11 -08002142 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002143 if (NULL == indexBuffer) {
tfarina38406c82014-10-31 07:11:12 -07002144 SkDebugf("Failed to create index buffer!\n");
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002145 return false;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002146 }
2147
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002148 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002149 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002150 SkScalar innerRadius = 0.0f;
2151 SkScalar outerRadius = xRadius;
2152 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002153 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002154 if (SkScalarNearlyZero(scaledStroke.fX)) {
2155 halfWidth = SK_ScalarHalf;
2156 } else {
2157 halfWidth = SkScalarHalf(scaledStroke.fX);
2158 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002159
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002160 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002161 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002162 }
2163 outerRadius += halfWidth;
2164 bounds.outset(halfWidth, halfWidth);
2165 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002166
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002167 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002168
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002169 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08002170 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2171 // Second, the outer radius is used to compute the verts of the bounding box that is
2172 // rendered and the outset ensures the box will cover all partially covered by the rrect
2173 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002174 outerRadius += SK_ScalarHalf;
2175 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002176
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002177 // Expand the rect so all the pixels will be captured.
2178 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002179
joshualitt76e7fb62015-02-11 08:52:27 -08002180 RRectCircleRendererBatch::Geometry geometry;
2181 geometry.fViewMatrix = viewMatrix;
2182 geometry.fColor = color;
2183 geometry.fInnerRadius = innerRadius;
2184 geometry.fOuterRadius = outerRadius;
2185 geometry.fStroke = isStrokeOnly;
2186 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002187
joshualitt76e7fb62015-02-11 08:52:27 -08002188 SkAutoTUnref<GrBatch> batch(RRectCircleRendererBatch::Create(geometry, indexBuffer));
2189 target->drawBatch(pipelineBuilder, batch, &bounds);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002190
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002191 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002192 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002193 SkScalar innerXRadius = 0.0f;
2194 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002195 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002196 if (SkScalarNearlyZero(scaledStroke.length())) {
2197 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2198 } else {
2199 scaledStroke.scale(SK_ScalarHalf);
2200 }
2201
2202 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00002203 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002204 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
2205 return false;
2206 }
2207
2208 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2209 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2210 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
2211 return false;
2212 }
2213
2214 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002215 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002216 innerXRadius = xRadius - scaledStroke.fX;
2217 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002218 }
2219
2220 xRadius += scaledStroke.fX;
2221 yRadius += scaledStroke.fY;
2222 bounds.outset(scaledStroke.fX, scaledStroke.fY);
2223 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002224
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002225 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002226
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002227 // Expand the rect so all the pixels will be captured.
2228 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2229
joshualitt76e7fb62015-02-11 08:52:27 -08002230 RRectEllipseRendererBatch::Geometry geometry;
2231 geometry.fViewMatrix = viewMatrix;
2232 geometry.fColor = color;
2233 geometry.fXRadius = xRadius;
2234 geometry.fYRadius = yRadius;
2235 geometry.fInnerXRadius = innerXRadius;
2236 geometry.fInnerYRadius = innerYRadius;
2237 geometry.fStroke = isStrokeOnly;
2238 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002239
joshualitt76e7fb62015-02-11 08:52:27 -08002240 SkAutoTUnref<GrBatch> batch(RRectEllipseRendererBatch::Create(geometry, indexBuffer));
2241 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002242 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002243 return true;
2244}