1 /*
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
10 #include "GrBatch.h"
11 #include "GrBatchTarget.h"
12 #include "GrBatchTest.h"
13 #include "GrDrawTarget.h"
14 #include "GrGeometryProcessor.h"
15 #include "GrInvariantOutput.h"
16 #include "GrPipelineBuilder.h"
17 #include "GrProcessor.h"
18 #include "GrResourceProvider.h"
19 #include "GrVertexBuffer.h"
20 #include "SkRRect.h"
21 #include "SkStrokeRec.h"
22 #include "SkTLazy.h"
23 #include "effects/GrRRectEffect.h"
24 #include "gl/GrGLProcessor.h"
25 #include "gl/GrGLSL.h"
26 #include "gl/GrGLGeometryProcessor.h"
27 #include "gl/builders/GrGLProgramBuilder.h"
28
29 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
30
31 namespace {
32 // TODO(joshualitt) add per vertex colors
33 struct CircleVertex {
34 SkPoint fPos;
35 SkPoint fOffset;
36 SkScalar fOuterRadius;
37 SkScalar fInnerRadius;
38 };
39
40 struct EllipseVertex {
41 SkPoint fPos;
42 SkPoint fOffset;
43 SkPoint fOuterRadii;
44 SkPoint fInnerRadii;
45 };
46
47 struct DIEllipseVertex {
48 SkPoint fPos;
49 SkPoint fOuterOffset;
50 SkPoint fInnerOffset;
51 };
52
circle_stays_circle(const SkMatrix & m)53 inline bool circle_stays_circle(const SkMatrix& m) {
54 return m.isSimilarity();
55 }
56
57 }
58
59 ///////////////////////////////////////////////////////////////////////////////
60
61 /**
62 * The output of this effect is a modulation of the input color and coverage for a circle. It
63 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
64 * with origin at the circle center. Two vertex attributes are used:
65 * vec2f : position in device space of the bounding geometry vertices
66 * vec4f : (p.xy, outerRad, innerRad)
67 * p is the position in the normalized space.
68 * outerRad is the outerRadius in device space.
69 * innerRad is the innerRadius in normalized space (ignored if not stroking).
70 */
71
72 class CircleEdgeEffect : public GrGeometryProcessor {
73 public:
Create(GrColor color,bool stroke,const SkMatrix & localMatrix)74 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
75 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
76 }
77
inPosition() const78 const Attribute* inPosition() const { return fInPosition; }
inCircleEdge() const79 const Attribute* inCircleEdge() const { return fInCircleEdge; }
color() const80 GrColor color() const { return fColor; }
localMatrix() const81 const SkMatrix& localMatrix() const { return fLocalMatrix; }
~CircleEdgeEffect()82 virtual ~CircleEdgeEffect() {}
83
name() const84 const char* name() const override { return "CircleEdge"; }
85
isStroked() const86 inline bool isStroked() const { return fStroke; }
87
88 class GLProcessor : public GrGLGeometryProcessor {
89 public:
GLProcessor(const GrGeometryProcessor &,const GrBatchTracker &)90 GLProcessor(const GrGeometryProcessor&,
91 const GrBatchTracker&)
92 : fColor(GrColor_ILLEGAL) {}
93
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)94 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
95 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
96 GrGLGPBuilder* pb = args.fPB;
97 const BatchTracker& local = args.fBT.cast<BatchTracker>();
98 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
99
100 // emit attributes
101 vsBuilder->emitAttributes(ce);
102
103 GrGLVertToFrag v(kVec4f_GrSLType);
104 args.fPB->addVarying("CircleEdge", &v);
105 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
106
107 // Setup pass through color
108 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
109 &fColorUniform);
110
111 // Setup position
112 this->setupPosition(pb, gpArgs, ce.inPosition()->fName);
113
114 // emit transforms
115 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
116 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
117
118 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
119 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
120 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
121 if (ce.isStroked()) {
122 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
123 v.fsIn(), v.fsIn());
124 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
125 }
126
127 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
128 }
129
GenKey(const GrGeometryProcessor & gp,const GrBatchTracker & bt,const GrGLSLCaps &,GrProcessorKeyBuilder * b)130 static void GenKey(const GrGeometryProcessor& gp,
131 const GrBatchTracker& bt,
132 const GrGLSLCaps&,
133 GrProcessorKeyBuilder* b) {
134 const BatchTracker& local = bt.cast<BatchTracker>();
135 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
136 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
137 key |= local.fUsesLocalCoords && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
138 b->add32(key << 16 | local.fInputColorType);
139 }
140
setData(const GrGLProgramDataManager & pdman,const GrPrimitiveProcessor & gp,const GrBatchTracker & bt)141 virtual void setData(const GrGLProgramDataManager& pdman,
142 const GrPrimitiveProcessor& gp,
143 const GrBatchTracker& bt) override {
144 const BatchTracker& local = bt.cast<BatchTracker>();
145 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
146 GrGLfloat c[4];
147 GrColorToRGBAFloat(local.fColor, c);
148 pdman.set4fv(fColorUniform, 1, c);
149 fColor = local.fColor;
150 }
151 }
152
setTransformData(const GrPrimitiveProcessor & primProc,const GrGLProgramDataManager & pdman,int index,const SkTArray<const GrCoordTransform *,true> & transforms)153 void setTransformData(const GrPrimitiveProcessor& primProc,
154 const GrGLProgramDataManager& pdman,
155 int index,
156 const SkTArray<const GrCoordTransform*, true>& transforms) override {
157 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
158 }
159
160 private:
161 GrColor fColor;
162 UniformHandle fColorUniform;
163 typedef GrGLGeometryProcessor INHERITED;
164 };
165
getGLProcessorKey(const GrBatchTracker & bt,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const166 virtual void getGLProcessorKey(const GrBatchTracker& bt,
167 const GrGLSLCaps& caps,
168 GrProcessorKeyBuilder* b) const override {
169 GLProcessor::GenKey(*this, bt, caps, b);
170 }
171
createGLInstance(const GrBatchTracker & bt,const GrGLSLCaps &) const172 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
173 const GrGLSLCaps&) const override {
174 return SkNEW_ARGS(GLProcessor, (*this, bt));
175 }
176
initBatchTracker(GrBatchTracker * bt,const GrPipelineInfo & init) const177 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
178 BatchTracker* local = bt->cast<BatchTracker>();
179 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
180 local->fUsesLocalCoords = init.fUsesLocalCoords;
181 }
182
183 private:
CircleEdgeEffect(GrColor color,bool stroke,const SkMatrix & localMatrix)184 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
185 : fColor(color)
186 , fLocalMatrix(localMatrix) {
187 this->initClassID<CircleEdgeEffect>();
188 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
189 kHigh_GrSLPrecision));
190 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
191 kVec4f_GrVertexAttribType));
192 fStroke = stroke;
193 }
194
195 struct BatchTracker {
196 GrGPInput fInputColorType;
197 GrColor fColor;
198 bool fUsesLocalCoords;
199 };
200
201 GrColor fColor;
202 SkMatrix fLocalMatrix;
203 const Attribute* fInPosition;
204 const Attribute* fInCircleEdge;
205 bool fStroke;
206
207 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
208
209 typedef GrGeometryProcessor INHERITED;
210 };
211
212 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
213
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])214 GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
215 GrContext* context,
216 const GrDrawTargetCaps&,
217 GrTexture* textures[]) {
218 return CircleEdgeEffect::Create(GrRandomColor(random),
219 random->nextBool(),
220 GrTest::TestMatrix(random));
221 }
222
223 ///////////////////////////////////////////////////////////////////////////////
224
225 /**
226 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
227 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
228 * in both x and y directions.
229 *
230 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
231 */
232
233 class EllipseEdgeEffect : public GrGeometryProcessor {
234 public:
Create(GrColor color,bool stroke,const SkMatrix & localMatrix)235 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
236 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
237 }
238
~EllipseEdgeEffect()239 virtual ~EllipseEdgeEffect() {}
240
name() const241 const char* name() const override { return "EllipseEdge"; }
242
inPosition() const243 const Attribute* inPosition() const { return fInPosition; }
inEllipseOffset() const244 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
inEllipseRadii() const245 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
color() const246 GrColor color() const { return fColor; }
localMatrix() const247 const SkMatrix& localMatrix() const { return fLocalMatrix; }
248
isStroked() const249 inline bool isStroked() const { return fStroke; }
250
251 class GLProcessor : public GrGLGeometryProcessor {
252 public:
GLProcessor(const GrGeometryProcessor &,const GrBatchTracker &)253 GLProcessor(const GrGeometryProcessor&,
254 const GrBatchTracker&)
255 : fColor(GrColor_ILLEGAL) {}
256
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)257 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
258 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
259 GrGLGPBuilder* pb = args.fPB;
260 const BatchTracker& local = args.fBT.cast<BatchTracker>();
261 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
262
263 // emit attributes
264 vsBuilder->emitAttributes(ee);
265
266 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
267 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
268 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
269 ee.inEllipseOffset()->fName);
270
271 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
272 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
273 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
274 ee.inEllipseRadii()->fName);
275
276 // Setup pass through color
277 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
278 &fColorUniform);
279
280 // Setup position
281 this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
282
283 // emit transforms
284 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
285 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
286
287 // for outer curve
288 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
289 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
290 ellipseRadii.fsIn());
291 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
292 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
293 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
294
295 // avoid calling inversesqrt on zero.
296 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
297 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
298 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
299
300 // for inner curve
301 if (ee.isStroked()) {
302 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
303 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
304 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
305 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
306 ellipseRadii.fsIn());
307 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
308 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
309 }
310
311 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
312 }
313
GenKey(const GrGeometryProcessor & gp,const GrBatchTracker & bt,const GrGLSLCaps &,GrProcessorKeyBuilder * b)314 static void GenKey(const GrGeometryProcessor& gp,
315 const GrBatchTracker& bt,
316 const GrGLSLCaps&,
317 GrProcessorKeyBuilder* b) {
318 const BatchTracker& local = bt.cast<BatchTracker>();
319 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
320 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
321 key |= local.fUsesLocalCoords && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
322 b->add32(key << 16 | local.fInputColorType);
323 }
324
setData(const GrGLProgramDataManager & pdman,const GrPrimitiveProcessor & gp,const GrBatchTracker & bt)325 virtual void setData(const GrGLProgramDataManager& pdman,
326 const GrPrimitiveProcessor& gp,
327 const GrBatchTracker& bt) override {
328
329 const BatchTracker& local = bt.cast<BatchTracker>();
330 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
331 GrGLfloat c[4];
332 GrColorToRGBAFloat(local.fColor, c);
333 pdman.set4fv(fColorUniform, 1, c);
334 fColor = local.fColor;
335 }
336 }
337
setTransformData(const GrPrimitiveProcessor & primProc,const GrGLProgramDataManager & pdman,int index,const SkTArray<const GrCoordTransform *,true> & transforms)338 void setTransformData(const GrPrimitiveProcessor& primProc,
339 const GrGLProgramDataManager& pdman,
340 int index,
341 const SkTArray<const GrCoordTransform*, true>& transforms) override {
342 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
343 }
344
345 private:
346 GrColor fColor;
347 UniformHandle fColorUniform;
348
349 typedef GrGLGeometryProcessor INHERITED;
350 };
351
getGLProcessorKey(const GrBatchTracker & bt,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const352 virtual void getGLProcessorKey(const GrBatchTracker& bt,
353 const GrGLSLCaps& caps,
354 GrProcessorKeyBuilder* b) const override {
355 GLProcessor::GenKey(*this, bt, caps, b);
356 }
357
createGLInstance(const GrBatchTracker & bt,const GrGLSLCaps &) const358 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
359 const GrGLSLCaps&) const override {
360 return SkNEW_ARGS(GLProcessor, (*this, bt));
361 }
362
initBatchTracker(GrBatchTracker * bt,const GrPipelineInfo & init) const363 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
364 BatchTracker* local = bt->cast<BatchTracker>();
365 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
366 local->fUsesLocalCoords = init.fUsesLocalCoords;
367 }
368
369 private:
EllipseEdgeEffect(GrColor color,bool stroke,const SkMatrix & localMatrix)370 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
371 : fColor(color)
372 , fLocalMatrix(localMatrix) {
373 this->initClassID<EllipseEdgeEffect>();
374 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
375 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
376 kVec2f_GrVertexAttribType));
377 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
378 kVec4f_GrVertexAttribType));
379 fStroke = stroke;
380 }
381
382 struct BatchTracker {
383 GrGPInput fInputColorType;
384 GrColor fColor;
385 bool fUsesLocalCoords;
386 };
387
388 const Attribute* fInPosition;
389 const Attribute* fInEllipseOffset;
390 const Attribute* fInEllipseRadii;
391 GrColor fColor;
392 SkMatrix fLocalMatrix;
393 bool fStroke;
394
395 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
396
397 typedef GrGeometryProcessor INHERITED;
398 };
399
400 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
401
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])402 GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
403 GrContext* context,
404 const GrDrawTargetCaps&,
405 GrTexture* textures[]) {
406 return EllipseEdgeEffect::Create(GrRandomColor(random),
407 random->nextBool(),
408 GrTest::TestMatrix(random));
409 }
410
411 ///////////////////////////////////////////////////////////////////////////////
412
413 /**
414 * The output of this effect is a modulation of the input color and coverage for an ellipse,
415 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
416 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
417 * using differentials.
418 *
419 * The result is device-independent and can be used with any affine matrix.
420 */
421
422 class DIEllipseEdgeEffect : public GrGeometryProcessor {
423 public:
424 enum Mode { kStroke = 0, kHairline, kFill };
425
Create(GrColor color,const SkMatrix & viewMatrix,Mode mode)426 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
427 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
428 }
429
~DIEllipseEdgeEffect()430 virtual ~DIEllipseEdgeEffect() {}
431
name() const432 const char* name() const override { return "DIEllipseEdge"; }
433
inPosition() const434 const Attribute* inPosition() const { return fInPosition; }
inEllipseOffsets0() const435 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
inEllipseOffsets1() const436 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
color() const437 GrColor color() const { return fColor; }
viewMatrix() const438 const SkMatrix& viewMatrix() const { return fViewMatrix; }
439
getMode() const440 inline Mode getMode() const { return fMode; }
441
442 class GLProcessor : public GrGLGeometryProcessor {
443 public:
GLProcessor(const GrGeometryProcessor &,const GrBatchTracker &)444 GLProcessor(const GrGeometryProcessor&,
445 const GrBatchTracker&)
446 : fColor(GrColor_ILLEGAL) {}
447
onEmitCode(EmitArgs & args,GrGPArgs * gpArgs)448 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
449 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
450 GrGLGPBuilder* pb = args.fPB;
451 const BatchTracker& local = args.fBT.cast<BatchTracker>();
452 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
453
454 // emit attributes
455 vsBuilder->emitAttributes(ee);
456
457 GrGLVertToFrag offsets0(kVec2f_GrSLType);
458 args.fPB->addVarying("EllipseOffsets0", &offsets0);
459 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
460 ee.inEllipseOffsets0()->fName);
461
462 GrGLVertToFrag offsets1(kVec2f_GrSLType);
463 args.fPB->addVarying("EllipseOffsets1", &offsets1);
464 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
465 ee.inEllipseOffsets1()->fName);
466
467 // Setup pass through color
468 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
469 &fColorUniform);
470
471 // Setup position
472 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
473
474 // emit transforms
475 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
476 args.fTransformsIn, args.fTransformsOut);
477
478 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
479 SkAssertResult(fsBuilder->enableFeature(
480 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
481 // for outer curve
482 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
483 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
484 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
485 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
486 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
487 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
488 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
489
490 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
491 // avoid calling inversesqrt on zero.
492 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
493 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
494 if (kHairline == ee.getMode()) {
495 // can probably do this with one step
496 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
497 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
498 } else {
499 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
500 }
501
502 // for inner curve
503 if (kStroke == ee.getMode()) {
504 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
505 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
506 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
507 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
508 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
509 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
510 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
511 offsets1.fsIn());
512 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
513 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
514 }
515
516 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
517 }
518
GenKey(const GrGeometryProcessor & gp,const GrBatchTracker & bt,const GrGLSLCaps &,GrProcessorKeyBuilder * b)519 static void GenKey(const GrGeometryProcessor& gp,
520 const GrBatchTracker& bt,
521 const GrGLSLCaps&,
522 GrProcessorKeyBuilder* b) {
523 const BatchTracker& local = bt.cast<BatchTracker>();
524 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
525 uint16_t key = ellipseEffect.getMode();
526 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 9;
527 b->add32(key << 16 | local.fInputColorType);
528 }
529
setData(const GrGLProgramDataManager & pdman,const GrPrimitiveProcessor & gp,const GrBatchTracker & bt)530 virtual void setData(const GrGLProgramDataManager& pdman,
531 const GrPrimitiveProcessor& gp,
532 const GrBatchTracker& bt) override {
533 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
534 this->setUniformViewMatrix(pdman, dee.viewMatrix());
535
536 const BatchTracker& local = bt.cast<BatchTracker>();
537 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
538 GrGLfloat c[4];
539 GrColorToRGBAFloat(local.fColor, c);
540 pdman.set4fv(fColorUniform, 1, c);
541 fColor = local.fColor;
542 }
543 }
544
545 private:
546 GrColor fColor;
547 UniformHandle fColorUniform;
548
549 typedef GrGLGeometryProcessor INHERITED;
550 };
551
getGLProcessorKey(const GrBatchTracker & bt,const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const552 virtual void getGLProcessorKey(const GrBatchTracker& bt,
553 const GrGLSLCaps& caps,
554 GrProcessorKeyBuilder* b) const override {
555 GLProcessor::GenKey(*this, bt, caps, b);
556 }
557
createGLInstance(const GrBatchTracker & bt,const GrGLSLCaps &) const558 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
559 const GrGLSLCaps&) const override {
560 return SkNEW_ARGS(GLProcessor, (*this, bt));
561 }
562
initBatchTracker(GrBatchTracker * bt,const GrPipelineInfo & init) const563 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
564 BatchTracker* local = bt->cast<BatchTracker>();
565 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
566 local->fUsesLocalCoords = init.fUsesLocalCoords;
567 }
568
569 private:
DIEllipseEdgeEffect(GrColor color,const SkMatrix & viewMatrix,Mode mode)570 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
571 : fColor(color)
572 , fViewMatrix(viewMatrix) {
573 this->initClassID<DIEllipseEdgeEffect>();
574 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
575 kHigh_GrSLPrecision));
576 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
577 kVec2f_GrVertexAttribType));
578 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
579 kVec2f_GrVertexAttribType));
580 fMode = mode;
581 }
582
583 struct BatchTracker {
584 GrGPInput fInputColorType;
585 GrColor fColor;
586 bool fUsesLocalCoords;
587 };
588
589 const Attribute* fInPosition;
590 const Attribute* fInEllipseOffsets0;
591 const Attribute* fInEllipseOffsets1;
592 GrColor fColor;
593 SkMatrix fViewMatrix;
594 Mode fMode;
595
596 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
597
598 typedef GrGeometryProcessor INHERITED;
599 };
600
601 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
602
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])603 GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
604 GrContext* context,
605 const GrDrawTargetCaps&,
606 GrTexture* textures[]) {
607 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
608 GrTest::TestMatrix(random),
609 (Mode)(random->nextRangeU(0,2)));
610 }
611
612 ///////////////////////////////////////////////////////////////////////////////
613
drawOval(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,bool useAA,const SkRect & oval,const SkStrokeRec & stroke)614 bool GrOvalRenderer::drawOval(GrDrawTarget* target,
615 GrPipelineBuilder* pipelineBuilder,
616 GrColor color,
617 const SkMatrix& viewMatrix,
618 bool useAA,
619 const SkRect& oval,
620 const SkStrokeRec& stroke)
621 {
622 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
623
624 if (!useCoverageAA) {
625 return false;
626 }
627
628 // we can draw circles
629 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
630 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
631 // if we have shader derivative support, render as device-independent
632 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
633 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
634 stroke);
635 // otherwise axis-aligned ellipses only
636 } else if (viewMatrix.rectStaysRect()) {
637 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
638 stroke);
639 } else {
640 return false;
641 }
642
643 return true;
644 }
645
646 ///////////////////////////////////////////////////////////////////////////////
647
648 class CircleBatch : public GrBatch {
649 public:
650 struct Geometry {
651 GrColor fColor;
652 SkMatrix fViewMatrix;
653 SkScalar fInnerRadius;
654 SkScalar fOuterRadius;
655 bool fStroke;
656 SkRect fDevBounds;
657 };
658
Create(const Geometry & geometry)659 static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); }
660
name() const661 const char* name() const override { return "CircleBatch"; }
662
getInvariantOutputColor(GrInitInvariantOutput * out) const663 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
664 // When this is called on a batch, there is only one geometry bundle
665 out->setKnownFourComponents(fGeoData[0].fColor);
666 }
667
getInvariantOutputCoverage(GrInitInvariantOutput * out) const668 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
669 out->setUnknownSingleComponent();
670 }
671
initBatchTracker(const GrPipelineInfo & init)672 void initBatchTracker(const GrPipelineInfo& init) override {
673 // Handle any color overrides
674 if (init.fColorIgnored) {
675 fGeoData[0].fColor = GrColor_ILLEGAL;
676 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
677 fGeoData[0].fColor = init.fOverrideColor;
678 }
679
680 // setup batch properties
681 fBatch.fColorIgnored = init.fColorIgnored;
682 fBatch.fColor = fGeoData[0].fColor;
683 fBatch.fStroke = fGeoData[0].fStroke;
684 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
685 fBatch.fCoverageIgnored = init.fCoverageIgnored;
686 }
687
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)688 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
689 SkMatrix invert;
690 if (!this->viewMatrix().invert(&invert)) {
691 return;
692 }
693
694 // Setup geometry processor
695 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
696 this->stroke(),
697 invert));
698
699 batchTarget->initDraw(gp, pipeline);
700
701 // TODO this is hacky, but the only way we have to initialize the GP is to use the
702 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
703 // everywhere we can remove this nastiness
704 GrPipelineInfo init;
705 init.fColorIgnored = fBatch.fColorIgnored;
706 init.fOverrideColor = GrColor_ILLEGAL;
707 init.fCoverageIgnored = fBatch.fCoverageIgnored;
708 init.fUsesLocalCoords = this->usesLocalCoords();
709 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
710
711 int instanceCount = fGeoData.count();
712 size_t vertexStride = gp->getVertexStride();
713 SkASSERT(vertexStride == sizeof(CircleVertex));
714 QuadHelper helper;
715 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, vertexStride,
716 instanceCount));
717 if (!verts) {
718 return;
719 }
720
721 for (int i = 0; i < instanceCount; i++) {
722 Geometry& geom = fGeoData[i];
723
724 SkScalar innerRadius = geom.fInnerRadius;
725 SkScalar outerRadius = geom.fOuterRadius;
726
727 const SkRect& bounds = geom.fDevBounds;
728
729 // The inner radius in the vertex data must be specified in normalized space.
730 innerRadius = innerRadius / outerRadius;
731 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
732 verts[0].fOffset = SkPoint::Make(-1, -1);
733 verts[0].fOuterRadius = outerRadius;
734 verts[0].fInnerRadius = innerRadius;
735
736 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
737 verts[1].fOffset = SkPoint::Make(-1, 1);
738 verts[1].fOuterRadius = outerRadius;
739 verts[1].fInnerRadius = innerRadius;
740
741 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
742 verts[2].fOffset = SkPoint::Make(1, 1);
743 verts[2].fOuterRadius = outerRadius;
744 verts[2].fInnerRadius = innerRadius;
745
746 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
747 verts[3].fOffset = SkPoint::Make(1, -1);
748 verts[3].fOuterRadius = outerRadius;
749 verts[3].fInnerRadius = innerRadius;
750
751 verts += kVerticesPerQuad;
752 }
753 helper.issueDraw(batchTarget);
754 }
755
geoData()756 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
757
758 private:
CircleBatch(const Geometry & geometry)759 CircleBatch(const Geometry& geometry) {
760 this->initClassID<CircleBatch>();
761 fGeoData.push_back(geometry);
762
763 this->setBounds(geometry.fDevBounds);
764 }
765
onCombineIfPossible(GrBatch * t)766 bool onCombineIfPossible(GrBatch* t) override {
767 CircleBatch* that = t->cast<CircleBatch>();
768
769 // TODO use vertex color to avoid breaking batches
770 if (this->color() != that->color()) {
771 return false;
772 }
773
774 if (this->stroke() != that->stroke()) {
775 return false;
776 }
777
778 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
779 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
780 return false;
781 }
782
783 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
784 this->joinBounds(that->bounds());
785 return true;
786 }
787
color() const788 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const789 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const790 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const791 bool stroke() const { return fBatch.fStroke; }
792
793 struct BatchTracker {
794 GrColor fColor;
795 bool fStroke;
796 bool fUsesLocalCoords;
797 bool fColorIgnored;
798 bool fCoverageIgnored;
799 };
800
801 BatchTracker fBatch;
802 SkSTArray<1, Geometry, true> fGeoData;
803 };
804
create_circle_batch(GrColor color,const SkMatrix & viewMatrix,bool useCoverageAA,const SkRect & circle,const SkStrokeRec & stroke)805 static GrBatch* create_circle_batch(GrColor color,
806 const SkMatrix& viewMatrix,
807 bool useCoverageAA,
808 const SkRect& circle,
809 const SkStrokeRec& stroke) {
810 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
811 viewMatrix.mapPoints(¢er, 1);
812 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
813 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
814
815 SkStrokeRec::Style style = stroke.getStyle();
816 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
817 SkStrokeRec::kHairline_Style == style;
818 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
819
820 SkScalar innerRadius = 0.0f;
821 SkScalar outerRadius = radius;
822 SkScalar halfWidth = 0;
823 if (hasStroke) {
824 if (SkScalarNearlyZero(strokeWidth)) {
825 halfWidth = SK_ScalarHalf;
826 } else {
827 halfWidth = SkScalarHalf(strokeWidth);
828 }
829
830 outerRadius += halfWidth;
831 if (isStrokeOnly) {
832 innerRadius = radius - halfWidth;
833 }
834 }
835
836 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
837 // computation because the computed alpha is zero, rather than 50%, at the radius.
838 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
839 // and the outset ensures the box will cover all partially covered by the circle.
840 outerRadius += SK_ScalarHalf;
841 innerRadius -= SK_ScalarHalf;
842
843 CircleBatch::Geometry geometry;
844 geometry.fViewMatrix = viewMatrix;
845 geometry.fColor = color;
846 geometry.fInnerRadius = innerRadius;
847 geometry.fOuterRadius = outerRadius;
848 geometry.fStroke = isStrokeOnly && innerRadius > 0;
849 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
850 center.fX + outerRadius, center.fY + outerRadius);
851
852 return CircleBatch::Create(geometry);
853 }
854
drawCircle(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,bool useCoverageAA,const SkRect & circle,const SkStrokeRec & stroke)855 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
856 GrPipelineBuilder* pipelineBuilder,
857 GrColor color,
858 const SkMatrix& viewMatrix,
859 bool useCoverageAA,
860 const SkRect& circle,
861 const SkStrokeRec& stroke) {
862 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
863 stroke));
864 target->drawBatch(pipelineBuilder, batch);
865 }
866
867 ///////////////////////////////////////////////////////////////////////////////
868
869 class EllipseBatch : public GrBatch {
870 public:
871 struct Geometry {
872 GrColor fColor;
873 SkMatrix fViewMatrix;
874 SkScalar fXRadius;
875 SkScalar fYRadius;
876 SkScalar fInnerXRadius;
877 SkScalar fInnerYRadius;
878 bool fStroke;
879 SkRect fDevBounds;
880 };
881
Create(const Geometry & geometry)882 static GrBatch* Create(const Geometry& geometry) {
883 return SkNEW_ARGS(EllipseBatch, (geometry));
884 }
885
name() const886 const char* name() const override { return "EllipseBatch"; }
887
getInvariantOutputColor(GrInitInvariantOutput * out) const888 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
889 // When this is called on a batch, there is only one geometry bundle
890 out->setKnownFourComponents(fGeoData[0].fColor);
891 }
getInvariantOutputCoverage(GrInitInvariantOutput * out) const892 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
893 out->setUnknownSingleComponent();
894 }
895
initBatchTracker(const GrPipelineInfo & init)896 void initBatchTracker(const GrPipelineInfo& init) override {
897 // Handle any color overrides
898 if (init.fColorIgnored) {
899 fGeoData[0].fColor = GrColor_ILLEGAL;
900 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
901 fGeoData[0].fColor = init.fOverrideColor;
902 }
903
904 // setup batch properties
905 fBatch.fColorIgnored = init.fColorIgnored;
906 fBatch.fColor = fGeoData[0].fColor;
907 fBatch.fStroke = fGeoData[0].fStroke;
908 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
909 fBatch.fCoverageIgnored = init.fCoverageIgnored;
910 }
911
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)912 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
913 SkMatrix invert;
914 if (!this->viewMatrix().invert(&invert)) {
915 return;
916 }
917
918 // Setup geometry processor
919 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
920 this->stroke(),
921 invert));
922
923 batchTarget->initDraw(gp, pipeline);
924
925 // TODO this is hacky, but the only way we have to initialize the GP is to use the
926 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
927 // everywhere we can remove this nastiness
928 GrPipelineInfo init;
929 init.fColorIgnored = fBatch.fColorIgnored;
930 init.fOverrideColor = GrColor_ILLEGAL;
931 init.fCoverageIgnored = fBatch.fCoverageIgnored;
932 init.fUsesLocalCoords = this->usesLocalCoords();
933 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
934
935 int instanceCount = fGeoData.count();
936 QuadHelper helper;
937 size_t vertexStride = gp->getVertexStride();
938 SkASSERT(vertexStride == sizeof(EllipseVertex));
939 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
940 helper.init(batchTarget, vertexStride, instanceCount));
941 if (!verts) {
942 return;
943 }
944
945 for (int i = 0; i < instanceCount; i++) {
946 Geometry& geom = fGeoData[i];
947
948 SkScalar xRadius = geom.fXRadius;
949 SkScalar yRadius = geom.fYRadius;
950
951 // Compute the reciprocals of the radii here to save time in the shader
952 SkScalar xRadRecip = SkScalarInvert(xRadius);
953 SkScalar yRadRecip = SkScalarInvert(yRadius);
954 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
955 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
956
957 const SkRect& bounds = geom.fDevBounds;
958
959 // The inner radius in the vertex data must be specified in normalized space.
960 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
961 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
962 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
963 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
964
965 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
966 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
967 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
968 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
969
970 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
971 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
972 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
973 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
974
975 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
976 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
977 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
978 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
979
980 verts += kVerticesPerQuad;
981 }
982 helper.issueDraw(batchTarget);
983 }
984
geoData()985 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
986
987 private:
EllipseBatch(const Geometry & geometry)988 EllipseBatch(const Geometry& geometry) {
989 this->initClassID<EllipseBatch>();
990 fGeoData.push_back(geometry);
991
992 this->setBounds(geometry.fDevBounds);
993 }
994
onCombineIfPossible(GrBatch * t)995 bool onCombineIfPossible(GrBatch* t) override {
996 EllipseBatch* that = t->cast<EllipseBatch>();
997
998 // TODO use vertex color to avoid breaking batches
999 if (this->color() != that->color()) {
1000 return false;
1001 }
1002
1003 if (this->stroke() != that->stroke()) {
1004 return false;
1005 }
1006
1007 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1008 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1009 return false;
1010 }
1011
1012 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1013 this->joinBounds(that->bounds());
1014 return true;
1015 }
1016
color() const1017 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1018 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1019 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1020 bool stroke() const { return fBatch.fStroke; }
1021
1022 struct BatchTracker {
1023 GrColor fColor;
1024 bool fStroke;
1025 bool fUsesLocalCoords;
1026 bool fColorIgnored;
1027 bool fCoverageIgnored;
1028 };
1029
1030 BatchTracker fBatch;
1031 SkSTArray<1, Geometry, true> fGeoData;
1032 };
1033
create_ellipse_batch(GrColor color,const SkMatrix & viewMatrix,bool useCoverageAA,const SkRect & ellipse,const SkStrokeRec & stroke)1034 static GrBatch* create_ellipse_batch(GrColor color,
1035 const SkMatrix& viewMatrix,
1036 bool useCoverageAA,
1037 const SkRect& ellipse,
1038 const SkStrokeRec& stroke) {
1039 #ifdef SK_DEBUG
1040 {
1041 // we should have checked for this previously
1042 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
1043 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
1044 }
1045 #endif
1046
1047 // do any matrix crunching before we reset the draw state for device coords
1048 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1049 viewMatrix.mapPoints(¢er, 1);
1050 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1051 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1052 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1053 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1054 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1055 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
1056
1057 // do (potentially) anisotropic mapping of stroke
1058 SkVector scaledStroke;
1059 SkScalar strokeWidth = stroke.getWidth();
1060 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1061 viewMatrix[SkMatrix::kMSkewY]));
1062 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1063 viewMatrix[SkMatrix::kMScaleY]));
1064
1065 SkStrokeRec::Style style = stroke.getStyle();
1066 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1067 SkStrokeRec::kHairline_Style == style;
1068 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1069
1070 SkScalar innerXRadius = 0;
1071 SkScalar innerYRadius = 0;
1072 if (hasStroke) {
1073 if (SkScalarNearlyZero(scaledStroke.length())) {
1074 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1075 } else {
1076 scaledStroke.scale(SK_ScalarHalf);
1077 }
1078
1079 // we only handle thick strokes for near-circular ellipses
1080 if (scaledStroke.length() > SK_ScalarHalf &&
1081 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1082 return NULL;
1083 }
1084
1085 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1086 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1087 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1088 return NULL;
1089 }
1090
1091 // this is legit only if scale & translation (which should be the case at the moment)
1092 if (isStrokeOnly) {
1093 innerXRadius = xRadius - scaledStroke.fX;
1094 innerYRadius = yRadius - scaledStroke.fY;
1095 }
1096
1097 xRadius += scaledStroke.fX;
1098 yRadius += scaledStroke.fY;
1099 }
1100
1101 // We've extended the outer x radius out half a pixel to antialias.
1102 // This will also expand the rect so all the pixels will be captured.
1103 // TODO: Consider if we should use sqrt(2)/2 instead
1104 xRadius += SK_ScalarHalf;
1105 yRadius += SK_ScalarHalf;
1106
1107 EllipseBatch::Geometry geometry;
1108 geometry.fViewMatrix = viewMatrix;
1109 geometry.fColor = color;
1110 geometry.fXRadius = xRadius;
1111 geometry.fYRadius = yRadius;
1112 geometry.fInnerXRadius = innerXRadius;
1113 geometry.fInnerYRadius = innerYRadius;
1114 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1115 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1116 center.fX + xRadius, center.fY + yRadius);
1117
1118 return EllipseBatch::Create(geometry);
1119 }
1120
drawEllipse(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,bool useCoverageAA,const SkRect & ellipse,const SkStrokeRec & stroke)1121 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1122 GrPipelineBuilder* pipelineBuilder,
1123 GrColor color,
1124 const SkMatrix& viewMatrix,
1125 bool useCoverageAA,
1126 const SkRect& ellipse,
1127 const SkStrokeRec& stroke) {
1128 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1129 stroke));
1130 if (!batch) {
1131 return false;
1132 }
1133
1134 target->drawBatch(pipelineBuilder, batch);
1135 return true;
1136 }
1137
1138 /////////////////////////////////////////////////////////////////////////////////////////////////
1139
1140 class DIEllipseBatch : public GrBatch {
1141 public:
1142 struct Geometry {
1143 GrColor fColor;
1144 SkMatrix fViewMatrix;
1145 SkScalar fXRadius;
1146 SkScalar fYRadius;
1147 SkScalar fInnerXRadius;
1148 SkScalar fInnerYRadius;
1149 SkScalar fGeoDx;
1150 SkScalar fGeoDy;
1151 DIEllipseEdgeEffect::Mode fMode;
1152 SkRect fBounds;
1153 };
1154
Create(const Geometry & geometry,const SkRect & bounds)1155 static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1156 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
1157 }
1158
name() const1159 const char* name() const override { return "DIEllipseBatch"; }
1160
getInvariantOutputColor(GrInitInvariantOutput * out) const1161 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1162 // When this is called on a batch, there is only one geometry bundle
1163 out->setKnownFourComponents(fGeoData[0].fColor);
1164 }
getInvariantOutputCoverage(GrInitInvariantOutput * out) const1165 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
1166 out->setUnknownSingleComponent();
1167 }
1168
initBatchTracker(const GrPipelineInfo & init)1169 void initBatchTracker(const GrPipelineInfo& init) override {
1170 // Handle any color overrides
1171 if (init.fColorIgnored) {
1172 fGeoData[0].fColor = GrColor_ILLEGAL;
1173 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1174 fGeoData[0].fColor = init.fOverrideColor;
1175 }
1176
1177 // setup batch properties
1178 fBatch.fColorIgnored = init.fColorIgnored;
1179 fBatch.fColor = fGeoData[0].fColor;
1180 fBatch.fMode = fGeoData[0].fMode;
1181 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1182 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1183 }
1184
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)1185 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1186 // Setup geometry processor
1187 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1188 this->viewMatrix(),
1189 this->mode()));
1190
1191 batchTarget->initDraw(gp, pipeline);
1192
1193 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1194 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1195 // everywhere we can remove this nastiness
1196 GrPipelineInfo init;
1197 init.fColorIgnored = fBatch.fColorIgnored;
1198 init.fOverrideColor = GrColor_ILLEGAL;
1199 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1200 init.fUsesLocalCoords = this->usesLocalCoords();
1201 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1202
1203 int instanceCount = fGeoData.count();
1204 size_t vertexStride = gp->getVertexStride();
1205 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1206 QuadHelper helper;
1207 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1208 helper.init(batchTarget, vertexStride, instanceCount));
1209 if (!verts) {
1210 return;
1211 }
1212
1213 for (int i = 0; i < instanceCount; i++) {
1214 Geometry& geom = fGeoData[i];
1215
1216 SkScalar xRadius = geom.fXRadius;
1217 SkScalar yRadius = geom.fYRadius;
1218
1219 const SkRect& bounds = geom.fBounds;
1220
1221 // This adjusts the "radius" to include the half-pixel border
1222 SkScalar offsetDx = geom.fGeoDx / xRadius;
1223 SkScalar offsetDy = geom.fGeoDy / yRadius;
1224
1225 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1226 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
1227
1228 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1229 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1230 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1231
1232 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1233 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1234 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1235
1236 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1237 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1238 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1239
1240 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1241 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1242 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1243
1244 verts += kVerticesPerQuad;
1245 }
1246 helper.issueDraw(batchTarget);
1247 }
1248
geoData()1249 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1250
1251 private:
DIEllipseBatch(const Geometry & geometry,const SkRect & bounds)1252 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
1253 this->initClassID<DIEllipseBatch>();
1254 fGeoData.push_back(geometry);
1255
1256 this->setBounds(bounds);
1257 }
1258
onCombineIfPossible(GrBatch * t)1259 bool onCombineIfPossible(GrBatch* t) override {
1260 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1261
1262 // TODO use vertex color to avoid breaking batches
1263 if (this->color() != that->color()) {
1264 return false;
1265 }
1266
1267 if (this->mode() != that->mode()) {
1268 return false;
1269 }
1270
1271 // TODO rewrite to allow positioning on CPU
1272 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1273 return false;
1274 }
1275
1276 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1277 this->joinBounds(that->bounds());
1278 return true;
1279 }
1280
color() const1281 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1282 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1283 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
mode() const1284 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1285
1286 struct BatchTracker {
1287 GrColor fColor;
1288 DIEllipseEdgeEffect::Mode fMode;
1289 bool fUsesLocalCoords;
1290 bool fColorIgnored;
1291 bool fCoverageIgnored;
1292 };
1293
1294 BatchTracker fBatch;
1295 SkSTArray<1, Geometry, true> fGeoData;
1296 };
1297
create_diellipse_batch(GrColor color,const SkMatrix & viewMatrix,bool useCoverageAA,const SkRect & ellipse,const SkStrokeRec & stroke)1298 static GrBatch* create_diellipse_batch(GrColor color,
1299 const SkMatrix& viewMatrix,
1300 bool useCoverageAA,
1301 const SkRect& ellipse,
1302 const SkStrokeRec& stroke) {
1303 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1304 SkScalar xRadius = SkScalarHalf(ellipse.width());
1305 SkScalar yRadius = SkScalarHalf(ellipse.height());
1306
1307 SkStrokeRec::Style style = stroke.getStyle();
1308 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
1309 DIEllipseEdgeEffect::kStroke :
1310 (SkStrokeRec::kHairline_Style == style) ?
1311 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1312
1313 SkScalar innerXRadius = 0;
1314 SkScalar innerYRadius = 0;
1315 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1316 SkScalar strokeWidth = stroke.getWidth();
1317
1318 if (SkScalarNearlyZero(strokeWidth)) {
1319 strokeWidth = SK_ScalarHalf;
1320 } else {
1321 strokeWidth *= SK_ScalarHalf;
1322 }
1323
1324 // we only handle thick strokes for near-circular ellipses
1325 if (strokeWidth > SK_ScalarHalf &&
1326 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1327 return NULL;
1328 }
1329
1330 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1331 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1332 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1333 return NULL;
1334 }
1335
1336 // set inner radius (if needed)
1337 if (SkStrokeRec::kStroke_Style == style) {
1338 innerXRadius = xRadius - strokeWidth;
1339 innerYRadius = yRadius - strokeWidth;
1340 }
1341
1342 xRadius += strokeWidth;
1343 yRadius += strokeWidth;
1344 }
1345 if (DIEllipseEdgeEffect::kStroke == mode) {
1346 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1347 DIEllipseEdgeEffect::kFill;
1348 }
1349
1350 // This expands the outer rect so that after CTM we end up with a half-pixel border
1351 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1352 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1353 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1354 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1355 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1356 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1357
1358 DIEllipseBatch::Geometry geometry;
1359 geometry.fViewMatrix = viewMatrix;
1360 geometry.fColor = color;
1361 geometry.fXRadius = xRadius;
1362 geometry.fYRadius = yRadius;
1363 geometry.fInnerXRadius = innerXRadius;
1364 geometry.fInnerYRadius = innerYRadius;
1365 geometry.fGeoDx = geoDx;
1366 geometry.fGeoDy = geoDy;
1367 geometry.fMode = mode;
1368 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1369 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
1370
1371 SkRect devBounds = geometry.fBounds;
1372 viewMatrix.mapRect(&devBounds);
1373 return DIEllipseBatch::Create(geometry, devBounds);
1374 }
1375
drawDIEllipse(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,bool useCoverageAA,const SkRect & ellipse,const SkStrokeRec & stroke)1376 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1377 GrPipelineBuilder* pipelineBuilder,
1378 GrColor color,
1379 const SkMatrix& viewMatrix,
1380 bool useCoverageAA,
1381 const SkRect& ellipse,
1382 const SkStrokeRec& stroke) {
1383 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1384 stroke));
1385 if (!batch) {
1386 return false;
1387 }
1388 target->drawBatch(pipelineBuilder, batch);
1389 return true;
1390 }
1391
1392 ///////////////////////////////////////////////////////////////////////////////
1393
1394 static const uint16_t gRRectIndices[] = {
1395 // corners
1396 0, 1, 5, 0, 5, 4,
1397 2, 3, 7, 2, 7, 6,
1398 8, 9, 13, 8, 13, 12,
1399 10, 11, 15, 10, 15, 14,
1400
1401 // edges
1402 1, 2, 6, 1, 6, 5,
1403 4, 5, 9, 4, 9, 8,
1404 6, 7, 11, 6, 11, 10,
1405 9, 10, 14, 9, 14, 13,
1406
1407 // center
1408 // we place this at the end so that we can ignore these indices when rendering stroke-only
1409 5, 6, 10, 5, 10, 9
1410 };
1411
1412 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1413 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1414 static const int kVertsPerRRect = 16;
1415 static const int kNumRRectsInIndexBuffer = 256;
1416
1417 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1418 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
ref_rrect_index_buffer(bool strokeOnly,GrResourceProvider * resourceProvider)1419 static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1420 GrResourceProvider* resourceProvider) {
1421 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1422 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1423 if (strokeOnly) {
1424 return resourceProvider->refOrCreateInstancedIndexBuffer(
1425 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1426 gStrokeRRectOnlyIndexBufferKey);
1427 } else {
1428 return resourceProvider->refOrCreateInstancedIndexBuffer(
1429 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1430 gRRectOnlyIndexBufferKey);
1431
1432 }
1433 }
1434
drawDRRect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,bool useAA,const SkRRect & origOuter,const SkRRect & origInner)1435 bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
1436 GrPipelineBuilder* pipelineBuilder,
1437 GrColor color,
1438 const SkMatrix& viewMatrix,
1439 bool useAA,
1440 const SkRRect& origOuter,
1441 const SkRRect& origInner) {
1442 bool applyAA = useAA &&
1443 !pipelineBuilder->getRenderTarget()->isMultisampled();
1444 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
1445 if (!origInner.isEmpty()) {
1446 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
1447 if (!viewMatrix.isIdentity()) {
1448 if (!origInner.transform(viewMatrix, inner.writable())) {
1449 return false;
1450 }
1451 }
1452 GrPrimitiveEdgeType edgeType = applyAA ?
1453 kInverseFillAA_GrProcessorEdgeType :
1454 kInverseFillBW_GrProcessorEdgeType;
1455 // TODO this needs to be a geometry processor
1456 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1457 if (NULL == fp) {
1458 return false;
1459 }
1460 arfp.set(pipelineBuilder);
1461 pipelineBuilder->addCoverageProcessor(fp)->unref();
1462 }
1463
1464 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
1465 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
1466 return true;
1467 }
1468
1469 SkASSERT(!origOuter.isEmpty());
1470 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
1471 if (!viewMatrix.isIdentity()) {
1472 if (!origOuter.transform(viewMatrix, outer.writable())) {
1473 return false;
1474 }
1475 }
1476 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
1477 kFillBW_GrProcessorEdgeType;
1478 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
1479 if (NULL == effect) {
1480 return false;
1481 }
1482 if (!arfp.isSet()) {
1483 arfp.set(pipelineBuilder);
1484 }
1485
1486 SkMatrix invert;
1487 if (!viewMatrix.invert(&invert)) {
1488 return false;
1489 }
1490
1491 pipelineBuilder->addCoverageProcessor(effect)->unref();
1492 SkRect bounds = outer->getBounds();
1493 if (applyAA) {
1494 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1495 }
1496 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
1497 return true;
1498 }
1499
1500 ///////////////////////////////////////////////////////////////////////////////////////////////////
1501
1502 class RRectCircleRendererBatch : public GrBatch {
1503 public:
1504 struct Geometry {
1505 GrColor fColor;
1506 SkMatrix fViewMatrix;
1507 SkScalar fInnerRadius;
1508 SkScalar fOuterRadius;
1509 bool fStroke;
1510 SkRect fDevBounds;
1511 };
1512
Create(const Geometry & geometry)1513 static GrBatch* Create(const Geometry& geometry) {
1514 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
1515 }
1516
name() const1517 const char* name() const override { return "RRectCircleBatch"; }
1518
getInvariantOutputColor(GrInitInvariantOutput * out) const1519 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1520 // When this is called on a batch, there is only one geometry bundle
1521 out->setKnownFourComponents(fGeoData[0].fColor);
1522 }
getInvariantOutputCoverage(GrInitInvariantOutput * out) const1523 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
1524 out->setUnknownSingleComponent();
1525 }
1526
initBatchTracker(const GrPipelineInfo & init)1527 void initBatchTracker(const GrPipelineInfo& init) override {
1528 // Handle any color overrides
1529 if (init.fColorIgnored) {
1530 fGeoData[0].fColor = GrColor_ILLEGAL;
1531 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1532 fGeoData[0].fColor = init.fOverrideColor;
1533 }
1534
1535 // setup batch properties
1536 fBatch.fColorIgnored = init.fColorIgnored;
1537 fBatch.fColor = fGeoData[0].fColor;
1538 fBatch.fStroke = fGeoData[0].fStroke;
1539 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1540 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1541 }
1542
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)1543 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1544 // reset to device coordinates
1545 SkMatrix invert;
1546 if (!this->viewMatrix().invert(&invert)) {
1547 SkDebugf("Failed to invert\n");
1548 return;
1549 }
1550
1551 // Setup geometry processor
1552 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1553 this->stroke(),
1554 invert));
1555
1556 batchTarget->initDraw(gp, pipeline);
1557
1558 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1559 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1560 // everywhere we can remove this nastiness
1561 GrPipelineInfo init;
1562 init.fColorIgnored = fBatch.fColorIgnored;
1563 init.fOverrideColor = GrColor_ILLEGAL;
1564 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1565 init.fUsesLocalCoords = this->usesLocalCoords();
1566 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1567
1568 int instanceCount = fGeoData.count();
1569 size_t vertexStride = gp->getVertexStride();
1570 SkASSERT(vertexStride == sizeof(CircleVertex));
1571
1572 // drop out the middle quad if we're stroked
1573 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1574 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1575 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
1576
1577 InstancedHelper helper;
1578 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget,
1579 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1580 indicesPerInstance, instanceCount));
1581 if (!verts || !indexBuffer) {
1582 SkDebugf("Could not allocate vertices\n");
1583 return;
1584 }
1585
1586 for (int i = 0; i < instanceCount; i++) {
1587 Geometry& args = fGeoData[i];
1588
1589 SkScalar outerRadius = args.fOuterRadius;
1590
1591 const SkRect& bounds = args.fDevBounds;
1592
1593 SkScalar yCoords[4] = {
1594 bounds.fTop,
1595 bounds.fTop + outerRadius,
1596 bounds.fBottom - outerRadius,
1597 bounds.fBottom
1598 };
1599
1600 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1601 // The inner radius in the vertex data must be specified in normalized space.
1602 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1603 for (int i = 0; i < 4; ++i) {
1604 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1605 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1606 verts->fOuterRadius = outerRadius;
1607 verts->fInnerRadius = innerRadius;
1608 verts++;
1609
1610 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1611 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1612 verts->fOuterRadius = outerRadius;
1613 verts->fInnerRadius = innerRadius;
1614 verts++;
1615
1616 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1617 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1618 verts->fOuterRadius = outerRadius;
1619 verts->fInnerRadius = innerRadius;
1620 verts++;
1621
1622 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1623 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1624 verts->fOuterRadius = outerRadius;
1625 verts->fInnerRadius = innerRadius;
1626 verts++;
1627 }
1628 }
1629
1630 helper.issueDraw(batchTarget);
1631 }
1632
geoData()1633 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1634
1635 private:
RRectCircleRendererBatch(const Geometry & geometry)1636 RRectCircleRendererBatch(const Geometry& geometry) {
1637 this->initClassID<RRectCircleRendererBatch>();
1638 fGeoData.push_back(geometry);
1639
1640 this->setBounds(geometry.fDevBounds);
1641 }
1642
onCombineIfPossible(GrBatch * t)1643 bool onCombineIfPossible(GrBatch* t) override {
1644 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1645
1646 // TODO use vertex color to avoid breaking batches
1647 if (this->color() != that->color()) {
1648 return false;
1649 }
1650
1651 if (this->stroke() != that->stroke()) {
1652 return false;
1653 }
1654
1655 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1656 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1657 return false;
1658 }
1659
1660 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1661 this->joinBounds(that->bounds());
1662 return true;
1663 }
1664
color() const1665 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1666 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1667 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1668 bool stroke() const { return fBatch.fStroke; }
1669
1670 struct BatchTracker {
1671 GrColor fColor;
1672 bool fStroke;
1673 bool fUsesLocalCoords;
1674 bool fColorIgnored;
1675 bool fCoverageIgnored;
1676 };
1677
1678 BatchTracker fBatch;
1679 SkSTArray<1, Geometry, true> fGeoData;
1680 };
1681
1682 class RRectEllipseRendererBatch : public GrBatch {
1683 public:
1684 struct Geometry {
1685 GrColor fColor;
1686 SkMatrix fViewMatrix;
1687 SkScalar fXRadius;
1688 SkScalar fYRadius;
1689 SkScalar fInnerXRadius;
1690 SkScalar fInnerYRadius;
1691 bool fStroke;
1692 SkRect fDevBounds;
1693 };
1694
Create(const Geometry & geometry)1695 static GrBatch* Create(const Geometry& geometry) {
1696 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
1697 }
1698
name() const1699 const char* name() const override { return "RRectEllipseRendererBatch"; }
1700
getInvariantOutputColor(GrInitInvariantOutput * out) const1701 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
1702 // When this is called on a batch, there is only one geometry bundle
1703 out->setKnownFourComponents(fGeoData[0].fColor);
1704 }
getInvariantOutputCoverage(GrInitInvariantOutput * out) const1705 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
1706 out->setUnknownSingleComponent();
1707 }
1708
initBatchTracker(const GrPipelineInfo & init)1709 void initBatchTracker(const GrPipelineInfo& init) override {
1710 // Handle any color overrides
1711 if (init.fColorIgnored) {
1712 fGeoData[0].fColor = GrColor_ILLEGAL;
1713 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1714 fGeoData[0].fColor = init.fOverrideColor;
1715 }
1716
1717 // setup batch properties
1718 fBatch.fColorIgnored = init.fColorIgnored;
1719 fBatch.fColor = fGeoData[0].fColor;
1720 fBatch.fStroke = fGeoData[0].fStroke;
1721 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1722 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1723 }
1724
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)1725 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
1726 // reset to device coordinates
1727 SkMatrix invert;
1728 if (!this->viewMatrix().invert(&invert)) {
1729 SkDebugf("Failed to invert\n");
1730 return;
1731 }
1732
1733 // Setup geometry processor
1734 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1735 this->stroke(),
1736 invert));
1737
1738 batchTarget->initDraw(gp, pipeline);
1739
1740 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1741 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1742 // everywhere we can remove this nastiness
1743 GrPipelineInfo init;
1744 init.fColorIgnored = fBatch.fColorIgnored;
1745 init.fOverrideColor = GrColor_ILLEGAL;
1746 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1747 init.fUsesLocalCoords = this->usesLocalCoords();
1748 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1749
1750 int instanceCount = fGeoData.count();
1751 size_t vertexStride = gp->getVertexStride();
1752 SkASSERT(vertexStride == sizeof(EllipseVertex));
1753
1754 // drop out the middle quad if we're stroked
1755 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
1756 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1757 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
1758
1759 InstancedHelper helper;
1760 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1761 helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1762 kVertsPerRRect, indicesPerInstance, instanceCount));
1763 if (!verts || !indexBuffer) {
1764 SkDebugf("Could not allocate vertices\n");
1765 return;
1766 }
1767
1768 for (int i = 0; i < instanceCount; i++) {
1769 Geometry& args = fGeoData[i];
1770
1771 // Compute the reciprocals of the radii here to save time in the shader
1772 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1773 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1774 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1775 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1776
1777 // Extend the radii out half a pixel to antialias.
1778 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1779 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1780
1781 const SkRect& bounds = args.fDevBounds;
1782
1783 SkScalar yCoords[4] = {
1784 bounds.fTop,
1785 bounds.fTop + yOuterRadius,
1786 bounds.fBottom - yOuterRadius,
1787 bounds.fBottom
1788 };
1789 SkScalar yOuterOffsets[4] = {
1790 yOuterRadius,
1791 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1792 SK_ScalarNearlyZero,
1793 yOuterRadius
1794 };
1795
1796 for (int i = 0; i < 4; ++i) {
1797 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1798 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1799 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1800 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1801 verts++;
1802
1803 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1804 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1805 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1806 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1807 verts++;
1808
1809 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1810 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1811 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1812 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1813 verts++;
1814
1815 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1816 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1817 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1818 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1819 verts++;
1820 }
1821 }
1822 helper.issueDraw(batchTarget);
1823 }
1824
geoData()1825 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1826
1827 private:
RRectEllipseRendererBatch(const Geometry & geometry)1828 RRectEllipseRendererBatch(const Geometry& geometry) {
1829 this->initClassID<RRectEllipseRendererBatch>();
1830 fGeoData.push_back(geometry);
1831
1832 this->setBounds(geometry.fDevBounds);
1833 }
1834
onCombineIfPossible(GrBatch * t)1835 bool onCombineIfPossible(GrBatch* t) override {
1836 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1837
1838 // TODO use vertex color to avoid breaking batches
1839 if (this->color() != that->color()) {
1840 return false;
1841 }
1842
1843 if (this->stroke() != that->stroke()) {
1844 return false;
1845 }
1846
1847 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1848 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1849 return false;
1850 }
1851
1852 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1853 this->joinBounds(that->bounds());
1854 return true;
1855 }
1856
color() const1857 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const1858 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const1859 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
stroke() const1860 bool stroke() const { return fBatch.fStroke; }
1861
1862 struct BatchTracker {
1863 GrColor fColor;
1864 bool fStroke;
1865 bool fUsesLocalCoords;
1866 bool fColorIgnored;
1867 bool fCoverageIgnored;
1868 };
1869
1870 BatchTracker fBatch;
1871 SkSTArray<1, Geometry, true> fGeoData;
1872 };
1873
create_rrect_batch(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkStrokeRec & stroke)1874 static GrBatch* create_rrect_batch(GrColor color,
1875 const SkMatrix& viewMatrix,
1876 const SkRRect& rrect,
1877 const SkStrokeRec& stroke) {
1878 SkASSERT(viewMatrix.rectStaysRect());
1879 SkASSERT(rrect.isSimple());
1880 SkASSERT(!rrect.isOval());
1881
1882 // RRect batchs only handle simple, but not too simple, rrects
1883 // do any matrix crunching before we reset the draw state for device coords
1884 const SkRect& rrectBounds = rrect.getBounds();
1885 SkRect bounds;
1886 viewMatrix.mapRect(&bounds, rrectBounds);
1887
1888 SkVector radii = rrect.getSimpleRadii();
1889 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1890 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1891 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1892 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
1893
1894 SkStrokeRec::Style style = stroke.getStyle();
1895
1896 // do (potentially) anisotropic mapping of stroke
1897 SkVector scaledStroke;
1898 SkScalar strokeWidth = stroke.getWidth();
1899
1900 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1901 SkStrokeRec::kHairline_Style == style;
1902 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1903
1904 if (hasStroke) {
1905 if (SkStrokeRec::kHairline_Style == style) {
1906 scaledStroke.set(1, 1);
1907 } else {
1908 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1909 viewMatrix[SkMatrix::kMSkewY]));
1910 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1911 viewMatrix[SkMatrix::kMScaleY]));
1912 }
1913
1914 // if half of strokewidth is greater than radius, we don't handle that right now
1915 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1916 return NULL;
1917 }
1918 }
1919
1920 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1921 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1922 // patch will have fractional coverage. This only matters when the interior is actually filled.
1923 // We could consider falling back to rect rendering here, since a tiny radius is
1924 // indistinguishable from a square corner.
1925 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
1926 return NULL;
1927 }
1928
1929 // if the corners are circles, use the circle renderer
1930 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
1931 SkScalar innerRadius = 0.0f;
1932 SkScalar outerRadius = xRadius;
1933 SkScalar halfWidth = 0;
1934 if (hasStroke) {
1935 if (SkScalarNearlyZero(scaledStroke.fX)) {
1936 halfWidth = SK_ScalarHalf;
1937 } else {
1938 halfWidth = SkScalarHalf(scaledStroke.fX);
1939 }
1940
1941 if (isStrokeOnly) {
1942 innerRadius = xRadius - halfWidth;
1943 }
1944 outerRadius += halfWidth;
1945 bounds.outset(halfWidth, halfWidth);
1946 }
1947
1948 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
1949
1950 // The radii are outset for two reasons. First, it allows the shader to simply perform
1951 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1952 // Second, the outer radius is used to compute the verts of the bounding box that is
1953 // rendered and the outset ensures the box will cover all partially covered by the rrect
1954 // corners.
1955 outerRadius += SK_ScalarHalf;
1956 innerRadius -= SK_ScalarHalf;
1957
1958 // Expand the rect so all the pixels will be captured.
1959 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1960
1961 RRectCircleRendererBatch::Geometry geometry;
1962 geometry.fViewMatrix = viewMatrix;
1963 geometry.fColor = color;
1964 geometry.fInnerRadius = innerRadius;
1965 geometry.fOuterRadius = outerRadius;
1966 geometry.fStroke = isStrokeOnly;
1967 geometry.fDevBounds = bounds;
1968
1969 return RRectCircleRendererBatch::Create(geometry);
1970 // otherwise we use the ellipse renderer
1971 } else {
1972 SkScalar innerXRadius = 0.0f;
1973 SkScalar innerYRadius = 0.0f;
1974 if (hasStroke) {
1975 if (SkScalarNearlyZero(scaledStroke.length())) {
1976 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1977 } else {
1978 scaledStroke.scale(SK_ScalarHalf);
1979 }
1980
1981 // we only handle thick strokes for near-circular ellipses
1982 if (scaledStroke.length() > SK_ScalarHalf &&
1983 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1984 return NULL;
1985 }
1986
1987 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1988 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1989 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1990 return NULL;
1991 }
1992
1993 // this is legit only if scale & translation (which should be the case at the moment)
1994 if (isStrokeOnly) {
1995 innerXRadius = xRadius - scaledStroke.fX;
1996 innerYRadius = yRadius - scaledStroke.fY;
1997 }
1998
1999 xRadius += scaledStroke.fX;
2000 yRadius += scaledStroke.fY;
2001 bounds.outset(scaledStroke.fX, scaledStroke.fY);
2002 }
2003
2004 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
2005
2006 // Expand the rect so all the pixels will be captured.
2007 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2008
2009 RRectEllipseRendererBatch::Geometry geometry;
2010 geometry.fViewMatrix = viewMatrix;
2011 geometry.fColor = color;
2012 geometry.fXRadius = xRadius;
2013 geometry.fYRadius = yRadius;
2014 geometry.fInnerXRadius = innerXRadius;
2015 geometry.fInnerYRadius = innerYRadius;
2016 geometry.fStroke = isStrokeOnly;
2017 geometry.fDevBounds = bounds;
2018
2019 return RRectEllipseRendererBatch::Create(geometry);
2020 }
2021 }
2022
drawRRect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,bool useAA,const SkRRect & rrect,const SkStrokeRec & stroke)2023 bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2024 GrPipelineBuilder* pipelineBuilder,
2025 GrColor color,
2026 const SkMatrix& viewMatrix,
2027 bool useAA,
2028 const SkRRect& rrect,
2029 const SkStrokeRec& stroke) {
2030 if (rrect.isOval()) {
2031 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2032 stroke);
2033 }
2034
2035 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
2036
2037 // only anti-aliased rrects for now
2038 if (!useCoverageAA) {
2039 return false;
2040 }
2041
2042 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2043 return false;
2044 }
2045
2046 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
2047 if (!batch) {
2048 return false;
2049 }
2050
2051 target->drawBatch(pipelineBuilder, batch);
2052 return true;
2053 }
2054
2055 ///////////////////////////////////////////////////////////////////////////////////////////////////
2056
2057 #ifdef GR_TEST_UTILS
2058
BATCH_TEST_DEFINE(CircleBatch)2059 BATCH_TEST_DEFINE(CircleBatch) {
2060 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2061 GrColor color = GrRandomColor(random);
2062 bool useCoverageAA = random->nextBool();
2063 SkRect circle = GrTest::TestSquare(random);
2064 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2065 GrTest::TestStrokeRec(random));
2066 }
2067
BATCH_TEST_DEFINE(EllipseBatch)2068 BATCH_TEST_DEFINE(EllipseBatch) {
2069 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2070 GrColor color = GrRandomColor(random);
2071 SkRect ellipse = GrTest::TestSquare(random);
2072 return create_ellipse_batch(color, viewMatrix, true, ellipse,
2073 GrTest::TestStrokeRec(random));
2074 }
2075
BATCH_TEST_DEFINE(DIEllipseBatch)2076 BATCH_TEST_DEFINE(DIEllipseBatch) {
2077 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2078 GrColor color = GrRandomColor(random);
2079 bool useCoverageAA = random->nextBool();
2080 SkRect ellipse = GrTest::TestSquare(random);
2081 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
2082 GrTest::TestStrokeRec(random));
2083 }
2084
BATCH_TEST_DEFINE(RRectBatch)2085 BATCH_TEST_DEFINE(RRectBatch) {
2086 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2087 GrColor color = GrRandomColor(random);
2088 const SkRRect& rrect = GrTest::TestRRectSimple(random);
2089 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
2090 }
2091
2092 #endif
2093