diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-04-25 15:27:00 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-04-25 15:27:00 +0000 |
commit | f2bfd54de32ffbcf90ddcd0e249aaebb1559d9c2 (patch) | |
tree | 1068c54781522b1908b5c94a3fb7645045eab0a0 | |
parent | eb02957a5ff4e7b639263b2071e5e2522c7bc4fa (diff) |
Add GPU support for roundrects
This uses the OvalRenderer to render roundrects as "stretched ovals." It adds an
additional shader that handles the straight edges of ellipsoid roundrects better,
and uses the circle shader for roundrects where the two radii are the same. Only
axis-aligned, simple roundrects are supported. Handles fill, stroke and hairline.
R=bsalomon@google.com, robertphillips@google.com, reed@google.com
Author: jvanverth@google.com
Review URL: https://chromiumcodereview.appspot.com/13852049
git-svn-id: http://skia.googlecode.com/svn/trunk@8859 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | include/core/SkDevice.h | 3 | ||||
-rw-r--r-- | include/core/SkDrawFilter.h | 1 | ||||
-rw-r--r-- | include/gpu/GrContext.h | 11 | ||||
-rw-r--r-- | include/gpu/GrOvalRenderer.h | 12 | ||||
-rw-r--r-- | include/gpu/SkGpuDevice.h | 2 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 16 | ||||
-rw-r--r-- | src/core/SkDevice.cpp | 11 | ||||
-rw-r--r-- | src/gpu/GrContext.cpp | 16 | ||||
-rw-r--r-- | src/gpu/GrOvalRenderer.cpp | 442 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 34 |
10 files changed, 539 insertions, 9 deletions
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index 40931091c4..3d915d8b55 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -259,6 +259,9 @@ protected: const SkPaint& paint); virtual void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint); + virtual void drawRRect(const SkDraw&, const SkRRect& rr, + const SkPaint& paint); + /** * If pathIsMutable, then the implementation is allowed to cast path to a * non-const pointer and modify it in place (as an optimization). Canvas diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h index 6a50ca7ac6..52cbba9d20 100644 --- a/include/core/SkDrawFilter.h +++ b/include/core/SkDrawFilter.h @@ -31,6 +31,7 @@ public: kLine_Type, kBitmap_Type, kRect_Type, + kRRect_Type, kOval_Type, kPath_Type, kText_Type, diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 4d328d1379..d8a7f91d55 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -410,6 +410,17 @@ public: const SkMatrix* localMatrix = NULL); /** + * Draw a roundrect using a paint. + * + * @param paint describes how to color pixels. + * @param rrect the roundrect to draw + * @param stroke the stroke information (width, join, cap) + */ + void drawRRect(const GrPaint& paint, + const SkRRect& rrect, + const SkStrokeRec& stroke); + + /** * Draws a path. * * @param paint describes how to color pixels. diff --git a/include/gpu/GrOvalRenderer.h b/include/gpu/GrOvalRenderer.h index 7e46dac1f8..68e6070b45 100644 --- a/include/gpu/GrOvalRenderer.h +++ b/include/gpu/GrOvalRenderer.h @@ -20,18 +20,20 @@ class GrPaint; class SkStrokeRec; /* - * This class wraps helper functions that draw ovals (filled & stroked) + * This class wraps helper functions that draw ovals and roundrects (filled & stroked) */ class GrOvalRenderer : public GrRefCnt { public: SK_DECLARE_INST_COUNT(GrOvalRenderer) - GrOvalRenderer() {} - + GrOvalRenderer() : fRRectIndexBuffer(NULL) {} ~GrOvalRenderer() {} bool drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke); + bool drawSimpleRRect(GrDrawTarget* target, GrContext* context, const GrPaint& paint, + const SkRRect& rrect, const SkStrokeRec& stroke); + private: bool drawEllipse(GrDrawTarget* target, const GrPaint& paint, const GrRect& ellipse, @@ -40,6 +42,10 @@ private: const GrRect& circle, const SkStrokeRec& stroke); + GrIndexBuffer* rRectIndexBuffer(GrGpu* gpu); + + GrIndexBuffer* fRRectIndexBuffer; + typedef GrRefCnt INHERITED; }; diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index 8aa7d4aac8..ab24703310 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -73,6 +73,8 @@ public: const SkPoint[], const SkPaint& paint) SK_OVERRIDE; virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) SK_OVERRIDE; + virtual void drawRRect(const SkDraw&, const SkRRect& r, + const SkPaint& paint) SK_OVERRIDE; virtual void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) SK_OVERRIDE; virtual void drawPath(const SkDraw&, const SkPath& path, diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index d200ba3088..c491632759 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -1612,12 +1612,20 @@ void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { if (rrect.isRect()) { // call the non-virtual version this->SkCanvas::drawRect(rrect.getBounds(), paint); - } else { - SkPath path; - path.addRRect(rrect); + return; + } else if (rrect.isOval()) { // call the non-virtual version - this->SkCanvas::drawPath(path, paint); + this->SkCanvas::drawOval(rrect.getBounds(), paint); + return; } + + LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type) + + while (iter.next()) { + iter.fDevice->drawRRect(iter, rrect, looper.paint()); + } + + LOOPER_END } diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 90d41862de..dee5bc76af 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -12,6 +12,7 @@ #include "SkMetaData.h" #include "SkRasterClip.h" #include "SkRect.h" +#include "SkRRect.h" #include "SkShader.h" SK_DEFINE_INST_COUNT(SkDevice) @@ -367,6 +368,16 @@ void SkDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& p this->drawPath(draw, path, paint, NULL, true); } +void SkDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) { + CHECK_FOR_NODRAW_ANNOTATION(paint); + + SkPath path; + path.addRRect(rrect); + // call the VIRTUAL version, so any subclasses who do handle drawPath aren't + // required to override drawRRect. + this->drawPath(draw, path, paint, NULL, true); +} + void SkDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 5d0465e65d..0dd0465af9 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -974,6 +974,22 @@ void GrContext::drawVertices(const GrPaint& paint, /////////////////////////////////////////////////////////////////////////////// +void GrContext::drawRRect(const GrPaint& paint, + const SkRRect& rect, + const SkStrokeRec& stroke) { + + GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW); + GrDrawState::AutoStageDisable atr(fDrawState); + + if (!fOvalRenderer->drawSimpleRRect(target, this, paint, rect, stroke)) { + SkPath path; + path.addRRect(rect); + this->internalDrawPath(target, paint, path, stroke); + } +} + +/////////////////////////////////////////////////////////////////////////////// + void GrContext::drawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) { diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index 4b795287af..5ab7a647c6 100644 --- a/src/gpu/GrOvalRenderer.cpp +++ b/src/gpu/GrOvalRenderer.cpp @@ -14,6 +14,9 @@ #include "GrDrawState.h" #include "GrDrawTarget.h" +#include "GrGpu.h" + +#include "SkRRect.h" #include "SkStrokeRec.h" SK_DEFINE_INST_COUNT(GrOvalRenderer) @@ -35,6 +38,13 @@ struct EllipseVertex { GrPoint fInnerOffset; }; +struct RRectVertex { + GrPoint fPos; + GrPoint fOffset; + GrPoint fOuterRadii; + GrPoint fInnerRadii; +}; + inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); } @@ -275,6 +285,146 @@ GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random, /////////////////////////////////////////////////////////////////////////////// +/** + * The output of this effect is a modulation of the input color and coverage for an axis-aligned + * ellipse, specified as an offset vector from center and outer and inner radii in both + * x and y directions. + * + * This uses a slightly different algorithm than the EllipseEdgeEffect, above. Rather than + * scaling an ellipse to be a circle, it attempts to find the distance from the offset point to the + * ellipse by determining where the line through the origin and offset point would cross the + * ellipse, and computing the distance to that. This is slower but works better for roundrects + * because the straight edges will be more accurate. + */ + +class AltEllipseEdgeEffect : public GrEffect { +public: + static GrEffectRef* Create(bool stroke) { + // we go through this so we only have one copy of each effect (stroked/filled) + GR_CREATE_STATIC_EFFECT(gAltEllipseStrokeEdge, AltEllipseEdgeEffect, (true)); + GR_CREATE_STATIC_EFFECT(gAltEllipseFillEdge, AltEllipseEdgeEffect, (false)); + + if (stroke) { + gAltEllipseStrokeEdge->ref(); + return gAltEllipseStrokeEdge; + } else { + gAltEllipseFillEdge->ref(); + return gAltEllipseFillEdge; + } + } + + virtual void getConstantColorComponents(GrColor* color, + uint32_t* validFlags) const SK_OVERRIDE { + *validFlags = 0; + } + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { + return GrTBackendEffectFactory<AltEllipseEdgeEffect>::getInstance(); + } + + virtual ~AltEllipseEdgeEffect() {} + + static const char* Name() { return "RRectEdge"; } + + inline bool isStroked() const { return fStroke; } + + class GLEffect : public GrGLEffect { + public: + GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) + : INHERITED (factory) {} + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TextureSamplerArray& samplers) SK_OVERRIDE { + const AltEllipseEdgeEffect& rrectEffect = drawEffect.castEffect<AltEllipseEdgeEffect>(); + + const char *vsOffsetName, *fsOffsetName; + const char *vsRadiiName, *fsRadiiName; + + builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName); + const SkString* attr0Name = + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); + builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str()); + + builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName); + const SkString* attr1Name = + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); + builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str()); + + builder->fsCodeAppend("\tfloat edgeAlpha;\n"); + // get length of offset + builder->fsCodeAppendf("\tfloat len = length(%s.xy);\n", fsOffsetName); + builder->fsCodeAppend("\tvec2 offset;\n"); + + // for outer curve + builder->fsCodeAppendf("\toffset.xy = %s.xy*%s.yx;\n", + fsOffsetName, fsRadiiName); + builder->fsCodeAppendf("\tfloat tOuter = " + "%s.x*%s.y*inversesqrt(dot(offset.xy, offset.xy));\n", + fsRadiiName, fsRadiiName); + builder->fsCodeAppend("\tedgeAlpha = clamp(len*tOuter - len, 0.0, 1.0);\n"); + + // for inner curve + if (rrectEffect.isStroked()) { + builder->fsCodeAppendf("\toffset.xy = %s.xy*%s.wz;\n", + fsOffsetName, fsRadiiName); + builder->fsCodeAppendf("\tfloat tInner = " + "%s.z*%s.w*inversesqrt(dot(offset.xy, offset.xy));\n", + fsRadiiName, fsRadiiName); + builder->fsCodeAppend("\tedgeAlpha *= clamp(len - len*tInner, 0.0, 1.0);\n"); + } + + SkString modulate; + GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); + } + + static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { + const AltEllipseEdgeEffect& rrectEffect = drawEffect.castEffect<AltEllipseEdgeEffect>(); + + return rrectEffect.isStroked() ? 0x1 : 0x0; + } + + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { + } + + private: + typedef GrGLEffect INHERITED; + }; + +private: + AltEllipseEdgeEffect(bool stroke) : GrEffect() { + this->addVertexAttrib(kVec2f_GrSLType); + this->addVertexAttrib(kVec4f_GrSLType); + fStroke = stroke; + } + + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { + const AltEllipseEdgeEffect& aeee = CastEffect<AltEllipseEdgeEffect>(other); + return aeee.fStroke == fStroke; + } + + bool fStroke; + + GR_DECLARE_EFFECT_TEST; + + typedef GrEffect INHERITED; +}; + +GR_DEFINE_EFFECT_TEST(AltEllipseEdgeEffect); + +GrEffectRef* AltEllipseEdgeEffect::TestCreate(SkMWCRandom* random, + GrContext* context, + const GrDrawTargetCaps&, + GrTexture* textures[]) { + return AltEllipseEdgeEffect::Create(random->nextBool()); +} + +/////////////////////////////////////////////////////////////////////////////// + bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) { @@ -302,6 +452,8 @@ bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, co namespace { +/////////////////////////////////////////////////////////////////////////////// + // position + edge extern const GrVertexAttrib gCircleVertexAttribs[] = { {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, @@ -405,6 +557,8 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target, target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); } +/////////////////////////////////////////////////////////////////////////////// + namespace { // position + edge @@ -485,8 +639,6 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, SkScalar innerRatio = 1.0f; if (SkStrokeRec::kFill_Style != style) { - - if (SkScalarNearlyZero(scaledStroke.length())) { scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); } else { @@ -552,3 +704,289 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, return true; } + +/////////////////////////////////////////////////////////////////////////////// + +static const uint16_t gRRectIndices[] = { + // corners + 0, 1, 5, 0, 5, 4, + 2, 3, 7, 2, 7, 6, + 8, 9, 13, 8, 13, 12, + 10, 11, 15, 10, 15, 14, + + // edges + 1, 2, 6, 1, 6, 5, + 4, 5, 9, 4, 9, 8, + 6, 7, 11, 6, 11, 10, + 9, 10, 14, 9, 14, 13, + + // center + // we place this at the end so that we can ignore these indices when rendering stroke-only + 5, 6, 10, 5, 10, 9 +}; + + +GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) { + if (NULL == fRRectIndexBuffer) { + fRRectIndexBuffer = + gpu->createIndexBuffer(sizeof(gRRectIndices), false); + if (NULL != fRRectIndexBuffer) { +#if GR_DEBUG + bool updated = +#endif + fRRectIndexBuffer->updateData(gRRectIndices, + sizeof(gRRectIndices)); + GR_DEBUGASSERT(updated); + } + } + return fRRectIndexBuffer; +} + +bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, + const GrPaint& paint, const SkRRect& rrect, + const SkStrokeRec& stroke) +{ + const SkMatrix& vm = context->getMatrix(); +#ifdef SK_DEBUG + { + // we should have checked for this previously + SkASSERT(paint.isAntiAlias() && vm.rectStaysRect() && rrect.isSimple()); + } +#endif + + // do any matrix crunching before we reset the draw state for device coords + const SkRect& rrectBounds = rrect.getBounds(); + SkRect bounds; + vm.mapRect(&bounds, rrectBounds); + + SkVector radii = rrect.getSimpleRadii(); + SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX + + vm[SkMatrix::kMSkewY]*radii.fY); + SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX + + vm[SkMatrix::kMScaleY]*radii.fY); + // tall or wide quarter-ellipse corners aren't handled + if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) { + return false; + } + // 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) { + return false; + } + + // reset to device coordinates + GrDrawState* drawState = target->drawState(); + GrDrawState::AutoDeviceCoordDraw adcd(drawState); + if (!adcd.succeeded()) { + return false; + } + + 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, + }; + + GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu()); + if (NULL == indexBuffer) { + GrPrintf("Failed to create index buffer!\n"); + return false; + } + + // if the corners are circles, use the circle renderer + if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { + drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); + GrAssert(sizeof(CircleVertex) == drawState->getVertexSize()); + + GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); + if (!geo.succeeded()) { + GrPrintf("Failed to get space for vertices!\n"); + return false; + } + CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); + + GrEffectRef* effect = CircleEdgeEffect::Create(isStroked); + static const int kCircleEdgeAttrIndex = 1; + drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref(); + + SkScalar innerRadius = 0.0f; + SkScalar outerRadius = xRadius; + SkScalar halfWidth = 0; + if (style != SkStrokeRec::kFill_Style) { + if (SkScalarNearlyZero(scaledStroke.fX)) { + halfWidth = SK_ScalarHalf; + } else { + halfWidth = SkScalarHalf(scaledStroke.fX); + } + + if (isStroked) { + innerRadius = SkMaxScalar(0, xRadius - halfWidth); + } + outerRadius += halfWidth; + bounds.outset(halfWidth, halfWidth); + } + + // The radii are outset for two reasons. First, it allows the shader to simply perform + // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the + // verts of the bounding box that is rendered and the outset ensures the box will cover all + // pixels partially covered by the circle. + outerRadius += SK_ScalarHalf; + innerRadius -= SK_ScalarHalf; + + // Expand the rect so all the pixels will be captured. + bounds.outset(SK_ScalarHalf, SK_ScalarHalf); + + SkScalar yCoords[4] = { + bounds.fTop, + bounds.fTop + outerRadius, + bounds.fBottom - outerRadius, + bounds.fBottom + }; + SkScalar yOuterRadii[4] = { + -outerRadius, + 0, + 0, + outerRadius + }; + for (int i = 0; i < 4; ++i) { + verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); + verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]); + verts->fOuterRadius = outerRadius; + verts->fInnerRadius = innerRadius; + verts++; + + verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); + verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); + verts->fOuterRadius = outerRadius; + verts->fInnerRadius = innerRadius; + verts++; + + verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); + verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); + verts->fOuterRadius = outerRadius; + verts->fInnerRadius = innerRadius; + verts++; + + verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); + verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]); + verts->fOuterRadius = outerRadius; + verts->fInnerRadius = innerRadius; + verts++; + } + + // drop out the middle quad if we're stroked + int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices); + target->setIndexSourceToBuffer(indexBuffer); + target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); + + // otherwise we use the special ellipse renderer + } else { + + drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); + GrAssert(sizeof(RRectVertex) == drawState->getVertexSize()); + + GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); + if (!geo.succeeded()) { + GrPrintf("Failed to get space for vertices!\n"); + return false; + } + RRectVertex* verts = reinterpret_cast<RRectVertex*>(geo.vertices()); + + GrEffectRef* effect = AltEllipseEdgeEffect::Create(isStroked); + static const int kEllipseOffsetAttrIndex = 1; + static const int kEllipseRadiiAttrIndex = 2; + drawState->setEffect(kEdgeEffectStage, effect, + kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref(); + + SkScalar innerXRadius = 0.0f; + SkScalar innerYRadius = 0.0f; + + if (SkStrokeRec::kFill_Style != style) { + 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) { + innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX); + innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY); + } + xRadius += scaledStroke.fX; + yRadius += scaledStroke.fY; + bounds.outset(scaledStroke.fX, scaledStroke.fY); + } + + // Extend the radii out half a pixel to antialias. + SkScalar xOuterRadius = xRadius + SK_ScalarHalf; + SkScalar yOuterRadius = yRadius + SK_ScalarHalf; + SkScalar xInnerRadius = SkMaxScalar(innerXRadius - SK_ScalarHalf, 0); + SkScalar yInnerRadius = SkMaxScalar(innerYRadius - SK_ScalarHalf, 0); + + // Expand the rect so all the pixels will be captured. + bounds.outset(SK_ScalarHalf, SK_ScalarHalf); + + SkScalar yCoords[4] = { + bounds.fTop, + bounds.fTop + yOuterRadius, + bounds.fBottom - yOuterRadius, + bounds.fBottom + }; + SkScalar yOuterOffsets[4] = { + -yOuterRadius, + SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0 + SK_ScalarNearlyZero, + yOuterRadius + }; + + for (int i = 0; i < 4; ++i) { + verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); + verts->fOffset = SkPoint::Make(-xOuterRadius, yOuterOffsets[i]); + verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius); + verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius); + verts++; + + verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); + verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); + verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius); + verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius); + verts++; + + verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); + verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); + verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius); + verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius); + verts++; + + verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); + verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); + verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius); + verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius); + verts++; + } + + // drop out the middle quad if we're stroked + int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices); + target->setIndexSourceToBuffer(indexBuffer); + target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); + } + + return true; +} + diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 299cf3de66..0ee17e839a 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -21,6 +21,7 @@ #include "SkGlyphCache.h" #include "SkImageFilter.h" #include "SkPathEffect.h" +#include "SkRRect.h" #include "SkStroke.h" #include "SkUtils.h" @@ -707,6 +708,39 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, /////////////////////////////////////////////////////////////////////////////// +void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, + const SkPaint& paint) { + CHECK_FOR_NODRAW_ANNOTATION(paint); + CHECK_SHOULD_DRAW(draw, false); + + bool usePath = !rect.isSimple() || !paint.isAntiAlias(); + // 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); + return; + } + + GrPaint grPaint; + if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { + return; + } + + SkStrokeRec stroke(paint); + fContext->drawRRect(grPaint, rect, stroke); +} + +/////////////////////////////////////////////////////////////////////////////// + void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { CHECK_FOR_NODRAW_ANNOTATION(paint); |