From 0a09d7195b8d9945e5c9c76cc4cfe6ef65d6d390 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Wed, 9 Apr 2014 21:26:11 +0000 Subject: Implement drawDRRect for GPU BUG=skia:2259 R=jvanverth@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/220233011 git-svn-id: http://skia.googlecode.com/svn/trunk@14118 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/gpu/GrContext.cpp | 34 +++++++++-- src/gpu/GrOvalRenderer.cpp | 127 +++++++++++++++++++++++++++------------ src/gpu/GrOvalRenderer.h | 6 +- src/gpu/SkGpuDevice.cpp | 38 +++++++++--- src/gpu/effects/GrOvalEffect.cpp | 11 +++- 5 files changed, 160 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 89548ab5c9..90bf8c042e 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -1002,9 +1002,9 @@ void GrContext::drawVertices(const GrPaint& paint, /////////////////////////////////////////////////////////////////////////////// void GrContext::drawRRect(const GrPaint& paint, - const SkRRect& rect, + const SkRRect& rrect, const SkStrokeRec& stroke) { - if (rect.isEmpty()) { + if (rrect.isEmpty()) { return; } @@ -1014,15 +1014,41 @@ void GrContext::drawRRect(const GrPaint& paint, GR_CREATE_TRACE_MARKER("GrContext::drawRRect", target); - if (!fOvalRenderer->drawSimpleRRect(target, this, paint.isAntiAlias(), rect, stroke)) { + if (!fOvalRenderer->drawRRect(target, this, paint.isAntiAlias(), rrect, stroke)) { SkPath path; - path.addRRect(rect); + path.addRRect(rrect); this->internalDrawPath(target, paint.isAntiAlias(), path, stroke); } } /////////////////////////////////////////////////////////////////////////////// +void GrContext::drawDRRect(const GrPaint& paint, + const SkRRect& outer, + const SkRRect& inner) { + if (outer.isEmpty()) { + return; + } + + AutoRestoreEffects are; + AutoCheckFlush acf(this); + GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf); + + GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target); + + if (!fOvalRenderer->drawDRRect(target, this, paint.isAntiAlias(), outer, inner)) { + SkPath path; + path.addRRect(inner); + path.addRRect(outer); + path.setFillType(SkPath::kEvenOdd_FillType); + + SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle); + this->internalDrawPath(target, paint.isAntiAlias(), path, fillRec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + void GrContext::drawOval(const GrPaint& paint, const SkRect& oval, const SkStrokeRec& stroke) { diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index 2ade8cde07..45564bcce0 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 { @@ -519,12 +521,14 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target, CircleVertex* verts = reinterpret_cast(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 { @@ -532,12 +536,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(); @@ -626,11 +630,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 { @@ -650,7 +656,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; } @@ -675,7 +681,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, EllipseVertex* verts = reinterpret_cast(geo.vertices()); - GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked && + GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly && innerXRadius > 0 && innerYRadius > 0); static const int kEllipseCenterAttrIndex = 1; @@ -877,9 +883,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 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(); @@ -890,12 +926,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(); @@ -908,21 +942,38 @@ 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(); - 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; + + if (hasStroke) { + if (SkStrokeRec::kHairline_Style == style) { + scaledStroke.set(1, 1); + } else { + 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; + } + } + + // 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; } @@ -933,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"); @@ -942,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(SK_ARRAY_COUNT(gCircleVertexAttribs)); SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize()); @@ -956,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(); @@ -1025,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); @@ -1036,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 { @@ -1056,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; } @@ -1066,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()) { @@ -1075,7 +1125,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b } EllipseVertex* verts = reinterpret_cast(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, @@ -1134,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); } diff --git a/src/gpu/GrOvalRenderer.h b/src/gpu/GrOvalRenderer.h index 9653fccd72..92f6ff0cdf 100644 --- a/src/gpu/GrOvalRenderer.h +++ b/src/gpu/GrOvalRenderer.h @@ -33,8 +33,10 @@ public: bool drawOval(GrDrawTarget* target, const GrContext* context, bool useAA, const SkRect& oval, const SkStrokeRec& stroke); - bool drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA, - const SkRRect& rrect, const SkStrokeRec& stroke); + bool drawRRect(GrDrawTarget* target, GrContext* context, bool useAA, + const SkRRect& rrect, const SkStrokeRec& stroke); + bool drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA, + const SkRRect& outer, const SkRRect& inner); private: bool drawEllipse(GrDrawTarget* target, bool useCoverageAA, diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 3045782895..9acc204342 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -656,17 +656,7 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, } - bool usePath = !rect.isSimple(); - // another two reasons we might need to call drawPath... if (paint.getMaskFilter() || paint.getPathEffect()) { - usePath = true; - } - // until we can rotate rrects... - if (!usePath && !fContext->getMatrix().rectStaysRect()) { - usePath = true; - } - - if (usePath) { SkPath path; path.addRRect(rect); this->drawPath(draw, path, paint, NULL, true); @@ -676,6 +666,34 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, fContext->drawRRect(grPaint, rect, stroke); } +void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer, + const SkRRect& inner, const SkPaint& paint) { + SkStrokeRec stroke(paint); + if (stroke.isFillStyle()) { + + CHECK_FOR_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + if (NULL == paint.getMaskFilter() && NULL == paint.getPathEffect()) { + fContext->drawDRRect(grPaint, outer, inner); + return; + } + } + + SkPath path; + path.addRRect(outer); + path.addRRect(inner); + path.setFillType(SkPath::kEvenOdd_FillType); + + this->drawPath(draw, path, paint, NULL, true); +} + + ///////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, diff --git a/src/gpu/effects/GrOvalEffect.cpp b/src/gpu/effects/GrOvalEffect.cpp index 97b977f754..40870e27d2 100644 --- a/src/gpu/effects/GrOvalEffect.cpp +++ b/src/gpu/effects/GrOvalEffect.cpp @@ -137,7 +137,8 @@ void GLCircleEffect::emitCode(GrGLShaderBuilder* builder, const TextureSamplerArray& samplers) { const CircleEffect& ce = drawEffect.castEffect(); const char *circleName; - // The circle uniform is (center.x, center.y, radius + 0.5) + // The circle uniform is (center.x, center.y, radius + 0.5) for regular fills and + // (... ,radius - 0.5) for inverse fills. fCircleUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, kVec3f_GrSLType, "circle", @@ -171,7 +172,13 @@ GrGLEffect::EffectKey GLCircleEffect::GenKey(const GrDrawEffect& drawEffect, void GLCircleEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { const CircleEffect& ce = drawEffect.castEffect(); if (ce.getRadius() != fPrevRadius || ce.getCenter() != fPrevCenter) { - uman.set3f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, ce.getRadius() + 0.5f); + SkScalar radius = ce.getRadius(); + if (GrEffectEdgeTypeIsInverseFill(ce.getEdgeType())) { + radius -= 0.5f; + } else { + radius += 0.5f; + } + uman.set3f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, radius); fPrevCenter = ce.getCenter(); fPrevRadius = ce.getRadius(); } -- cgit v1.2.3