Chromium Code Reviews| Index: src/gpu/GrOvalRenderer.cpp |
| =================================================================== |
| --- src/gpu/GrOvalRenderer.cpp (revision 0) |
| +++ src/gpu/GrOvalRenderer.cpp (working copy) |
| @@ -0,0 +1,290 @@ |
| +/* |
|
robertphillips
2013/03/21 13:12:39
2013
jvanverth1
2013/03/21 18:42:18
Done.
|
| + * Copyright 2012 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 "GrContext.h" |
| +#include "GrDrawState.h" |
| +#include "GrDrawTarget.h" |
| +#include "GrPaint.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; |
| +}; |
| + |
|
robertphillips
2013/03/21 13:12:39
circle_stays_circle
jvanverth1
2013/03/21 18:42:18
Done.
|
| +inline bool circleStaysCircle(const SkMatrix& m) { |
| + return m.isSimilarity(); |
| +} |
| + |
| +} |
| + |
| +bool GrOvalRenderer::canDrawOval(const GrContext* context, const GrPaint& paint, |
| + const GrRect& oval, bool* isCircle) const |
| +{ |
| + GrAssert(isCircle != NULL); |
| + |
| + if (!paint.isAntiAlias()) { |
| + return false; |
| + } |
| + |
| + // we can draw circles |
| + *isCircle = SkScalarNearlyEqual(oval.width(), oval.height()) |
| + && circleStaysCircle(context->getMatrix()); |
| + // and axis-aligned ellipses only |
| + bool isAxisAlignedEllipse = context->getMatrix().rectStaysRect(); |
| + |
| + return *isCircle || isAxisAlignedEllipse; |
| + |
| +} |
| + |
| +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 |
| + |
|
robertphillips
2013/03/21 13:12:39
Do we even need "rt"?
jvanverth1
2013/03/21 18:42:18
Done.
|
| + const GrRenderTarget* rt = drawState->getRenderTarget(); |
| + if (NULL == rt) { |
| + return; |
| + } |
| + |
|
robertphillips
2013/03/21 13:12:39
&
jvanverth1
2013/03/21 18:42:18
Done.
|
| + const SkMatrix vm = drawState->getViewMatrix(); |
| + |
| + 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()); |
| + |
| + GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY()); |
| + vm.mapPoints(¢er, 1); |
| + |
| + 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(); |
| + |
| + SkRect xformedRect; |
| + vm.mapRect(&xformedRect, ellipse); |
| + |
| + 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); |
| +} |
| + |
| +void GrOvalRenderer::drawCircle(GrDrawTarget* target, |
| + const GrPaint& paint, |
| + const GrRect& circle, |
| + const SkStrokeRec& stroke) |
| +{ |
| + GrDrawState* drawState = target->drawState(); |
|
robertphillips
2013/03/21 13:12:39
Do we even need "rt"?
jvanverth1
2013/03/21 18:42:18
Done.
|
| + const GrRenderTarget* rt = drawState->getRenderTarget(); |
| + if (NULL == rt) { |
| + return; |
| + } |
| + |
| + SkScalar radius = SkScalarHalf(circle.width()); |
| + |
| + SkScalar strokeWidth = stroke.getWidth(); |
| + SkStrokeRec::Style style = stroke.getStyle(); |
| + |
|
robertphillips
2013/03/21 13:12:39
&
jvanverth1
2013/03/21 18:42:18
Done.
|
| + const SkMatrix vm = drawState->getViewMatrix(); |
| + |
| + 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()); |
| + |
| + GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY()); |
| + vm.mapPoints(¢er, 1); |
| + |
| + 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(); |
| + |
| + radius = vm.mapRadius(radius); |
| + |
| + SkScalar innerRadius = 0.0f; |
| + SkScalar outerRadius = radius; |
| + SkScalar halfWidth = 0; |
| + if (style != SkStrokeRec::kFill_Style) { |
| + strokeWidth = vm.mapRadius(strokeWidth); |
| + 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); |
| +} |