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"
10 #include "gl/builders/GrGLProgramBuilder.h"
11 #include "gl/GrGLProcessor.h"
12 #include "gl/GrGLSL.h"
13 #include "gl/GrGLGeometryProcessor.h"
14 #include "GrProcessor.h"
15 #include "GrTBackendProcessorFactory.h"
17 #include "GrDrawState.h"
18 #include "GrDrawTarget.h"
22 #include "SkStrokeRec.h"
25 #include "GrGeometryProcessor.h"
26 #include "effects/GrRRectEffect.h"
29 // TODO(joshualitt) add per vertex colors
33 SkScalar fOuterRadius;
34 SkScalar fInnerRadius;
37 struct EllipseVertex {
44 struct DIEllipseVertex {
50 inline bool circle_stays_circle(const SkMatrix& m) {
51 return m.isSimilarity();
56 ///////////////////////////////////////////////////////////////////////////////
59 * The output of this effect is a modulation of the input color and coverage for a circle,
60 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
63 class CircleEdgeEffect : public GrGeometryProcessor {
65 static GrGeometryProcessor* Create(bool stroke) {
66 GR_CREATE_STATIC_PROCESSOR(gCircleStrokeEdge, CircleEdgeEffect, (true));
67 GR_CREATE_STATIC_PROCESSOR(gCircleFillEdge, CircleEdgeEffect, (false));
70 gCircleStrokeEdge->ref();
71 return gCircleStrokeEdge;
73 gCircleFillEdge->ref();
74 return gCircleFillEdge;
78 const GrShaderVar& inCircleEdge() const { return fInCircleEdge; }
80 virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
81 return GrTBackendGeometryProcessorFactory<CircleEdgeEffect>::getInstance();
84 virtual ~CircleEdgeEffect() {}
86 static const char* Name() { return "CircleEdge"; }
88 inline bool isStroked() const { return fStroke; }
90 class GLProcessor : public GrGLGeometryProcessor {
92 GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
93 : INHERITED (factory) {}
95 virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
96 const CircleEdgeEffect& circleEffect = args.fGP.cast<CircleEdgeEffect>();
97 GrGLVertToFrag v(kVec4f_GrSLType);
98 args.fPB->addVarying("CircleEdge", &v);
100 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();;
101 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), circleEffect.inCircleEdge().c_str());
103 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
104 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
105 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z - d, 0.0, 1.0);", v.fsIn());
106 if (circleEffect.isStroked()) {
107 fsBuilder->codeAppendf("float innerAlpha = clamp(d - %s.w, 0.0, 1.0);",
109 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
112 fsBuilder->codeAppendf("%s = %s;\n", args.fOutput,
113 (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("edgeAlpha")).c_str());
116 static void GenKey(const GrProcessor& processor, const GrGLCaps&,
117 GrProcessorKeyBuilder* b) {
118 const CircleEdgeEffect& circleEffect = processor.cast<CircleEdgeEffect>();
119 b->add32(circleEffect.isStroked());
122 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {}
125 typedef GrGLGeometryProcessor INHERITED;
130 CircleEdgeEffect(bool stroke)
131 : fInCircleEdge(this->addVertexAttrib(
132 GrShaderVar("inCircleEdge",
134 GrShaderVar::kAttribute_TypeModifier))) {
138 virtual bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
139 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
140 return cee.fStroke == fStroke;
143 virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
144 inout->mulByUnknownAlpha();
147 const GrShaderVar& fInCircleEdge;
150 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
152 typedef GrGeometryProcessor INHERITED;
155 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
157 GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
159 const GrDrawTargetCaps&,
160 GrTexture* textures[]) {
161 return CircleEdgeEffect::Create(random->nextBool());
164 ///////////////////////////////////////////////////////////////////////////////
167 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
168 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
169 * in both x and y directions.
171 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
174 class EllipseEdgeEffect : public GrGeometryProcessor {
176 static GrGeometryProcessor* Create(bool stroke) {
177 GR_CREATE_STATIC_PROCESSOR(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
178 GR_CREATE_STATIC_PROCESSOR(gEllipseFillEdge, EllipseEdgeEffect, (false));
181 gEllipseStrokeEdge->ref();
182 return gEllipseStrokeEdge;
184 gEllipseFillEdge->ref();
185 return gEllipseFillEdge;
189 virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
190 return GrTBackendGeometryProcessorFactory<EllipseEdgeEffect>::getInstance();
193 virtual ~EllipseEdgeEffect() {}
195 static const char* Name() { return "EllipseEdge"; }
197 const GrShaderVar& inEllipseOffset() const { return fInEllipseOffset; }
198 const GrShaderVar& inEllipseRadii() const { return fInEllipseRadii; }
200 inline bool isStroked() const { return fStroke; }
202 class GLProcessor : public GrGLGeometryProcessor {
204 GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
205 : INHERITED (factory) {}
207 virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
208 const EllipseEdgeEffect& ellipseEffect = args.fGP.cast<EllipseEdgeEffect>();
210 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
211 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
213 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
214 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
215 ellipseEffect.inEllipseOffset().c_str());
217 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
218 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
219 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
220 ellipseEffect.inEllipseRadii().c_str());
223 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
224 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
225 ellipseRadii.fsIn());
226 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
227 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
228 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
230 // avoid calling inversesqrt on zero.
231 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
232 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
233 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
236 if (ellipseEffect.isStroked()) {
237 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
238 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
239 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
240 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
241 ellipseRadii.fsIn());
242 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
243 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
246 fsBuilder->codeAppendf("%s = %s;", args.fOutput,
247 (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("edgeAlpha")).c_str());
250 static void GenKey(const GrProcessor& processor, const GrGLCaps&,
251 GrProcessorKeyBuilder* b) {
252 const EllipseEdgeEffect& ellipseEffect = processor.cast<EllipseEdgeEffect>();
253 b->add32(ellipseEffect.isStroked());
256 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {
260 typedef GrGLGeometryProcessor INHERITED;
264 EllipseEdgeEffect(bool stroke)
265 : fInEllipseOffset(this->addVertexAttrib(
266 GrShaderVar("inEllipseOffset",
268 GrShaderVar::kAttribute_TypeModifier)))
269 , fInEllipseRadii(this->addVertexAttrib(
270 GrShaderVar("inEllipseRadii",
272 GrShaderVar::kAttribute_TypeModifier))) {
276 virtual bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
277 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
278 return eee.fStroke == fStroke;
281 virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
282 inout->mulByUnknownAlpha();
285 const GrShaderVar& fInEllipseOffset;
286 const GrShaderVar& fInEllipseRadii;
289 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
291 typedef GrGeometryProcessor INHERITED;
294 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
296 GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
298 const GrDrawTargetCaps&,
299 GrTexture* textures[]) {
300 return EllipseEdgeEffect::Create(random->nextBool());
303 ///////////////////////////////////////////////////////////////////////////////
306 * The output of this effect is a modulation of the input color and coverage for an ellipse,
307 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
308 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
309 * using differentials.
311 * The result is device-independent and can be used with any affine matrix.
314 class DIEllipseEdgeEffect : public GrGeometryProcessor {
316 enum Mode { kStroke = 0, kHairline, kFill };
318 static GrGeometryProcessor* Create(Mode mode) {
319 GR_CREATE_STATIC_PROCESSOR(gEllipseStrokeEdge, DIEllipseEdgeEffect, (kStroke));
320 GR_CREATE_STATIC_PROCESSOR(gEllipseHairlineEdge, DIEllipseEdgeEffect, (kHairline));
321 GR_CREATE_STATIC_PROCESSOR(gEllipseFillEdge, DIEllipseEdgeEffect, (kFill));
323 if (kStroke == mode) {
324 gEllipseStrokeEdge->ref();
325 return gEllipseStrokeEdge;
326 } else if (kHairline == mode) {
327 gEllipseHairlineEdge->ref();
328 return gEllipseHairlineEdge;
330 gEllipseFillEdge->ref();
331 return gEllipseFillEdge;
335 virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
336 return GrTBackendGeometryProcessorFactory<DIEllipseEdgeEffect>::getInstance();
339 virtual ~DIEllipseEdgeEffect() {}
341 static const char* Name() { return "DIEllipseEdge"; }
343 const GrShaderVar& inEllipseOffsets0() const { return fInEllipseOffsets0; }
344 const GrShaderVar& inEllipseOffsets1() const { return fInEllipseOffsets1; }
346 inline Mode getMode() const { return fMode; }
348 class GLProcessor : public GrGLGeometryProcessor {
350 GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
351 : INHERITED (factory) {}
353 virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
354 const DIEllipseEdgeEffect& ellipseEffect = args.fGP.cast<DIEllipseEdgeEffect>();
356 GrGLVertToFrag offsets0(kVec2f_GrSLType);
357 args.fPB->addVarying("EllipseOffsets0", &offsets0);
359 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
360 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
361 ellipseEffect.inEllipseOffsets0().c_str());
363 GrGLVertToFrag offsets1(kVec2f_GrSLType);
364 args.fPB->addVarying("EllipseOffsets1", &offsets1);
365 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
366 ellipseEffect.inEllipseOffsets1().c_str());
368 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
369 SkAssertResult(fsBuilder->enableFeature(
370 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
372 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
373 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
374 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
375 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
376 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
377 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
378 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
380 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
381 // avoid calling inversesqrt on zero.
382 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
383 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
384 if (kHairline == ellipseEffect.getMode()) {
385 // can probably do this with one step
386 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
387 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
389 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
393 if (kStroke == ellipseEffect.getMode()) {
394 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
395 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
396 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
397 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
398 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
399 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
400 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
402 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
403 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
406 fsBuilder->codeAppendf("%s = %s;", args.fOutput,
407 (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("edgeAlpha")).c_str());
410 static void GenKey(const GrProcessor& processor, const GrGLCaps&,
411 GrProcessorKeyBuilder* b) {
412 const DIEllipseEdgeEffect& ellipseEffect = processor.cast<DIEllipseEdgeEffect>();
414 b->add32(ellipseEffect.getMode());
417 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {
421 typedef GrGLGeometryProcessor INHERITED;
425 DIEllipseEdgeEffect(Mode mode)
426 : fInEllipseOffsets0(this->addVertexAttrib(
427 GrShaderVar("inEllipseOffsets0",
429 GrShaderVar::kAttribute_TypeModifier)))
430 , fInEllipseOffsets1(this->addVertexAttrib(
431 GrShaderVar("inEllipseOffsets1",
433 GrShaderVar::kAttribute_TypeModifier))) {
437 virtual bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
438 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
439 return eee.fMode == fMode;
442 virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
443 inout->mulByUnknownAlpha();
446 const GrShaderVar& fInEllipseOffsets0;
447 const GrShaderVar& fInEllipseOffsets1;
450 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
452 typedef GrGeometryProcessor INHERITED;
455 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
457 GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
459 const GrDrawTargetCaps&,
460 GrTexture* textures[]) {
461 return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2)));
464 ///////////////////////////////////////////////////////////////////////////////
466 void GrOvalRenderer::reset() {
467 SkSafeSetNull(fRRectIndexBuffer);
468 SkSafeSetNull(fStrokeRRectIndexBuffer);
471 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
472 const SkRect& oval, const SkStrokeRec& stroke)
474 bool useCoverageAA = useAA &&
475 !target->getDrawState().getRenderTarget()->isMultisampled() &&
476 !target->shouldDisableCoverageAAForBlend();
478 if (!useCoverageAA) {
482 const SkMatrix& vm = context->getMatrix();
484 // we can draw circles
485 if (SkScalarNearlyEqual(oval.width(), oval.height())
486 && circle_stays_circle(vm)) {
487 this->drawCircle(target, context, useCoverageAA, oval, stroke);
488 // if we have shader derivative support, render as device-independent
489 } else if (target->caps()->shaderDerivativeSupport()) {
490 return this->drawDIEllipse(target, context, useCoverageAA, oval, stroke);
491 // otherwise axis-aligned ellipses only
492 } else if (vm.rectStaysRect()) {
493 return this->drawEllipse(target, context, useCoverageAA, oval, stroke);
501 ///////////////////////////////////////////////////////////////////////////////
504 extern const GrVertexAttrib gCircleVertexAttribs[] = {
505 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
506 {kVec4f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
509 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
510 const GrContext* context,
512 const SkRect& circle,
513 const SkStrokeRec& stroke)
515 GrDrawState* drawState = target->drawState();
517 const SkMatrix& vm = drawState->getViewMatrix();
518 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
519 vm.mapPoints(¢er, 1);
520 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
521 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
523 GrDrawState::AutoViewMatrixRestore avmr;
524 if (!avmr.setIdentity(drawState)) {
528 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs),
529 sizeof(CircleVertex));
531 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
532 if (!geo.succeeded()) {
533 SkDebugf("Failed to get space for vertices!\n");
537 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
539 SkStrokeRec::Style style = stroke.getStyle();
540 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
541 SkStrokeRec::kHairline_Style == style;
542 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
544 SkScalar innerRadius = 0.0f;
545 SkScalar outerRadius = radius;
546 SkScalar halfWidth = 0;
548 if (SkScalarNearlyZero(strokeWidth)) {
549 halfWidth = SK_ScalarHalf;
551 halfWidth = SkScalarHalf(strokeWidth);
554 outerRadius += halfWidth;
556 innerRadius = radius - halfWidth;
560 GrGeometryProcessor* gp = CircleEdgeEffect::Create(isStrokeOnly && innerRadius > 0);
561 drawState->setGeometryProcessor(gp)->unref();
563 // The radii are outset for two reasons. First, it allows the shader to simply perform
564 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
565 // verts of the bounding box that is rendered and the outset ensures the box will cover all
566 // pixels partially covered by the circle.
567 outerRadius += SK_ScalarHalf;
568 innerRadius -= SK_ScalarHalf;
570 SkRect bounds = SkRect::MakeLTRB(
571 center.fX - outerRadius,
572 center.fY - outerRadius,
573 center.fX + outerRadius,
574 center.fY + outerRadius
577 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
578 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
579 verts[0].fOuterRadius = outerRadius;
580 verts[0].fInnerRadius = innerRadius;
582 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
583 verts[1].fOffset = SkPoint::Make(-outerRadius, outerRadius);
584 verts[1].fOuterRadius = outerRadius;
585 verts[1].fInnerRadius = innerRadius;
587 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
588 verts[2].fOffset = SkPoint::Make(outerRadius, outerRadius);
589 verts[2].fOuterRadius = outerRadius;
590 verts[2].fInnerRadius = innerRadius;
592 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
593 verts[3].fOffset = SkPoint::Make(outerRadius, -outerRadius);
594 verts[3].fOuterRadius = outerRadius;
595 verts[3].fInnerRadius = innerRadius;
597 target->setIndexSourceToBuffer(context->getGpu()->getQuadIndexBuffer());
598 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
599 target->resetIndexSource();
602 ///////////////////////////////////////////////////////////////////////////////
604 // position + offset + 1/radii
605 extern const GrVertexAttrib gEllipseVertexAttribs[] = {
606 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
607 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding},
608 {kVec4f_GrVertexAttribType, 2*sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
611 // position + offsets
612 extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
613 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
614 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding},
615 {kVec2f_GrVertexAttribType, 2*sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding},
618 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
619 const GrContext* context,
621 const SkRect& ellipse,
622 const SkStrokeRec& stroke)
624 GrDrawState* drawState = target->drawState();
627 // we should have checked for this previously
628 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
629 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
633 // do any matrix crunching before we reset the draw state for device coords
634 const SkMatrix& vm = drawState->getViewMatrix();
635 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
636 vm.mapPoints(¢er, 1);
637 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
638 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
639 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
640 vm[SkMatrix::kMSkewY]*ellipseYRadius);
641 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
642 vm[SkMatrix::kMScaleY]*ellipseYRadius);
644 // do (potentially) anisotropic mapping of stroke
645 SkVector scaledStroke;
646 SkScalar strokeWidth = stroke.getWidth();
647 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
648 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
650 SkStrokeRec::Style style = stroke.getStyle();
651 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
652 SkStrokeRec::kHairline_Style == style;
653 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
655 SkScalar innerXRadius = 0;
656 SkScalar innerYRadius = 0;
658 if (SkScalarNearlyZero(scaledStroke.length())) {
659 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
661 scaledStroke.scale(SK_ScalarHalf);
664 // we only handle thick strokes for near-circular ellipses
665 if (scaledStroke.length() > SK_ScalarHalf &&
666 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
670 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
671 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
672 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
676 // this is legit only if scale & translation (which should be the case at the moment)
678 innerXRadius = xRadius - scaledStroke.fX;
679 innerYRadius = yRadius - scaledStroke.fY;
682 xRadius += scaledStroke.fX;
683 yRadius += scaledStroke.fY;
686 GrDrawState::AutoViewMatrixRestore avmr;
687 if (!avmr.setIdentity(drawState)) {
691 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs),
692 sizeof(EllipseVertex));
694 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
695 if (!geo.succeeded()) {
696 SkDebugf("Failed to get space for vertices!\n");
700 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
702 GrGeometryProcessor* gp = EllipseEdgeEffect::Create(isStrokeOnly &&
703 innerXRadius > 0 && innerYRadius > 0);
705 drawState->setGeometryProcessor(gp)->unref();
707 // Compute the reciprocals of the radii here to save time in the shader
708 SkScalar xRadRecip = SkScalarInvert(xRadius);
709 SkScalar yRadRecip = SkScalarInvert(yRadius);
710 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
711 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
713 // We've extended the outer x radius out half a pixel to antialias.
714 // This will also expand the rect so all the pixels will be captured.
715 // TODO: Consider if we should use sqrt(2)/2 instead
716 xRadius += SK_ScalarHalf;
717 yRadius += SK_ScalarHalf;
719 SkRect bounds = SkRect::MakeLTRB(
726 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
727 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
728 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
729 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
731 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
732 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
733 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
734 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
736 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
737 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
738 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
739 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
741 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
742 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
743 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
744 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
746 target->setIndexSourceToBuffer(context->getGpu()->getQuadIndexBuffer());
747 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
748 target->resetIndexSource();
753 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
754 const GrContext* context,
756 const SkRect& ellipse,
757 const SkStrokeRec& stroke)
759 GrDrawState* drawState = target->drawState();
760 const SkMatrix& vm = drawState->getViewMatrix();
762 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
763 SkScalar xRadius = SkScalarHalf(ellipse.width());
764 SkScalar yRadius = SkScalarHalf(ellipse.height());
766 SkStrokeRec::Style style = stroke.getStyle();
767 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
768 DIEllipseEdgeEffect::kStroke :
769 (SkStrokeRec::kHairline_Style == style) ?
770 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
772 SkScalar innerXRadius = 0;
773 SkScalar innerYRadius = 0;
774 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
775 SkScalar strokeWidth = stroke.getWidth();
777 if (SkScalarNearlyZero(strokeWidth)) {
778 strokeWidth = SK_ScalarHalf;
780 strokeWidth *= SK_ScalarHalf;
783 // we only handle thick strokes for near-circular ellipses
784 if (strokeWidth > SK_ScalarHalf &&
785 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
789 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
790 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
791 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
795 // set inner radius (if needed)
796 if (SkStrokeRec::kStroke_Style == style) {
797 innerXRadius = xRadius - strokeWidth;
798 innerYRadius = yRadius - strokeWidth;
801 xRadius += strokeWidth;
802 yRadius += strokeWidth;
804 if (DIEllipseEdgeEffect::kStroke == mode) {
805 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
806 DIEllipseEdgeEffect::kFill;
808 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
809 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
811 drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs),
812 sizeof(DIEllipseVertex));
814 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
815 if (!geo.succeeded()) {
816 SkDebugf("Failed to get space for vertices!\n");
820 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
822 GrGeometryProcessor* gp = DIEllipseEdgeEffect::Create(mode);
824 drawState->setGeometryProcessor(gp)->unref();
826 // This expands the outer rect so that after CTM we end up with a half-pixel border
827 SkScalar a = vm[SkMatrix::kMScaleX];
828 SkScalar b = vm[SkMatrix::kMSkewX];
829 SkScalar c = vm[SkMatrix::kMSkewY];
830 SkScalar d = vm[SkMatrix::kMScaleY];
831 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
832 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
833 // This adjusts the "radius" to include the half-pixel border
834 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
835 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
837 SkRect bounds = SkRect::MakeLTRB(
838 center.fX - xRadius - geoDx,
839 center.fY - yRadius - geoDy,
840 center.fX + xRadius + geoDx,
841 center.fY + yRadius + geoDy
844 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
845 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
846 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
848 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
849 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
850 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
852 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
853 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
854 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
856 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
857 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
858 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
860 target->setIndexSourceToBuffer(context->getGpu()->getQuadIndexBuffer());
861 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
862 target->resetIndexSource();
867 ///////////////////////////////////////////////////////////////////////////////
869 static const uint16_t gRRectIndices[] = {
874 10, 11, 15, 10, 15, 14,
880 9, 10, 14, 9, 14, 13,
883 // we place this at the end so that we can ignore these indices when rendering stroke-only
887 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
888 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
889 static const int kVertsPerRRect = 16;
890 static const int kNumRRectsInIndexBuffer = 256;
892 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly, GrGpu* gpu) {
894 if (NULL == fStrokeRRectIndexBuffer) {
895 fStrokeRRectIndexBuffer = gpu->createInstancedIndexBuffer(gRRectIndices,
896 kIndicesPerStrokeRRect,
897 kNumRRectsInIndexBuffer,
900 return fStrokeRRectIndexBuffer;
902 if (NULL == fRRectIndexBuffer) {
903 fRRectIndexBuffer = gpu->createInstancedIndexBuffer(gRRectIndices,
905 kNumRRectsInIndexBuffer,
908 return fRRectIndexBuffer;
912 bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA,
913 const SkRRect& origOuter, const SkRRect& origInner) {
914 bool applyAA = useAA &&
915 !target->getDrawState().getRenderTarget()->isMultisampled() &&
916 !target->shouldDisableCoverageAAForBlend();
917 GrDrawState::AutoRestoreEffects are;
918 if (!origInner.isEmpty()) {
919 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
920 if (!context->getMatrix().isIdentity()) {
921 if (!origInner.transform(context->getMatrix(), inner.writable())) {
925 GrPrimitiveEdgeType edgeType = applyAA ?
926 kInverseFillAA_GrProcessorEdgeType :
927 kInverseFillBW_GrProcessorEdgeType;
928 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
932 are.set(target->drawState());
933 target->drawState()->addCoverageProcessor(fp)->unref();
936 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
937 if (this->drawRRect(target, context, useAA, origOuter, fillRec)) {
941 SkASSERT(!origOuter.isEmpty());
942 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
943 if (!context->getMatrix().isIdentity()) {
944 if (!origOuter.transform(context->getMatrix(), outer.writable())) {
948 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
949 kFillBW_GrProcessorEdgeType;
950 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
951 if (NULL == effect) {
955 are.set(target->drawState());
957 GrDrawState::AutoViewMatrixRestore avmr;
958 if (!avmr.setIdentity(target->drawState())) {
961 target->drawState()->addCoverageProcessor(effect)->unref();
962 SkRect bounds = outer->getBounds();
964 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
966 target->drawRect(bounds, NULL, NULL);
970 bool GrOvalRenderer::drawRRect(GrDrawTarget* target, GrContext* context, bool useAA,
971 const SkRRect& rrect, const SkStrokeRec& stroke) {
972 if (rrect.isOval()) {
973 return this->drawOval(target, context, useAA, rrect.getBounds(), stroke);
976 bool useCoverageAA = useAA &&
977 !target->getDrawState().getRenderTarget()->isMultisampled() &&
978 !target->shouldDisableCoverageAAForBlend();
980 // only anti-aliased rrects for now
981 if (!useCoverageAA) {
985 const SkMatrix& vm = context->getMatrix();
987 if (!vm.rectStaysRect() || !rrect.isSimple()) {
991 // do any matrix crunching before we reset the draw state for device coords
992 const SkRect& rrectBounds = rrect.getBounds();
994 vm.mapRect(&bounds, rrectBounds);
996 SkVector radii = rrect.getSimpleRadii();
997 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
998 vm[SkMatrix::kMSkewY]*radii.fY);
999 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
1000 vm[SkMatrix::kMScaleY]*radii.fY);
1002 SkStrokeRec::Style style = stroke.getStyle();
1004 // do (potentially) anisotropic mapping of stroke
1005 SkVector scaledStroke;
1006 SkScalar strokeWidth = stroke.getWidth();
1008 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1009 SkStrokeRec::kHairline_Style == style;
1010 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1013 if (SkStrokeRec::kHairline_Style == style) {
1014 scaledStroke.set(1, 1);
1016 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] +
1017 vm[SkMatrix::kMSkewY]));
1018 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] +
1019 vm[SkMatrix::kMScaleY]));
1022 // if half of strokewidth is greater than radius, we don't handle that right now
1023 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1028 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1029 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1030 // patch will have fractional coverage. This only matters when the interior is actually filled.
1031 // We could consider falling back to rect rendering here, since a tiny radius is
1032 // indistinguishable from a square corner.
1033 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
1037 // reset to device coordinates
1038 GrDrawState* drawState = target->drawState();
1039 GrDrawState::AutoViewMatrixRestore avmr;
1040 if (!avmr.setIdentity(drawState)) {
1044 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly, context->getGpu());
1045 if (NULL == indexBuffer) {
1046 SkDebugf("Failed to create index buffer!\n");
1050 // if the corners are circles, use the circle renderer
1051 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
1052 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs),
1053 sizeof(CircleVertex));
1055 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
1056 if (!geo.succeeded()) {
1057 SkDebugf("Failed to get space for vertices!\n");
1060 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
1062 SkScalar innerRadius = 0.0f;
1063 SkScalar outerRadius = xRadius;
1064 SkScalar halfWidth = 0;
1066 if (SkScalarNearlyZero(scaledStroke.fX)) {
1067 halfWidth = SK_ScalarHalf;
1069 halfWidth = SkScalarHalf(scaledStroke.fX);
1073 innerRadius = xRadius - halfWidth;
1075 outerRadius += halfWidth;
1076 bounds.outset(halfWidth, halfWidth);
1079 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
1081 GrGeometryProcessor* effect = CircleEdgeEffect::Create(isStrokeOnly);
1082 drawState->setGeometryProcessor(effect)->unref();
1084 // The radii are outset for two reasons. First, it allows the shader to simply perform
1085 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
1086 // verts of the bounding box that is rendered and the outset ensures the box will cover all
1087 // pixels partially covered by the circle.
1088 outerRadius += SK_ScalarHalf;
1089 innerRadius -= SK_ScalarHalf;
1091 // Expand the rect so all the pixels will be captured.
1092 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1094 SkScalar yCoords[4] = {
1096 bounds.fTop + outerRadius,
1097 bounds.fBottom - outerRadius,
1100 SkScalar yOuterRadii[4] = {
1106 for (int i = 0; i < 4; ++i) {
1107 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1108 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
1109 verts->fOuterRadius = outerRadius;
1110 verts->fInnerRadius = innerRadius;
1113 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1114 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1115 verts->fOuterRadius = outerRadius;
1116 verts->fInnerRadius = innerRadius;
1119 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1120 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1121 verts->fOuterRadius = outerRadius;
1122 verts->fInnerRadius = innerRadius;
1125 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1126 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
1127 verts->fOuterRadius = outerRadius;
1128 verts->fInnerRadius = innerRadius;
1132 // drop out the middle quad if we're stroked
1133 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1134 SK_ARRAY_COUNT(gRRectIndices);
1135 target->setIndexSourceToBuffer(indexBuffer);
1136 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 16, indexCnt, &bounds);
1138 // otherwise we use the ellipse renderer
1140 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs),
1141 sizeof(EllipseVertex));
1143 SkScalar innerXRadius = 0.0f;
1144 SkScalar innerYRadius = 0.0f;
1146 if (SkScalarNearlyZero(scaledStroke.length())) {
1147 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1149 scaledStroke.scale(SK_ScalarHalf);
1152 // we only handle thick strokes for near-circular ellipses
1153 if (scaledStroke.length() > SK_ScalarHalf &&
1154 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1158 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1159 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1160 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1164 // this is legit only if scale & translation (which should be the case at the moment)
1166 innerXRadius = xRadius - scaledStroke.fX;
1167 innerYRadius = yRadius - scaledStroke.fY;
1170 xRadius += scaledStroke.fX;
1171 yRadius += scaledStroke.fY;
1172 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1175 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
1177 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
1178 if (!geo.succeeded()) {
1179 SkDebugf("Failed to get space for vertices!\n");
1182 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
1184 GrGeometryProcessor* effect = EllipseEdgeEffect::Create(isStrokeOnly);
1185 drawState->setGeometryProcessor(effect)->unref();
1187 // Compute the reciprocals of the radii here to save time in the shader
1188 SkScalar xRadRecip = SkScalarInvert(xRadius);
1189 SkScalar yRadRecip = SkScalarInvert(yRadius);
1190 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1191 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
1193 // Extend the radii out half a pixel to antialias.
1194 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1195 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
1197 // Expand the rect so all the pixels will be captured.
1198 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1200 SkScalar yCoords[4] = {
1202 bounds.fTop + yOuterRadius,
1203 bounds.fBottom - yOuterRadius,
1206 SkScalar yOuterOffsets[4] = {
1208 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1209 SK_ScalarNearlyZero,
1213 for (int i = 0; i < 4; ++i) {
1214 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1215 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1216 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1217 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1220 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1221 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1222 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1223 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1226 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1227 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1228 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1229 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1232 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1233 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1234 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1235 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1239 // drop out the middle quad if we're stroked
1240 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1241 SK_ARRAY_COUNT(gRRectIndices);
1242 target->setIndexSourceToBuffer(indexBuffer);
1243 target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 16, indexCnt, &bounds);
1246 target->resetIndexSource();