2 * Copyright 2013 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrOvalRenderer.h"
11 #include "GrBatchTarget.h"
12 #include "GrBufferAllocPool.h"
13 #include "GrDrawTarget.h"
14 #include "GrGeometryProcessor.h"
16 #include "GrInvariantOutput.h"
17 #include "GrPipelineBuilder.h"
18 #include "GrProcessor.h"
20 #include "SkStrokeRec.h"
22 #include "effects/GrRRectEffect.h"
23 #include "gl/GrGLProcessor.h"
24 #include "gl/GrGLSL.h"
25 #include "gl/GrGLGeometryProcessor.h"
26 #include "gl/builders/GrGLProgramBuilder.h"
28 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
31 // TODO(joshualitt) add per vertex colors
35 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
39 struct EllipseVertex {
46 struct DIEllipseVertex {
52 inline bool circle_stays_circle(const SkMatrix& m) {
53 return m.isSimilarity();
58 ///////////////////////////////////////////////////////////////////////////////
61 * 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).
71 class CircleEdgeEffect : public GrGeometryProcessor {
73 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
74 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
77 const Attribute* inPosition() const { return fInPosition; }
78 const Attribute* inCircleEdge() const { return fInCircleEdge; }
79 virtual ~CircleEdgeEffect() {}
81 const char* name() const SK_OVERRIDE { return "CircleEdge"; }
83 inline bool isStroked() const { return fStroke; }
85 class GLProcessor : public GrGLGeometryProcessor {
87 GLProcessor(const GrGeometryProcessor&,
88 const GrBatchTracker&)
89 : fColor(GrColor_ILLEGAL) {}
91 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
92 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
93 GrGLGPBuilder* pb = args.fPB;
94 const BatchTracker& local = args.fBT.cast<BatchTracker>();
95 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
98 vsBuilder->emitAttributes(ce);
100 GrGLVertToFrag v(kVec4f_GrSLType);
101 args.fPB->addVarying("CircleEdge", &v);
102 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
104 // Setup pass through color
105 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
109 this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
112 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
113 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);;
115 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
116 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
117 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
118 if (ce.isStroked()) {
119 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
121 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
124 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
127 static void GenKey(const GrGeometryProcessor& gp,
128 const GrBatchTracker& bt,
130 GrProcessorKeyBuilder* b) {
131 const BatchTracker& local = bt.cast<BatchTracker>();
132 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
133 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
134 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
135 key |= ComputePosKey(gp.viewMatrix()) << 2;
136 b->add32(key << 16 | local.fInputColorType);
139 virtual void setData(const GrGLProgramDataManager& pdman,
140 const GrPrimitiveProcessor& gp,
141 const GrBatchTracker& bt) SK_OVERRIDE {
142 this->setUniformViewMatrix(pdman, gp.viewMatrix());
144 const BatchTracker& local = bt.cast<BatchTracker>();
145 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
147 GrColorToRGBAFloat(local.fColor, c);
148 pdman.set4fv(fColorUniform, 1, c);
149 fColor = local.fColor;
155 UniformHandle fColorUniform;
156 typedef GrGLGeometryProcessor INHERITED;
159 virtual void getGLProcessorKey(const GrBatchTracker& bt,
160 const GrGLCaps& caps,
161 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
162 GLProcessor::GenKey(*this, bt, caps, b);
165 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
166 const GrGLCaps&) const SK_OVERRIDE {
167 return SkNEW_ARGS(GLProcessor, (*this, bt));
170 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
171 BatchTracker* local = bt->cast<BatchTracker>();
172 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
173 local->fUsesLocalCoords = init.fUsesLocalCoords;
176 bool onCanMakeEqual(const GrBatchTracker& m,
177 const GrGeometryProcessor& that,
178 const GrBatchTracker& t) const SK_OVERRIDE {
179 const BatchTracker& mine = m.cast<BatchTracker>();
180 const BatchTracker& theirs = t.cast<BatchTracker>();
181 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
182 that, theirs.fUsesLocalCoords) &&
183 CanCombineOutput(mine.fInputColorType, mine.fColor,
184 theirs.fInputColorType, theirs.fColor);
188 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
189 : INHERITED(color, SkMatrix::I(), localMatrix) {
190 this->initClassID<CircleEdgeEffect>();
191 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
192 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
193 kVec4f_GrVertexAttribType));
197 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
198 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
199 return cee.fStroke == fStroke;
202 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
203 out->setUnknownSingleComponent();
206 struct BatchTracker {
207 GrGPInput fInputColorType;
209 bool fUsesLocalCoords;
212 const Attribute* fInPosition;
213 const Attribute* fInCircleEdge;
216 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
218 typedef GrGeometryProcessor INHERITED;
221 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
223 GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
225 const GrDrawTargetCaps&,
226 GrTexture* textures[]) {
227 return CircleEdgeEffect::Create(GrRandomColor(random),
229 GrProcessorUnitTest::TestMatrix(random));
232 ///////////////////////////////////////////////////////////////////////////////
235 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
236 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
237 * in both x and y directions.
239 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
242 class EllipseEdgeEffect : public GrGeometryProcessor {
244 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
245 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
248 virtual ~EllipseEdgeEffect() {}
250 const char* name() const SK_OVERRIDE { return "EllipseEdge"; }
252 const Attribute* inPosition() const { return fInPosition; }
253 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
254 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
256 inline bool isStroked() const { return fStroke; }
258 class GLProcessor : public GrGLGeometryProcessor {
260 GLProcessor(const GrGeometryProcessor&,
261 const GrBatchTracker&)
262 : fColor(GrColor_ILLEGAL) {}
264 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
265 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
266 GrGLGPBuilder* pb = args.fPB;
267 const BatchTracker& local = args.fBT.cast<BatchTracker>();
268 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
271 vsBuilder->emitAttributes(ee);
273 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
274 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
275 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
276 ee.inEllipseOffset()->fName);
278 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
279 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
280 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
281 ee.inEllipseRadii()->fName);
283 // Setup pass through color
284 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
288 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
291 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
292 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
295 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
296 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
297 ellipseRadii.fsIn());
298 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
299 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
300 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
302 // avoid calling inversesqrt on zero.
303 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
304 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
305 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
308 if (ee.isStroked()) {
309 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
310 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
311 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
312 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
313 ellipseRadii.fsIn());
314 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
315 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
318 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
321 static void GenKey(const GrGeometryProcessor& gp,
322 const GrBatchTracker& bt,
324 GrProcessorKeyBuilder* b) {
325 const BatchTracker& local = bt.cast<BatchTracker>();
326 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
327 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
328 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
329 key |= ComputePosKey(gp.viewMatrix()) << 2;
330 b->add32(key << 16 | local.fInputColorType);
333 virtual void setData(const GrGLProgramDataManager& pdman,
334 const GrPrimitiveProcessor& gp,
335 const GrBatchTracker& bt) SK_OVERRIDE {
336 this->setUniformViewMatrix(pdman, gp.viewMatrix());
338 const BatchTracker& local = bt.cast<BatchTracker>();
339 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
341 GrColorToRGBAFloat(local.fColor, c);
342 pdman.set4fv(fColorUniform, 1, c);
343 fColor = local.fColor;
349 UniformHandle fColorUniform;
351 typedef GrGLGeometryProcessor INHERITED;
354 virtual void getGLProcessorKey(const GrBatchTracker& bt,
355 const GrGLCaps& caps,
356 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
357 GLProcessor::GenKey(*this, bt, caps, b);
360 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
361 const GrGLCaps&) const SK_OVERRIDE {
362 return SkNEW_ARGS(GLProcessor, (*this, bt));
365 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
366 BatchTracker* local = bt->cast<BatchTracker>();
367 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
368 local->fUsesLocalCoords = init.fUsesLocalCoords;
371 bool onCanMakeEqual(const GrBatchTracker& m,
372 const GrGeometryProcessor& that,
373 const GrBatchTracker& t) const SK_OVERRIDE {
374 const BatchTracker& mine = m.cast<BatchTracker>();
375 const BatchTracker& theirs = t.cast<BatchTracker>();
376 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
377 that, theirs.fUsesLocalCoords) &&
378 CanCombineOutput(mine.fInputColorType, mine.fColor,
379 theirs.fInputColorType, theirs.fColor);
383 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
384 : INHERITED(color, SkMatrix::I(), localMatrix) {
385 this->initClassID<EllipseEdgeEffect>();
386 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
387 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
388 kVec2f_GrVertexAttribType));
389 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
390 kVec4f_GrVertexAttribType));
394 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
395 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
396 return eee.fStroke == fStroke;
399 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
400 out->setUnknownSingleComponent();
403 struct BatchTracker {
404 GrGPInput fInputColorType;
406 bool fUsesLocalCoords;
409 const Attribute* fInPosition;
410 const Attribute* fInEllipseOffset;
411 const Attribute* fInEllipseRadii;
414 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
416 typedef GrGeometryProcessor INHERITED;
419 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
421 GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
423 const GrDrawTargetCaps&,
424 GrTexture* textures[]) {
425 return EllipseEdgeEffect::Create(GrRandomColor(random),
427 GrProcessorUnitTest::TestMatrix(random));
430 ///////////////////////////////////////////////////////////////////////////////
433 * The output of this effect is a modulation of the input color and coverage for an ellipse,
434 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
435 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
436 * using differentials.
438 * The result is device-independent and can be used with any affine matrix.
441 class DIEllipseEdgeEffect : public GrGeometryProcessor {
443 enum Mode { kStroke = 0, kHairline, kFill };
445 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
446 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
449 virtual ~DIEllipseEdgeEffect() {}
451 const char* name() const SK_OVERRIDE { return "DIEllipseEdge"; }
453 const Attribute* inPosition() const { return fInPosition; }
454 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
455 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
457 inline Mode getMode() const { return fMode; }
459 class GLProcessor : public GrGLGeometryProcessor {
461 GLProcessor(const GrGeometryProcessor&,
462 const GrBatchTracker&)
463 : fColor(GrColor_ILLEGAL) {}
465 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
466 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
467 GrGLGPBuilder* pb = args.fPB;
468 const BatchTracker& local = args.fBT.cast<BatchTracker>();
469 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
472 vsBuilder->emitAttributes(ee);
474 GrGLVertToFrag offsets0(kVec2f_GrSLType);
475 args.fPB->addVarying("EllipseOffsets0", &offsets0);
476 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
477 ee.inEllipseOffsets0()->fName);
479 GrGLVertToFrag offsets1(kVec2f_GrSLType);
480 args.fPB->addVarying("EllipseOffsets1", &offsets1);
481 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
482 ee.inEllipseOffsets1()->fName);
484 // Setup pass through color
485 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
489 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
492 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
493 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
495 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
496 SkAssertResult(fsBuilder->enableFeature(
497 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
499 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
500 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
501 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
502 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
503 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
504 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
505 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
507 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
508 // avoid calling inversesqrt on zero.
509 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
510 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
511 if (kHairline == ee.getMode()) {
512 // can probably do this with one step
513 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
514 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
516 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
520 if (kStroke == ee.getMode()) {
521 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
522 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
523 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
524 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
525 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
526 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
527 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
529 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
530 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
533 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
536 static void GenKey(const GrGeometryProcessor& gp,
537 const GrBatchTracker& bt,
539 GrProcessorKeyBuilder* b) {
540 const BatchTracker& local = bt.cast<BatchTracker>();
541 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
542 uint16_t key = ellipseEffect.getMode();
543 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
544 key |= ComputePosKey(gp.viewMatrix()) << 9;
545 b->add32(key << 16 | local.fInputColorType);
548 virtual void setData(const GrGLProgramDataManager& pdman,
549 const GrPrimitiveProcessor& gp,
550 const GrBatchTracker& bt) SK_OVERRIDE {
551 this->setUniformViewMatrix(pdman, gp.viewMatrix());
553 const BatchTracker& local = bt.cast<BatchTracker>();
554 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
556 GrColorToRGBAFloat(local.fColor, c);
557 pdman.set4fv(fColorUniform, 1, c);
558 fColor = local.fColor;
564 UniformHandle fColorUniform;
566 typedef GrGLGeometryProcessor INHERITED;
569 virtual void getGLProcessorKey(const GrBatchTracker& bt,
570 const GrGLCaps& caps,
571 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
572 GLProcessor::GenKey(*this, bt, caps, b);
575 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
576 const GrGLCaps&) const SK_OVERRIDE {
577 return SkNEW_ARGS(GLProcessor, (*this, bt));
580 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
581 BatchTracker* local = bt->cast<BatchTracker>();
582 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
583 local->fUsesLocalCoords = init.fUsesLocalCoords;
586 bool onCanMakeEqual(const GrBatchTracker& m,
587 const GrGeometryProcessor& that,
588 const GrBatchTracker& t) const SK_OVERRIDE {
589 const BatchTracker& mine = m.cast<BatchTracker>();
590 const BatchTracker& theirs = t.cast<BatchTracker>();
591 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
592 that, theirs.fUsesLocalCoords) &&
593 CanCombineOutput(mine.fInputColorType, mine.fColor,
594 theirs.fInputColorType, theirs.fColor);
598 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
599 : INHERITED(color, viewMatrix) {
600 this->initClassID<DIEllipseEdgeEffect>();
601 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
602 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
603 kVec2f_GrVertexAttribType));
604 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
605 kVec2f_GrVertexAttribType));
609 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
610 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
611 return eee.fMode == fMode;
614 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
615 out->setUnknownSingleComponent();
618 struct BatchTracker {
619 GrGPInput fInputColorType;
621 bool fUsesLocalCoords;
624 const Attribute* fInPosition;
625 const Attribute* fInEllipseOffsets0;
626 const Attribute* fInEllipseOffsets1;
629 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
631 typedef GrGeometryProcessor INHERITED;
634 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
636 GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
638 const GrDrawTargetCaps&,
639 GrTexture* textures[]) {
640 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
641 GrProcessorUnitTest::TestMatrix(random),
642 (Mode)(random->nextRangeU(0,2)));
645 ///////////////////////////////////////////////////////////////////////////////
647 void GrOvalRenderer::reset() {
648 SkSafeSetNull(fRRectIndexBuffer);
649 SkSafeSetNull(fStrokeRRectIndexBuffer);
652 bool GrOvalRenderer::drawOval(GrDrawTarget* target,
653 GrPipelineBuilder* pipelineBuilder,
655 const SkMatrix& viewMatrix,
658 const SkStrokeRec& stroke)
660 bool useCoverageAA = useAA &&
661 !pipelineBuilder->getRenderTarget()->isMultisampled();
663 if (!useCoverageAA) {
667 // we can draw circles
668 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
669 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
670 // if we have shader derivative support, render as device-independent
671 } else if (target->caps()->shaderDerivativeSupport()) {
672 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
674 // otherwise axis-aligned ellipses only
675 } else if (viewMatrix.rectStaysRect()) {
676 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
685 ///////////////////////////////////////////////////////////////////////////////
687 class CircleBatch : public GrBatch {
691 SkMatrix fViewMatrix;
692 SkScalar fInnerRadius;
693 SkScalar fOuterRadius;
698 static GrBatch* Create(const Geometry& geometry) {
699 return SkNEW_ARGS(CircleBatch, (geometry));
702 const char* name() const SK_OVERRIDE { return "CircleBatch"; }
704 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
705 // When this is called on a batch, there is only one geometry bundle
706 out->setKnownFourComponents(fGeoData[0].fColor);
709 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
710 out->setUnknownSingleComponent();
713 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
714 // Handle any color overrides
715 if (init.fColorIgnored) {
716 fGeoData[0].fColor = GrColor_ILLEGAL;
717 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
718 fGeoData[0].fColor = init.fOverrideColor;
721 // setup batch properties
722 fBatch.fColorIgnored = init.fColorIgnored;
723 fBatch.fColor = fGeoData[0].fColor;
724 fBatch.fStroke = fGeoData[0].fStroke;
725 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
726 fBatch.fCoverageIgnored = init.fCoverageIgnored;
729 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
731 if (!this->viewMatrix().invert(&invert)) {
735 // Setup geometry processor
736 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
740 batchTarget->initDraw(gp, pipeline);
742 // TODO this is hacky, but the only way we have to initialize the GP is to use the
743 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
744 // everywhere we can remove this nastiness
746 init.fColorIgnored = fBatch.fColorIgnored;
747 init.fOverrideColor = GrColor_ILLEGAL;
748 init.fCoverageIgnored = fBatch.fCoverageIgnored;
749 init.fUsesLocalCoords = this->usesLocalCoords();
750 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
752 int instanceCount = fGeoData.count();
753 int vertexCount = kVertsPerCircle * instanceCount;
754 size_t vertexStride = gp->getVertexStride();
755 SkASSERT(vertexStride == sizeof(CircleVertex));
757 const GrVertexBuffer* vertexBuffer;
760 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
765 if (!vertices || !batchTarget->quadIndexBuffer()) {
766 SkDebugf("Could not allocate buffers\n");
770 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
772 for (int i = 0; i < instanceCount; i++) {
773 Geometry& args = fGeoData[i];
775 SkScalar innerRadius = args.fInnerRadius;
776 SkScalar outerRadius = args.fOuterRadius;
778 const SkRect& bounds = args.fDevBounds;
780 // The inner radius in the vertex data must be specified in normalized space.
781 innerRadius = innerRadius / outerRadius;
782 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
783 verts[0].fOffset = SkPoint::Make(-1, -1);
784 verts[0].fOuterRadius = outerRadius;
785 verts[0].fInnerRadius = innerRadius;
787 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
788 verts[1].fOffset = SkPoint::Make(-1, 1);
789 verts[1].fOuterRadius = outerRadius;
790 verts[1].fInnerRadius = innerRadius;
792 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
793 verts[2].fOffset = SkPoint::Make(1, 1);
794 verts[2].fOuterRadius = outerRadius;
795 verts[2].fInnerRadius = innerRadius;
797 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
798 verts[3].fOffset = SkPoint::Make(1, -1);
799 verts[3].fOuterRadius = outerRadius;
800 verts[3].fInnerRadius = innerRadius;
802 verts += kVertsPerCircle;
805 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
807 GrDrawTarget::DrawInfo drawInfo;
808 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
809 drawInfo.setStartVertex(0);
810 drawInfo.setStartIndex(0);
811 drawInfo.setVerticesPerInstance(kVertsPerCircle);
812 drawInfo.setIndicesPerInstance(kIndicesPerCircle);
813 drawInfo.adjustStartVertex(firstVertex);
814 drawInfo.setVertexBuffer(vertexBuffer);
815 drawInfo.setIndexBuffer(quadIndexBuffer);
817 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
819 while (instanceCount) {
820 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
821 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
822 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
824 batchTarget->draw(drawInfo);
826 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
827 instanceCount -= drawInfo.instanceCount();
831 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
834 CircleBatch(const Geometry& geometry) {
835 this->initClassID<CircleBatch>();
836 fGeoData.push_back(geometry);
839 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
840 CircleBatch* that = t->cast<CircleBatch>();
842 // TODO use vertex color to avoid breaking batches
843 if (this->color() != that->color()) {
847 if (this->stroke() != that->stroke()) {
851 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
852 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
856 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
860 GrColor color() const { return fBatch.fColor; }
861 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
862 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
863 bool stroke() const { return fBatch.fStroke; }
865 struct BatchTracker {
868 bool fUsesLocalCoords;
870 bool fCoverageIgnored;
873 static const int kVertsPerCircle = 4;
874 static const int kIndicesPerCircle = 6;
877 SkSTArray<1, Geometry, true> fGeoData;
880 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
881 GrPipelineBuilder* pipelineBuilder,
883 const SkMatrix& viewMatrix,
885 const SkRect& circle,
886 const SkStrokeRec& stroke) {
887 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
888 viewMatrix.mapPoints(¢er, 1);
889 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
890 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
892 SkStrokeRec::Style style = stroke.getStyle();
893 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
894 SkStrokeRec::kHairline_Style == style;
895 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
897 SkScalar innerRadius = 0.0f;
898 SkScalar outerRadius = radius;
899 SkScalar halfWidth = 0;
901 if (SkScalarNearlyZero(strokeWidth)) {
902 halfWidth = SK_ScalarHalf;
904 halfWidth = SkScalarHalf(strokeWidth);
907 outerRadius += halfWidth;
909 innerRadius = radius - halfWidth;
913 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
914 // computation because the computed alpha is zero, rather than 50%, at the radius.
915 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
916 // and the outset ensures the box will cover all partially covered by the circle.
917 outerRadius += SK_ScalarHalf;
918 innerRadius -= SK_ScalarHalf;
920 SkRect bounds = SkRect::MakeLTRB(
921 center.fX - outerRadius,
922 center.fY - outerRadius,
923 center.fX + outerRadius,
924 center.fY + outerRadius
927 CircleBatch::Geometry geometry;
928 geometry.fViewMatrix = viewMatrix;
929 geometry.fColor = color;
930 geometry.fInnerRadius = innerRadius;
931 geometry.fOuterRadius = outerRadius;
932 geometry.fStroke = isStrokeOnly && innerRadius > 0;
933 geometry.fDevBounds = bounds;
935 SkAutoTUnref<GrBatch> batch(CircleBatch::Create(geometry));
936 target->drawBatch(pipelineBuilder, batch, &bounds);
939 ///////////////////////////////////////////////////////////////////////////////
941 class EllipseBatch : public GrBatch {
945 SkMatrix fViewMatrix;
948 SkScalar fInnerXRadius;
949 SkScalar fInnerYRadius;
954 static GrBatch* Create(const Geometry& geometry) {
955 return SkNEW_ARGS(EllipseBatch, (geometry));
958 const char* name() const SK_OVERRIDE { return "EllipseBatch"; }
960 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
961 // When this is called on a batch, there is only one geometry bundle
962 out->setKnownFourComponents(fGeoData[0].fColor);
964 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
965 out->setUnknownSingleComponent();
968 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
969 // Handle any color overrides
970 if (init.fColorIgnored) {
971 fGeoData[0].fColor = GrColor_ILLEGAL;
972 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
973 fGeoData[0].fColor = init.fOverrideColor;
976 // setup batch properties
977 fBatch.fColorIgnored = init.fColorIgnored;
978 fBatch.fColor = fGeoData[0].fColor;
979 fBatch.fStroke = fGeoData[0].fStroke;
980 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
981 fBatch.fCoverageIgnored = init.fCoverageIgnored;
984 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
986 if (!this->viewMatrix().invert(&invert)) {
990 // Setup geometry processor
991 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
995 batchTarget->initDraw(gp, pipeline);
997 // TODO this is hacky, but the only way we have to initialize the GP is to use the
998 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
999 // everywhere we can remove this nastiness
1000 GrPipelineInfo init;
1001 init.fColorIgnored = fBatch.fColorIgnored;
1002 init.fOverrideColor = GrColor_ILLEGAL;
1003 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1004 init.fUsesLocalCoords = this->usesLocalCoords();
1005 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1007 int instanceCount = fGeoData.count();
1008 int vertexCount = kVertsPerEllipse * instanceCount;
1009 size_t vertexStride = gp->getVertexStride();
1010 SkASSERT(vertexStride == sizeof(EllipseVertex));
1012 const GrVertexBuffer* vertexBuffer;
1015 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1020 if (!vertices || !batchTarget->quadIndexBuffer()) {
1021 SkDebugf("Could not allocate buffers\n");
1025 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1027 for (int i = 0; i < instanceCount; i++) {
1028 Geometry& args = fGeoData[i];
1030 SkScalar xRadius = args.fXRadius;
1031 SkScalar yRadius = args.fYRadius;
1033 // Compute the reciprocals of the radii here to save time in the shader
1034 SkScalar xRadRecip = SkScalarInvert(xRadius);
1035 SkScalar yRadRecip = SkScalarInvert(yRadius);
1036 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1037 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1039 const SkRect& bounds = args.fDevBounds;
1041 // The inner radius in the vertex data must be specified in normalized space.
1042 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1043 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
1044 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1045 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1047 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1048 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1049 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1050 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1052 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1053 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1054 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1055 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1057 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1058 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1059 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1060 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1062 verts += kVertsPerEllipse;
1065 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1067 GrDrawTarget::DrawInfo drawInfo;
1068 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1069 drawInfo.setStartVertex(0);
1070 drawInfo.setStartIndex(0);
1071 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1072 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1073 drawInfo.adjustStartVertex(firstVertex);
1074 drawInfo.setVertexBuffer(vertexBuffer);
1075 drawInfo.setIndexBuffer(quadIndexBuffer);
1077 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1079 while (instanceCount) {
1080 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1081 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1082 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1084 batchTarget->draw(drawInfo);
1086 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1087 instanceCount -= drawInfo.instanceCount();
1091 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1094 EllipseBatch(const Geometry& geometry) {
1095 this->initClassID<EllipseBatch>();
1096 fGeoData.push_back(geometry);
1099 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1100 EllipseBatch* that = t->cast<EllipseBatch>();
1102 // TODO use vertex color to avoid breaking batches
1103 if (this->color() != that->color()) {
1107 if (this->stroke() != that->stroke()) {
1111 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1112 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1116 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1120 GrColor color() const { return fBatch.fColor; }
1121 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1122 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1123 bool stroke() const { return fBatch.fStroke; }
1125 struct BatchTracker {
1128 bool fUsesLocalCoords;
1130 bool fCoverageIgnored;
1133 static const int kVertsPerEllipse = 4;
1134 static const int kIndicesPerEllipse = 6;
1136 BatchTracker fBatch;
1137 SkSTArray<1, Geometry, true> fGeoData;
1140 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1141 GrPipelineBuilder* pipelineBuilder,
1143 const SkMatrix& viewMatrix,
1145 const SkRect& ellipse,
1146 const SkStrokeRec& stroke) {
1149 // we should have checked for this previously
1150 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
1151 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
1155 // do any matrix crunching before we reset the draw state for device coords
1156 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1157 viewMatrix.mapPoints(¢er, 1);
1158 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1159 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1160 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1161 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1162 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1163 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
1165 // do (potentially) anisotropic mapping of stroke
1166 SkVector scaledStroke;
1167 SkScalar strokeWidth = stroke.getWidth();
1168 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1169 viewMatrix[SkMatrix::kMSkewY]));
1170 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1171 viewMatrix[SkMatrix::kMScaleY]));
1173 SkStrokeRec::Style style = stroke.getStyle();
1174 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1175 SkStrokeRec::kHairline_Style == style;
1176 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1178 SkScalar innerXRadius = 0;
1179 SkScalar innerYRadius = 0;
1181 if (SkScalarNearlyZero(scaledStroke.length())) {
1182 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1184 scaledStroke.scale(SK_ScalarHalf);
1187 // we only handle thick strokes for near-circular ellipses
1188 if (scaledStroke.length() > SK_ScalarHalf &&
1189 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1193 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1194 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1195 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1199 // this is legit only if scale & translation (which should be the case at the moment)
1201 innerXRadius = xRadius - scaledStroke.fX;
1202 innerYRadius = yRadius - scaledStroke.fY;
1205 xRadius += scaledStroke.fX;
1206 yRadius += scaledStroke.fY;
1209 // We've extended the outer x radius out half a pixel to antialias.
1210 // This will also expand the rect so all the pixels will be captured.
1211 // TODO: Consider if we should use sqrt(2)/2 instead
1212 xRadius += SK_ScalarHalf;
1213 yRadius += SK_ScalarHalf;
1215 SkRect bounds = SkRect::MakeLTRB(
1216 center.fX - xRadius,
1217 center.fY - yRadius,
1218 center.fX + xRadius,
1222 EllipseBatch::Geometry geometry;
1223 geometry.fViewMatrix = viewMatrix;
1224 geometry.fColor = color;
1225 geometry.fXRadius = xRadius;
1226 geometry.fYRadius = yRadius;
1227 geometry.fInnerXRadius = innerXRadius;
1228 geometry.fInnerYRadius = innerYRadius;
1229 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1230 geometry.fDevBounds = bounds;
1232 SkAutoTUnref<GrBatch> batch(EllipseBatch::Create(geometry));
1233 target->drawBatch(pipelineBuilder, batch, &bounds);
1238 /////////////////////////////////////////////////////////////////////////////////////////////////
1240 class DIEllipseBatch : public GrBatch {
1244 SkMatrix fViewMatrix;
1247 SkScalar fInnerXRadius;
1248 SkScalar fInnerYRadius;
1251 DIEllipseEdgeEffect::Mode fMode;
1255 static GrBatch* Create(const Geometry& geometry) {
1256 return SkNEW_ARGS(DIEllipseBatch, (geometry));
1259 const char* name() const SK_OVERRIDE { return "DIEllipseBatch"; }
1261 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1262 // When this is called on a batch, there is only one geometry bundle
1263 out->setKnownFourComponents(fGeoData[0].fColor);
1265 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1266 out->setUnknownSingleComponent();
1269 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1270 // Handle any color overrides
1271 if (init.fColorIgnored) {
1272 fGeoData[0].fColor = GrColor_ILLEGAL;
1273 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1274 fGeoData[0].fColor = init.fOverrideColor;
1277 // setup batch properties
1278 fBatch.fColorIgnored = init.fColorIgnored;
1279 fBatch.fColor = fGeoData[0].fColor;
1280 fBatch.fMode = fGeoData[0].fMode;
1281 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1282 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1285 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1286 // Setup geometry processor
1287 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1291 batchTarget->initDraw(gp, pipeline);
1293 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1294 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1295 // everywhere we can remove this nastiness
1296 GrPipelineInfo init;
1297 init.fColorIgnored = fBatch.fColorIgnored;
1298 init.fOverrideColor = GrColor_ILLEGAL;
1299 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1300 init.fUsesLocalCoords = this->usesLocalCoords();
1301 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1303 int instanceCount = fGeoData.count();
1304 int vertexCount = kVertsPerEllipse * instanceCount;
1305 size_t vertexStride = gp->getVertexStride();
1306 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1308 const GrVertexBuffer* vertexBuffer;
1311 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1316 if (!vertices || !batchTarget->quadIndexBuffer()) {
1317 SkDebugf("Could not allocate buffers\n");
1321 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
1323 for (int i = 0; i < instanceCount; i++) {
1324 Geometry& args = fGeoData[i];
1326 SkScalar xRadius = args.fXRadius;
1327 SkScalar yRadius = args.fYRadius;
1329 const SkRect& bounds = args.fDevBounds;
1331 // This adjusts the "radius" to include the half-pixel border
1332 SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
1333 SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
1335 SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
1336 SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
1338 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1339 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1340 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1342 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1343 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1344 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1346 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1347 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1348 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1350 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1351 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1352 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1354 verts += kVertsPerEllipse;
1357 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1359 GrDrawTarget::DrawInfo drawInfo;
1360 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1361 drawInfo.setStartVertex(0);
1362 drawInfo.setStartIndex(0);
1363 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1364 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1365 drawInfo.adjustStartVertex(firstVertex);
1366 drawInfo.setVertexBuffer(vertexBuffer);
1367 drawInfo.setIndexBuffer(quadIndexBuffer);
1369 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1371 while (instanceCount) {
1372 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1373 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1374 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1376 batchTarget->draw(drawInfo);
1378 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1379 instanceCount -= drawInfo.instanceCount();
1383 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1386 DIEllipseBatch(const Geometry& geometry) {
1387 this->initClassID<DIEllipseBatch>();
1388 fGeoData.push_back(geometry);
1391 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1392 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1394 // TODO use vertex color to avoid breaking batches
1395 if (this->color() != that->color()) {
1399 if (this->mode() != that->mode()) {
1403 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1404 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1408 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1412 GrColor color() const { return fBatch.fColor; }
1413 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1414 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1415 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1417 struct BatchTracker {
1419 DIEllipseEdgeEffect::Mode fMode;
1420 bool fUsesLocalCoords;
1422 bool fCoverageIgnored;
1425 static const int kVertsPerEllipse = 4;
1426 static const int kIndicesPerEllipse = 6;
1428 BatchTracker fBatch;
1429 SkSTArray<1, Geometry, true> fGeoData;
1432 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1433 GrPipelineBuilder* pipelineBuilder,
1435 const SkMatrix& viewMatrix,
1437 const SkRect& ellipse,
1438 const SkStrokeRec& stroke) {
1439 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1440 SkScalar xRadius = SkScalarHalf(ellipse.width());
1441 SkScalar yRadius = SkScalarHalf(ellipse.height());
1443 SkStrokeRec::Style style = stroke.getStyle();
1444 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
1445 DIEllipseEdgeEffect::kStroke :
1446 (SkStrokeRec::kHairline_Style == style) ?
1447 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1449 SkScalar innerXRadius = 0;
1450 SkScalar innerYRadius = 0;
1451 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1452 SkScalar strokeWidth = stroke.getWidth();
1454 if (SkScalarNearlyZero(strokeWidth)) {
1455 strokeWidth = SK_ScalarHalf;
1457 strokeWidth *= SK_ScalarHalf;
1460 // we only handle thick strokes for near-circular ellipses
1461 if (strokeWidth > SK_ScalarHalf &&
1462 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1466 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1467 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1468 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1472 // set inner radius (if needed)
1473 if (SkStrokeRec::kStroke_Style == style) {
1474 innerXRadius = xRadius - strokeWidth;
1475 innerYRadius = yRadius - strokeWidth;
1478 xRadius += strokeWidth;
1479 yRadius += strokeWidth;
1481 if (DIEllipseEdgeEffect::kStroke == mode) {
1482 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1483 DIEllipseEdgeEffect::kFill;
1486 // This expands the outer rect so that after CTM we end up with a half-pixel border
1487 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1488 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1489 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1490 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1491 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
1492 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
1494 SkRect bounds = SkRect::MakeLTRB(
1495 center.fX - xRadius - geoDx,
1496 center.fY - yRadius - geoDy,
1497 center.fX + xRadius + geoDx,
1498 center.fY + yRadius + geoDy
1501 DIEllipseBatch::Geometry geometry;
1502 geometry.fViewMatrix = viewMatrix;
1503 geometry.fColor = color;
1504 geometry.fXRadius = xRadius;
1505 geometry.fYRadius = yRadius;
1506 geometry.fInnerXRadius = innerXRadius;
1507 geometry.fInnerYRadius = innerYRadius;
1508 geometry.fGeoDx = geoDx;
1509 geometry.fGeoDy = geoDy;
1510 geometry.fMode = mode;
1511 geometry.fDevBounds = bounds;
1513 SkAutoTUnref<GrBatch> batch(DIEllipseBatch::Create(geometry));
1514 target->drawBatch(pipelineBuilder, batch, &bounds);
1519 ///////////////////////////////////////////////////////////////////////////////
1521 static const uint16_t gRRectIndices[] = {
1525 8, 9, 13, 8, 13, 12,
1526 10, 11, 15, 10, 15, 14,
1531 6, 7, 11, 6, 11, 10,
1532 9, 10, 14, 9, 14, 13,
1535 // we place this at the end so that we can ignore these indices when rendering stroke-only
1539 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1540 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1541 static const int kVertsPerRRect = 16;
1542 static const int kNumRRectsInIndexBuffer = 256;
1544 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly) {
1546 if (NULL == fStrokeRRectIndexBuffer) {
1547 fStrokeRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1548 kIndicesPerStrokeRRect,
1549 kNumRRectsInIndexBuffer,
1552 return fStrokeRRectIndexBuffer;
1554 if (NULL == fRRectIndexBuffer) {
1555 fRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1557 kNumRRectsInIndexBuffer,
1560 return fRRectIndexBuffer;
1564 bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
1565 GrPipelineBuilder* pipelineBuilder,
1567 const SkMatrix& viewMatrix,
1569 const SkRRect& origOuter,
1570 const SkRRect& origInner) {
1571 bool applyAA = useAA &&
1572 !pipelineBuilder->getRenderTarget()->isMultisampled();
1573 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
1574 if (!origInner.isEmpty()) {
1575 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
1576 if (!viewMatrix.isIdentity()) {
1577 if (!origInner.transform(viewMatrix, inner.writable())) {
1581 GrPrimitiveEdgeType edgeType = applyAA ?
1582 kInverseFillAA_GrProcessorEdgeType :
1583 kInverseFillBW_GrProcessorEdgeType;
1584 // TODO this needs to be a geometry processor
1585 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1589 arfp.set(pipelineBuilder);
1590 pipelineBuilder->addCoverageProcessor(fp)->unref();
1593 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
1594 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
1598 SkASSERT(!origOuter.isEmpty());
1599 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
1600 if (!viewMatrix.isIdentity()) {
1601 if (!origOuter.transform(viewMatrix, outer.writable())) {
1605 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
1606 kFillBW_GrProcessorEdgeType;
1607 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
1608 if (NULL == effect) {
1611 if (!arfp.isSet()) {
1612 arfp.set(pipelineBuilder);
1616 if (!viewMatrix.invert(&invert)) {
1620 pipelineBuilder->addCoverageProcessor(effect)->unref();
1621 SkRect bounds = outer->getBounds();
1623 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1625 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
1629 ///////////////////////////////////////////////////////////////////////////////////////////////////
1631 class RRectCircleRendererBatch : public GrBatch {
1635 SkMatrix fViewMatrix;
1636 SkScalar fInnerRadius;
1637 SkScalar fOuterRadius;
1642 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1643 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry, indexBuffer));
1646 const char* name() const SK_OVERRIDE { return "RRectCircleBatch"; }
1648 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1649 // When this is called on a batch, there is only one geometry bundle
1650 out->setKnownFourComponents(fGeoData[0].fColor);
1652 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1653 out->setUnknownSingleComponent();
1656 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1657 // Handle any color overrides
1658 if (init.fColorIgnored) {
1659 fGeoData[0].fColor = GrColor_ILLEGAL;
1660 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1661 fGeoData[0].fColor = init.fOverrideColor;
1664 // setup batch properties
1665 fBatch.fColorIgnored = init.fColorIgnored;
1666 fBatch.fColor = fGeoData[0].fColor;
1667 fBatch.fStroke = fGeoData[0].fStroke;
1668 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1669 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1672 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1673 // reset to device coordinates
1675 if (!this->viewMatrix().invert(&invert)) {
1676 SkDebugf("Failed to invert\n");
1680 // Setup geometry processor
1681 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1685 batchTarget->initDraw(gp, pipeline);
1687 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1688 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1689 // everywhere we can remove this nastiness
1690 GrPipelineInfo init;
1691 init.fColorIgnored = fBatch.fColorIgnored;
1692 init.fOverrideColor = GrColor_ILLEGAL;
1693 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1694 init.fUsesLocalCoords = this->usesLocalCoords();
1695 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1697 int instanceCount = fGeoData.count();
1698 int vertexCount = kVertsPerRRect * instanceCount;
1699 size_t vertexStride = gp->getVertexStride();
1700 SkASSERT(vertexStride == sizeof(CircleVertex));
1702 const GrVertexBuffer* vertexBuffer;
1705 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1711 SkDebugf("Could not allocate vertices\n");
1715 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
1717 for (int i = 0; i < instanceCount; i++) {
1718 Geometry& args = fGeoData[i];
1720 SkScalar outerRadius = args.fOuterRadius;
1722 const SkRect& bounds = args.fDevBounds;
1724 SkScalar yCoords[4] = {
1726 bounds.fTop + outerRadius,
1727 bounds.fBottom - outerRadius,
1731 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1732 // The inner radius in the vertex data must be specified in normalized space.
1733 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1734 for (int i = 0; i < 4; ++i) {
1735 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1736 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1737 verts->fOuterRadius = outerRadius;
1738 verts->fInnerRadius = innerRadius;
1741 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1742 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1743 verts->fOuterRadius = outerRadius;
1744 verts->fInnerRadius = innerRadius;
1747 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1748 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1749 verts->fOuterRadius = outerRadius;
1750 verts->fInnerRadius = innerRadius;
1753 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1754 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1755 verts->fOuterRadius = outerRadius;
1756 verts->fInnerRadius = innerRadius;
1761 // drop out the middle quad if we're stroked
1762 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1763 SK_ARRAY_COUNT(gRRectIndices);
1766 GrDrawTarget::DrawInfo drawInfo;
1767 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1768 drawInfo.setStartVertex(0);
1769 drawInfo.setStartIndex(0);
1770 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1771 drawInfo.setIndicesPerInstance(indexCnt);
1772 drawInfo.adjustStartVertex(firstVertex);
1773 drawInfo.setVertexBuffer(vertexBuffer);
1774 drawInfo.setIndexBuffer(fIndexBuffer);
1776 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1778 while (instanceCount) {
1779 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1780 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1781 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1783 batchTarget->draw(drawInfo);
1785 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1786 instanceCount -= drawInfo.instanceCount();
1790 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1793 RRectCircleRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
1794 : fIndexBuffer(indexBuffer) {
1795 this->initClassID<RRectCircleRendererBatch>();
1796 fGeoData.push_back(geometry);
1799 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
1800 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1802 // TODO use vertex color to avoid breaking batches
1803 if (this->color() != that->color()) {
1807 if (this->stroke() != that->stroke()) {
1811 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1812 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1816 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1820 GrColor color() const { return fBatch.fColor; }
1821 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1822 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1823 bool stroke() const { return fBatch.fStroke; }
1825 struct BatchTracker {
1828 bool fUsesLocalCoords;
1830 bool fCoverageIgnored;
1833 BatchTracker fBatch;
1834 SkSTArray<1, Geometry, true> fGeoData;
1835 const GrIndexBuffer* fIndexBuffer;
1838 class RRectEllipseRendererBatch : public GrBatch {
1842 SkMatrix fViewMatrix;
1845 SkScalar fInnerXRadius;
1846 SkScalar fInnerYRadius;
1851 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1852 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry, indexBuffer));
1855 const char* name() const SK_OVERRIDE { return "RRectEllipseRendererBatch"; }
1857 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
1858 // When this is called on a batch, there is only one geometry bundle
1859 out->setKnownFourComponents(fGeoData[0].fColor);
1861 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
1862 out->setUnknownSingleComponent();
1865 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
1866 // Handle any color overrides
1867 if (init.fColorIgnored) {
1868 fGeoData[0].fColor = GrColor_ILLEGAL;
1869 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1870 fGeoData[0].fColor = init.fOverrideColor;
1873 // setup batch properties
1874 fBatch.fColorIgnored = init.fColorIgnored;
1875 fBatch.fColor = fGeoData[0].fColor;
1876 fBatch.fStroke = fGeoData[0].fStroke;
1877 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1878 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1881 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
1882 // reset to device coordinates
1884 if (!this->viewMatrix().invert(&invert)) {
1885 SkDebugf("Failed to invert\n");
1889 // Setup geometry processor
1890 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1894 batchTarget->initDraw(gp, pipeline);
1896 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1897 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1898 // everywhere we can remove this nastiness
1899 GrPipelineInfo init;
1900 init.fColorIgnored = fBatch.fColorIgnored;
1901 init.fOverrideColor = GrColor_ILLEGAL;
1902 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1903 init.fUsesLocalCoords = this->usesLocalCoords();
1904 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1906 int instanceCount = fGeoData.count();
1907 int vertexCount = kVertsPerRRect * instanceCount;
1908 size_t vertexStride = gp->getVertexStride();
1909 SkASSERT(vertexStride == sizeof(EllipseVertex));
1911 const GrVertexBuffer* vertexBuffer;
1914 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1920 SkDebugf("Could not allocate vertices\n");
1924 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1926 for (int i = 0; i < instanceCount; i++) {
1927 Geometry& args = fGeoData[i];
1929 // Compute the reciprocals of the radii here to save time in the shader
1930 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1931 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1932 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1933 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1935 // Extend the radii out half a pixel to antialias.
1936 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1937 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1939 const SkRect& bounds = args.fDevBounds;
1941 SkScalar yCoords[4] = {
1943 bounds.fTop + yOuterRadius,
1944 bounds.fBottom - yOuterRadius,
1947 SkScalar yOuterOffsets[4] = {
1949 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1950 SK_ScalarNearlyZero,
1954 for (int i = 0; i < 4; ++i) {
1955 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1956 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1957 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1958 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1961 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1962 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1963 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1964 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1967 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1968 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1969 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1970 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1973 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1974 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1975 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1976 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1981 // drop out the middle quad if we're stroked
1982 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1983 SK_ARRAY_COUNT(gRRectIndices);
1985 GrDrawTarget::DrawInfo drawInfo;
1986 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1987 drawInfo.setStartVertex(0);
1988 drawInfo.setStartIndex(0);
1989 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1990 drawInfo.setIndicesPerInstance(indexCnt);
1991 drawInfo.adjustStartVertex(firstVertex);
1992 drawInfo.setVertexBuffer(vertexBuffer);
1993 drawInfo.setIndexBuffer(fIndexBuffer);
1995 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1997 while (instanceCount) {
1998 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1999 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
2000 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
2002 batchTarget->draw(drawInfo);
2004 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
2005 instanceCount -= drawInfo.instanceCount();
2009 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
2012 RRectEllipseRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
2013 : fIndexBuffer(indexBuffer) {
2014 this->initClassID<RRectEllipseRendererBatch>();
2015 fGeoData.push_back(geometry);
2018 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
2019 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2021 // TODO use vertex color to avoid breaking batches
2022 if (this->color() != that->color()) {
2026 if (this->stroke() != that->stroke()) {
2030 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
2031 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
2035 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
2039 GrColor color() const { return fBatch.fColor; }
2040 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
2041 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
2042 bool stroke() const { return fBatch.fStroke; }
2044 struct BatchTracker {
2047 bool fUsesLocalCoords;
2049 bool fCoverageIgnored;
2052 BatchTracker fBatch;
2053 SkSTArray<1, Geometry, true> fGeoData;
2054 const GrIndexBuffer* fIndexBuffer;
2057 bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2058 GrPipelineBuilder* pipelineBuilder,
2060 const SkMatrix& viewMatrix,
2062 const SkRRect& rrect,
2063 const SkStrokeRec& stroke) {
2064 if (rrect.isOval()) {
2065 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2069 bool useCoverageAA = useAA &&
2070 !pipelineBuilder->getRenderTarget()->isMultisampled();
2072 // only anti-aliased rrects for now
2073 if (!useCoverageAA) {
2077 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2081 // do any matrix crunching before we reset the draw state for device coords
2082 const SkRect& rrectBounds = rrect.getBounds();
2084 viewMatrix.mapRect(&bounds, rrectBounds);
2086 SkVector radii = rrect.getSimpleRadii();
2087 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2088 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2089 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2090 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
2092 SkStrokeRec::Style style = stroke.getStyle();
2094 // do (potentially) anisotropic mapping of stroke
2095 SkVector scaledStroke;
2096 SkScalar strokeWidth = stroke.getWidth();
2098 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2099 SkStrokeRec::kHairline_Style == style;
2100 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2103 if (SkStrokeRec::kHairline_Style == style) {
2104 scaledStroke.set(1, 1);
2106 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2107 viewMatrix[SkMatrix::kMSkewY]));
2108 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2109 viewMatrix[SkMatrix::kMScaleY]));
2112 // if half of strokewidth is greater than radius, we don't handle that right now
2113 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
2118 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2119 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2120 // patch will have fractional coverage. This only matters when the interior is actually filled.
2121 // We could consider falling back to rect rendering here, since a tiny radius is
2122 // indistinguishable from a square corner.
2123 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
2127 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
2128 if (NULL == indexBuffer) {
2129 SkDebugf("Failed to create index buffer!\n");
2133 // if the corners are circles, use the circle renderer
2134 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
2135 SkScalar innerRadius = 0.0f;
2136 SkScalar outerRadius = xRadius;
2137 SkScalar halfWidth = 0;
2139 if (SkScalarNearlyZero(scaledStroke.fX)) {
2140 halfWidth = SK_ScalarHalf;
2142 halfWidth = SkScalarHalf(scaledStroke.fX);
2146 innerRadius = xRadius - halfWidth;
2148 outerRadius += halfWidth;
2149 bounds.outset(halfWidth, halfWidth);
2152 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
2154 // The radii are outset for two reasons. First, it allows the shader to simply perform
2155 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2156 // Second, the outer radius is used to compute the verts of the bounding box that is
2157 // rendered and the outset ensures the box will cover all partially covered by the rrect
2159 outerRadius += SK_ScalarHalf;
2160 innerRadius -= SK_ScalarHalf;
2162 // Expand the rect so all the pixels will be captured.
2163 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2165 RRectCircleRendererBatch::Geometry geometry;
2166 geometry.fViewMatrix = viewMatrix;
2167 geometry.fColor = color;
2168 geometry.fInnerRadius = innerRadius;
2169 geometry.fOuterRadius = outerRadius;
2170 geometry.fStroke = isStrokeOnly;
2171 geometry.fDevBounds = bounds;
2173 SkAutoTUnref<GrBatch> batch(RRectCircleRendererBatch::Create(geometry, indexBuffer));
2174 target->drawBatch(pipelineBuilder, batch, &bounds);
2176 // otherwise we use the ellipse renderer
2178 SkScalar innerXRadius = 0.0f;
2179 SkScalar innerYRadius = 0.0f;
2181 if (SkScalarNearlyZero(scaledStroke.length())) {
2182 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2184 scaledStroke.scale(SK_ScalarHalf);
2187 // we only handle thick strokes for near-circular ellipses
2188 if (scaledStroke.length() > SK_ScalarHalf &&
2189 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
2193 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2194 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2195 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
2199 // this is legit only if scale & translation (which should be the case at the moment)
2201 innerXRadius = xRadius - scaledStroke.fX;
2202 innerYRadius = yRadius - scaledStroke.fY;
2205 xRadius += scaledStroke.fX;
2206 yRadius += scaledStroke.fY;
2207 bounds.outset(scaledStroke.fX, scaledStroke.fY);
2210 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
2212 // Expand the rect so all the pixels will be captured.
2213 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2215 RRectEllipseRendererBatch::Geometry geometry;
2216 geometry.fViewMatrix = viewMatrix;
2217 geometry.fColor = color;
2218 geometry.fXRadius = xRadius;
2219 geometry.fYRadius = yRadius;
2220 geometry.fInnerXRadius = innerXRadius;
2221 geometry.fInnerYRadius = innerYRadius;
2222 geometry.fStroke = isStrokeOnly;
2223 geometry.fDevBounds = bounds;
2225 SkAutoTUnref<GrBatch> batch(RRectEllipseRendererBatch::Create(geometry, indexBuffer));
2226 target->drawBatch(pipelineBuilder, batch, &bounds);