#include "SkRRect.h"
#include "SkStrokeRec.h"
+#include "SkTLazy.h"
#include "effects/GrVertexEffect.h"
+#include "effects/GrRRectEffect.h"
namespace {
struct CircleVertex {
- GrPoint fPos;
- GrPoint fOffset;
+ SkPoint fPos;
+ SkPoint fOffset;
SkScalar fOuterRadius;
SkScalar fInnerRadius;
};
struct EllipseVertex {
- GrPoint fPos;
- GrPoint fOffset;
- GrPoint fOuterRadii;
- GrPoint fInnerRadii;
+ SkPoint fPos;
+ SkPoint fOffset;
+ SkPoint fOuterRadii;
+ SkPoint fInnerRadii;
};
struct DIEllipseVertex {
- GrPoint fPos;
- GrPoint fOuterOffset;
- GrPoint fInnerOffset;
+ SkPoint fPos;
+ SkPoint fOuterOffset;
+ SkPoint fInnerOffset;
};
inline bool circle_stays_circle(const SkMatrix& m) {
builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
- // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
- // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
- if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
- builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
- }
+ // avoid calling inversesqrt on zero.
+ builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
fsOffsetName0, fsOffsetName0, fsOffsetName0, fsOffsetName0);
builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
- // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
- // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
- if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
- builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
- }
+ // avoid calling inversesqrt on zero.
+ builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
if (kHairline == ellipseEffect.getMode()) {
// can probably do this with one step
// position + edge
extern const GrVertexAttrib gCircleVertexAttribs[] = {
{kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
- {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
+ {kVec4f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}
};
void GrOvalRenderer::drawCircle(GrDrawTarget* target,
GrDrawState* drawState = target->drawState();
const SkMatrix& vm = drawState->getViewMatrix();
- GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
+ SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
vm.mapPoints(¢er, 1);
SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
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 {
}
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();
// position + offset + 1/radii
extern const GrVertexAttrib gEllipseVertexAttribs[] = {
{kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
- {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
- {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
+ {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding},
+ {kVec4f_GrVertexAttribType, 2*sizeof(SkPoint), kEffect_GrVertexAttribBinding}
};
// position + offsets
extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
{kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
- {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
- {kVec2f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding},
+ {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding},
+ {kVec2f_GrVertexAttribType, 2*sizeof(SkPoint), kEffect_GrVertexAttribBinding},
};
bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
// do any matrix crunching before we reset the draw state for device coords
const SkMatrix& vm = drawState->getViewMatrix();
- GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
+ SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
vm.mapPoints(¢er, 1);
SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
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 {
}
// 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;
}
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;
GrDrawState* drawState = target->drawState();
const SkMatrix& vm = drawState->getViewMatrix();
- GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
+ SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
SkScalar xRadius = SkScalarHalf(ellipse.width());
SkScalar yRadius = SkScalarHalf(ellipse.height());
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();
}
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();
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;
}
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");
}
// 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());
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();
}
// drop out the middle quad if we're stroked
- int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_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);
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 {
}
// 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;
}
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()) {
}
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,
}
// drop out the middle quad if we're stroked
- int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_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);
}