Chromium Code Reviews| Index: src/gpu/GrOvalRenderer.cpp |
| diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
| index 190bf9d257006fa4e52a84987c29896517d1256c..84aeb2d8b4e1ed29230313316efb11ecba1243f1 100644 |
| --- a/src/gpu/GrOvalRenderer.cpp |
| +++ b/src/gpu/GrOvalRenderer.cpp |
| @@ -19,8 +19,10 @@ |
| #include "SkRRect.h" |
| #include "SkStrokeRec.h" |
| +#include "SkTLazy.h" |
| #include "effects/GrVertexEffect.h" |
| +#include "effects/GrRRectEffect.h" |
| namespace { |
| @@ -525,12 +527,14 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target, |
| CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); |
| SkStrokeRec::Style style = stroke.getStyle(); |
| - bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); |
| + bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
| + SkStrokeRec::kHairline_Style == style; |
| + bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
| SkScalar innerRadius = 0.0f; |
| SkScalar outerRadius = radius; |
| SkScalar halfWidth = 0; |
| - if (style != SkStrokeRec::kFill_Style) { |
| + if (hasStroke) { |
| if (SkScalarNearlyZero(strokeWidth)) { |
| halfWidth = SK_ScalarHalf; |
| } else { |
| @@ -538,12 +542,12 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target, |
| } |
| outerRadius += halfWidth; |
| - if (isStroked) { |
| + if (isStrokeOnly) { |
| innerRadius = radius - halfWidth; |
| } |
| } |
| - GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0); |
| + GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly && innerRadius > 0); |
| static const int kCircleEdgeAttrIndex = 1; |
| drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); |
| @@ -632,11 +636,13 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, |
| scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); |
| SkStrokeRec::Style style = stroke.getStyle(); |
| - bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); |
| + bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
| + SkStrokeRec::kHairline_Style == style; |
| + bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
| SkScalar innerXRadius = 0; |
| SkScalar innerYRadius = 0; |
| - if (SkStrokeRec::kFill_Style != style) { |
| + if (hasStroke) { |
| if (SkScalarNearlyZero(scaledStroke.length())) { |
| scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
| } else { |
| @@ -656,7 +662,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, |
| } |
| // this is legit only if scale & translation (which should be the case at the moment) |
| - if (isStroked) { |
| + if (isStrokeOnly) { |
| innerXRadius = xRadius - scaledStroke.fX; |
| innerYRadius = yRadius - scaledStroke.fY; |
| } |
| @@ -681,7 +687,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, |
| EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); |
| - GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked && |
| + GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly && |
| innerXRadius > 0 && innerYRadius > 0); |
| static const int kEllipseCenterAttrIndex = 1; |
| @@ -883,9 +889,39 @@ GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) { |
| return fRRectIndexBuffer; |
| } |
| -bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA, |
| - const SkRRect& rrect, const SkStrokeRec& stroke) |
| -{ |
| +bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA, |
| + const SkRRect& outer, const SkRRect& origInner) { |
| + GrDrawState::AutoRestoreEffects are; |
| + if (!origInner.isEmpty()) { |
| + SkTCopyOnFirstWrite<SkRRect> inner(origInner); |
| + if (!context->getMatrix().isIdentity()) { |
| + if (!origInner.transform(context->getMatrix(), inner.writable())) { |
| + return false; |
| + } |
| + } |
| + bool applyAA = useAA && |
| + !target->getDrawState().getRenderTarget()->isMultisampled() && |
| + !target->shouldDisableCoverageAAForBlend(); |
| + GrEffectEdgeType edgeType = applyAA ? kInverseFillAA_GrEffectEdgeType : |
| + kInverseFillBW_GrEffectEdgeType; |
| + GrEffectRef* effect = GrRRectEffect::Create(edgeType, *inner); |
| + if (NULL == effect) { |
| + return false; |
| + } |
| + are.set(target->drawState()); |
| + target->drawState()->addCoverageEffect(effect)->unref(); |
| + } |
| + |
| + SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle); |
| + return this->drawRRect(target, context, useAA, outer, fillRec); |
| +} |
| + |
| +bool GrOvalRenderer::drawRRect(GrDrawTarget* target, GrContext* context, bool useAA, |
| + const SkRRect& rrect, const SkStrokeRec& stroke) { |
| + if (rrect.isOval()) { |
| + return this->drawOval(target, context, useAA, rrect.getBounds(), stroke); |
| + } |
| + |
| bool useCoverageAA = useAA && |
| !target->getDrawState().getRenderTarget()->isMultisampled() && |
| !target->shouldDisableCoverageAAForBlend(); |
| @@ -896,12 +932,10 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| } |
| const SkMatrix& vm = context->getMatrix(); |
| -#ifdef SK_DEBUG |
| - { |
| - // we should have checked for this previously |
| - SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple()); |
| + |
| + if (!vm.rectStaysRect() || !rrect.isSimple()) { |
| + return false; |
| } |
| -#endif |
| // do any matrix crunching before we reset the draw state for device coords |
| const SkRect& rrectBounds = rrect.getBounds(); |
| @@ -914,24 +948,35 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX + |
| vm[SkMatrix::kMScaleY]*radii.fY); |
| - // if hairline stroke is greater than radius, we don't handle that right now |
| SkStrokeRec::Style style = stroke.getStyle(); |
|
jvanverth1
2014/04/09 17:26:06
It's not clear to me where this hairline check wen
bsalomon
2014/04/09 19:23:25
Done.
|
| - if (SkStrokeRec::kHairline_Style == style && |
| - (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { |
| - return false; |
| - } |
| // do (potentially) anisotropic mapping of stroke |
| SkVector scaledStroke; |
| SkScalar strokeWidth = stroke.getWidth(); |
| - scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); |
| - scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); |
| - // if half of strokewidth is greater than radius, we don't handle that right now |
| - if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) { |
| + bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || |
| + SkStrokeRec::kHairline_Style == style; |
| + bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; |
| + |
| + // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on |
| + // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- |
| + // patch will have fractional coverage. This only matters when the interior is actually filled. |
| + // We could consider falling back to rect rendering here, since a tiny radius is |
| + // indistinguishable from a square corner. |
| + if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { |
| return false; |
| } |
| + if (hasStroke) { |
| + scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); |
| + scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); |
| + |
| + // if half of strokewidth is greater than radius, we don't handle that right now |
| + if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) { |
| + return false; |
| + } |
| + } |
| + |
| // reset to device coordinates |
| GrDrawState* drawState = target->drawState(); |
| GrDrawState::AutoViewMatrixRestore avmr; |
| @@ -939,8 +984,6 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| return false; |
| } |
| - bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); |
| - |
| GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu()); |
| if (NULL == indexBuffer) { |
| GrPrintf("Failed to create index buffer!\n"); |
| @@ -948,7 +991,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| } |
| // if the corners are circles, use the circle renderer |
| - if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { |
| + if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { |
| drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); |
| SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize()); |
| @@ -962,23 +1005,23 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| SkScalar innerRadius = 0.0f; |
| SkScalar outerRadius = xRadius; |
| SkScalar halfWidth = 0; |
| - if (style != SkStrokeRec::kFill_Style) { |
| + if (hasStroke) { |
| if (SkScalarNearlyZero(scaledStroke.fX)) { |
| halfWidth = SK_ScalarHalf; |
| } else { |
| halfWidth = SkScalarHalf(scaledStroke.fX); |
| } |
| - if (isStroked) { |
| + if (isStrokeOnly) { |
| innerRadius = xRadius - halfWidth; |
| } |
| outerRadius += halfWidth; |
| bounds.outset(halfWidth, halfWidth); |
| } |
| - isStroked = (isStroked && innerRadius >= 0); |
| + isStrokeOnly = (isStrokeOnly && innerRadius >= 0); |
| - GrEffectRef* effect = CircleEdgeEffect::Create(isStroked); |
| + GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly); |
| static const int kCircleEdgeAttrIndex = 1; |
| drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); |
| @@ -1031,7 +1074,8 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| } |
| // drop out the middle quad if we're stroked |
| - int indexCnt = isStroked ? SK_ARRAY_COUNT(gRRectIndices)-6 : SK_ARRAY_COUNT(gRRectIndices); |
| + int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 : |
| + SK_ARRAY_COUNT(gRRectIndices); |
| target->setIndexSourceToBuffer(indexBuffer); |
| target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); |
| @@ -1042,7 +1086,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| SkScalar innerXRadius = 0.0f; |
| SkScalar innerYRadius = 0.0f; |
| - if (SkStrokeRec::kFill_Style != style) { |
| + if (hasStroke) { |
| if (SkScalarNearlyZero(scaledStroke.length())) { |
| scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); |
| } else { |
| @@ -1062,7 +1106,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| } |
| // this is legit only if scale & translation (which should be the case at the moment) |
| - if (isStroked) { |
| + if (isStrokeOnly) { |
| innerXRadius = xRadius - scaledStroke.fX; |
| innerYRadius = yRadius - scaledStroke.fY; |
| } |
| @@ -1072,7 +1116,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| bounds.outset(scaledStroke.fX, scaledStroke.fY); |
| } |
| - isStroked = (isStroked && innerXRadius >= 0 && innerYRadius >= 0); |
| + isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0); |
| GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); |
| if (!geo.succeeded()) { |
| @@ -1081,7 +1125,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| } |
| EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); |
| - GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked); |
| + GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly); |
| static const int kEllipseOffsetAttrIndex = 1; |
| static const int kEllipseRadiiAttrIndex = 2; |
| drawState->addCoverageEffect(effect, |
| @@ -1140,7 +1184,8 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b |
| } |
| // drop out the middle quad if we're stroked |
| - int indexCnt = isStroked ? SK_ARRAY_COUNT(gRRectIndices)-6 : SK_ARRAY_COUNT(gRRectIndices); |
| + int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 : |
| + SK_ARRAY_COUNT(gRRectIndices); |
| target->setIndexSourceToBuffer(indexBuffer); |
| target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); |
| } |