| Index: src/gpu/GrOvalRenderer.cpp
|
| ===================================================================
|
| --- src/gpu/GrOvalRenderer.cpp (revision 0)
|
| +++ src/gpu/GrOvalRenderer.cpp (working copy)
|
| @@ -0,0 +1,278 @@
|
| +/*
|
| + * Copyright 2013 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "GrOvalRenderer.h"
|
| +
|
| +#include "effects/GrCircleEdgeEffect.h"
|
| +#include "effects/GrEllipseEdgeEffect.h"
|
| +
|
| +#include "GrDrawState.h"
|
| +#include "GrDrawTarget.h"
|
| +#include "SkStrokeRec.h"
|
| +
|
| +SK_DEFINE_INST_COUNT(GrOvalRenderer)
|
| +
|
| +namespace {
|
| +
|
| +struct CircleVertex {
|
| + GrPoint fPos;
|
| + GrPoint fCenter;
|
| + SkScalar fOuterRadius;
|
| + SkScalar fInnerRadius;
|
| +};
|
| +
|
| +struct EllipseVertex {
|
| + GrPoint fPos;
|
| + GrPoint fCenter;
|
| + SkScalar fOuterXRadius;
|
| + SkScalar fOuterXYRatio;
|
| + SkScalar fInnerXRadius;
|
| + SkScalar fInnerXYRatio;
|
| +};
|
| +
|
| +inline bool circle_stays_circle(const SkMatrix& m) {
|
| + return m.isSimilarity();
|
| +}
|
| +
|
| +}
|
| +
|
| +bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint,
|
| + const GrRect& oval, const SkStrokeRec& stroke)
|
| +{
|
| + if (!paint.isAntiAlias()) {
|
| + return false;
|
| + }
|
| +
|
| + const SkMatrix& vm = context->getMatrix();
|
| +
|
| + // we can draw circles
|
| + if (SkScalarNearlyEqual(oval.width(), oval.height())
|
| + && circle_stays_circle(vm)) {
|
| + drawCircle(target, paint, oval, stroke);
|
| +
|
| + // and axis-aligned ellipses only
|
| + } else if (vm.rectStaysRect()) {
|
| + drawEllipse(target, paint, oval, stroke);
|
| +
|
| + } else {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void GrOvalRenderer::drawCircle(GrDrawTarget* target,
|
| + const GrPaint& paint,
|
| + const GrRect& circle,
|
| + const SkStrokeRec& stroke)
|
| +{
|
| + GrDrawState* drawState = target->drawState();
|
| +
|
| + const SkMatrix& vm = drawState->getViewMatrix();
|
| + GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
|
| + vm.mapPoints(¢er, 1);
|
| + SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
|
| + SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
|
| +
|
| + GrDrawState::AutoDeviceCoordDraw adcd(drawState);
|
| + if (!adcd.succeeded()) {
|
| + return;
|
| + }
|
| +
|
| + // position + edge
|
| + static const GrVertexAttrib kVertexAttribs[] = {
|
| + {kVec2f_GrVertexAttribType, 0},
|
| + {kVec4f_GrVertexAttribType, sizeof(GrPoint)}
|
| + };
|
| + drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
|
| + drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
|
| + GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
|
| +
|
| + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
|
| + if (!geo.succeeded()) {
|
| + GrPrintf("Failed to get space for vertices!\n");
|
| + return;
|
| + }
|
| +
|
| + CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
|
| +
|
| + SkStrokeRec::Style style = stroke.getStyle();
|
| + bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
|
| + enum {
|
| + // the edge effects share this stage with glyph rendering
|
| + // (kGlyphMaskStage in GrTextContext) && SW path rendering
|
| + // (kPathMaskStage in GrSWMaskHelper)
|
| + kEdgeEffectStage = GrPaint::kTotalStages,
|
| + };
|
| + drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
|
| +
|
| + GrEffectRef* effect = GrCircleEdgeEffect::Create(isStroked);
|
| + static const int kCircleEdgeAttrIndex = 1;
|
| + drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
|
| +
|
| + SkScalar innerRadius = 0.0f;
|
| + SkScalar outerRadius = radius;
|
| + SkScalar halfWidth = 0;
|
| + if (style != SkStrokeRec::kFill_Style) {
|
| + if (SkScalarNearlyZero(strokeWidth)) {
|
| + halfWidth = SK_ScalarHalf;
|
| + } else {
|
| + halfWidth = SkScalarHalf(strokeWidth);
|
| + }
|
| +
|
| + outerRadius += halfWidth;
|
| + if (isStroked) {
|
| + innerRadius = SkMaxScalar(0, radius - halfWidth);
|
| + }
|
| + }
|
| +
|
| + for (int i = 0; i < 4; ++i) {
|
| + verts[i].fCenter = center;
|
| + verts[i].fOuterRadius = outerRadius + 0.5f;
|
| + verts[i].fInnerRadius = innerRadius - 0.5f;
|
| + }
|
| +
|
| + SkScalar L = -outerRadius;
|
| + SkScalar R = +outerRadius;
|
| + SkScalar T = -outerRadius;
|
| + SkScalar B = +outerRadius;
|
| +
|
| + // We've extended the outer radius out half a pixel to antialias.
|
| + // Expand the drawn rect here so all the pixels will be captured.
|
| + L += center.fX - SK_ScalarHalf;
|
| + R += center.fX + SK_ScalarHalf;
|
| + T += center.fY - SK_ScalarHalf;
|
| + B += center.fY + SK_ScalarHalf;
|
| +
|
| + verts[0].fPos = SkPoint::Make(L, T);
|
| + verts[1].fPos = SkPoint::Make(R, T);
|
| + verts[2].fPos = SkPoint::Make(L, B);
|
| + verts[3].fPos = SkPoint::Make(R, B);
|
| +
|
| + target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
|
| +}
|
| +
|
| +void GrOvalRenderer::drawEllipse(GrDrawTarget* target,
|
| + const GrPaint& paint,
|
| + const GrRect& ellipse,
|
| + const SkStrokeRec& stroke)
|
| +{
|
| + GrDrawState* drawState = target->drawState();
|
| +#ifdef SK_DEBUG
|
| + {
|
| + // we should have checked for this previously
|
| + bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
|
| + SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
|
| + }
|
| +#endif
|
| +
|
| + const SkMatrix& vm = drawState->getViewMatrix();
|
| + GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
|
| + vm.mapPoints(¢er, 1);
|
| + SkRect xformedRect;
|
| + vm.mapRect(&xformedRect, ellipse);
|
| +
|
| + GrDrawState::AutoDeviceCoordDraw adcd(drawState);
|
| + if (!adcd.succeeded()) {
|
| + return;
|
| + }
|
| +
|
| + // position + edge
|
| + static const GrVertexAttrib kVertexAttribs[] = {
|
| + {kVec2f_GrVertexAttribType, 0},
|
| + {kVec2f_GrVertexAttribType, sizeof(GrPoint)},
|
| + {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint)}
|
| + };
|
| + drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
|
| + drawState->setAttribIndex(GrDrawState::kPosition_AttribIndex, 0);
|
| + GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
|
| +
|
| + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
|
| + if (!geo.succeeded()) {
|
| + GrPrintf("Failed to get space for vertices!\n");
|
| + return;
|
| + }
|
| +
|
| + EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
|
| +
|
| + SkStrokeRec::Style style = stroke.getStyle();
|
| + bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
|
| + enum {
|
| + // the edge effects share this stage with glyph rendering
|
| + // (kGlyphMaskStage in GrTextContext) && SW path rendering
|
| + // (kPathMaskStage in GrSWMaskHelper)
|
| + kEdgeEffectStage = GrPaint::kTotalStages,
|
| + };
|
| + drawState->setAttribBindings(GrDrawState::kDefault_AttribBindings);
|
| +
|
| + GrEffectRef* effect = GrEllipseEdgeEffect::Create(isStroked);
|
| + static const int kEllipseCenterAttrIndex = 1;
|
| + static const int kEllipseEdgeAttrIndex = 2;
|
| + drawState->setEffect(kEdgeEffectStage, effect,
|
| + kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
|
| +
|
| + SkScalar xRadius = SkScalarHalf(xformedRect.width());
|
| + SkScalar yRadius = SkScalarHalf(xformedRect.height());
|
| + SkScalar innerXRadius = 0.0f;
|
| + SkScalar innerRatio = 1.0f;
|
| +
|
| + if (SkStrokeRec::kFill_Style != style) {
|
| + SkScalar strokeWidth = stroke.getWidth();
|
| +
|
| + // do (potentially) anisotropic mapping
|
| + SkVector scaledStroke;
|
| + scaledStroke.set(strokeWidth, strokeWidth);
|
| + vm.mapVectors(&scaledStroke, 1);
|
| +
|
| + if (SkScalarNearlyZero(scaledStroke.length())) {
|
| + scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
|
| + } else {
|
| + scaledStroke.scale(0.5f);
|
| + }
|
| +
|
| + // this is legit only if scale & translation (which should be the case at the moment)
|
| + if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
|
| + SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
|
| + if (innerYRadius > SK_ScalarNearlyZero) {
|
| + innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
|
| + innerRatio = innerXRadius/innerYRadius;
|
| + }
|
| + }
|
| + xRadius += scaledStroke.fX;
|
| + yRadius += scaledStroke.fY;
|
| + }
|
| +
|
| + SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
|
| +
|
| + for (int i = 0; i < 4; ++i) {
|
| + verts[i].fCenter = center;
|
| + verts[i].fOuterXRadius = xRadius + 0.5f;
|
| + verts[i].fOuterXYRatio = outerRatio;
|
| + verts[i].fInnerXRadius = innerXRadius - 0.5f;
|
| + verts[i].fInnerXYRatio = innerRatio;
|
| + }
|
| +
|
| + SkScalar L = -xRadius;
|
| + SkScalar R = +xRadius;
|
| + SkScalar T = -yRadius;
|
| + SkScalar B = +yRadius;
|
| +
|
| + // We've extended the outer x radius out half a pixel to antialias.
|
| + // Expand the drawn rect here so all the pixels will be captured.
|
| + L += center.fX - SK_ScalarHalf;
|
| + R += center.fX + SK_ScalarHalf;
|
| + T += center.fY - SK_ScalarHalf;
|
| + B += center.fY + SK_ScalarHalf;
|
| +
|
| + verts[0].fPos = SkPoint::Make(L, T);
|
| + verts[1].fPos = SkPoint::Make(R, T);
|
| + verts[2].fPos = SkPoint::Make(L, B);
|
| + verts[3].fPos = SkPoint::Make(R, B);
|
| +
|
| + target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
|
| +}
|
| +
|
|
|