aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-04-09 21:26:11 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-04-09 21:26:11 +0000
commit0a09d7195b8d9945e5c9c76cc4cfe6ef65d6d390 (patch)
treec45daab7ffc6c83198376c64719b9762a7cddcd2 /src
parent149e9a107c356b0151433fb23c2b1c8d0634947c (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrContext.cpp34
-rw-r--r--src/gpu/GrOvalRenderer.cpp127
-rw-r--r--src/gpu/GrOvalRenderer.h6
-rw-r--r--src/gpu/SkGpuDevice.cpp38
-rw-r--r--src/gpu/effects/GrOvalEffect.cpp11
5 files changed, 160 insertions, 56 deletions
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<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);
}
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<CircleEffect>();
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<CircleEffect>();
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();
}