Index: src/gpu/GrOvalRenderer.cpp |
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp |
index 2ade8cde07cad184f44c48bd34d6e27d243249ac..45564bcce049c8c3cbf457d39da9a1efe3cd702f 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<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 { |
@@ -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<EllipseVertex*>(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<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(); |
@@ -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<gCircleVertexAttribs>(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<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, |
@@ -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); |
} |