diff options
author | 2013-03-22 18:34:09 +0000 | |
---|---|---|
committer | 2013-03-22 18:34:09 +0000 | |
commit | 81312830ef73420efdc4821feb7c2d6fd9152af8 (patch) | |
tree | eed904a41dc717fe1ac1c9c757e416b99c69f85d /src/gpu/GrOvalRenderer.cpp | |
parent | 6f6568b27eae62fea23ab8192c6da02ab892bb5e (diff) |
Move oval rendering code to GrOvalRenderer.
Author: jvanverth@google.com
Reviewed By: bsalomon@google.com,robertphillips@google.com
Review URL: https://chromiumcodereview.appspot.com/12657003
git-svn-id: http://skia.googlecode.com/svn/trunk@8345 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/gpu/GrOvalRenderer.cpp')
-rw-r--r-- | src/gpu/GrOvalRenderer.cpp | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp new file mode 100644 index 0000000000..4847e0e9e1 --- /dev/null +++ b/src/gpu/GrOvalRenderer.cpp @@ -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); +} + |