draw circle paths directly via GPU

Review URL: http://codereview.appspot.com/5696086/

Submitted on behalf of Guanqun.Lu@gmail.com




git-svn-id: http://skia.googlecode.com/svn/trunk@3772 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2012-04-27 13:05:21 +00:00
parent 05141c23ed
commit 93c9660cd1
4 changed files with 175 additions and 5 deletions

View File

@ -439,6 +439,20 @@ public:
const uint16_t indices[],
int indexCount);
/**
* Draws an oval.
*
* @param paint describes how to color pixels.
* @param rect the bounding rect of the oval.
* @param strokeWidth if strokeWidth < 0, then the oval is filled, else
* the rect is stroked based on strokeWidth. If
* strokeWidth == 0, then the stroke is always a single
* pixel thick.
*/
void drawOval(const GrPaint& paint,
const GrRect& rect,
SkScalar strokeWidth);
///////////////////////////////////////////////////////////////////////////
// Misc.
@ -719,6 +733,9 @@ private:
const GrDrawTarget* target,
bool antiAlias);
void internalDrawPath(const GrPaint& paint, const GrPath& path,
GrPathFill fill, const GrPoint* translate);
/**
* Flags to the internal read/write pixels funcs
*/

View File

@ -1435,6 +1435,123 @@ void draw_around_inv_path(GrDrawTarget* target,
}
}
struct CircleVertex {
GrPoint fPos;
GrPoint fCenter;
GrScalar fOuterRadius;
GrScalar fInnerRadius;
};
/* Returns true if will map a circle to another circle. This can be true
* if the matrix only includes square-scale, rotation, translation.
*/
inline bool isSimilarityTransformation(const SkMatrix& matrix,
SkScalar tol = SK_ScalarNearlyZero) {
if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
return true;
}
if (matrix.hasPerspective()) {
return false;
}
SkScalar mx = matrix.get(SkMatrix::kMScaleX);
SkScalar sx = matrix.get(SkMatrix::kMSkewX);
SkScalar my = matrix.get(SkMatrix::kMScaleY);
SkScalar sy = matrix.get(SkMatrix::kMSkewY);
if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
return false;
}
// it has scales or skews, but it could also be rotation, check it out.
SkVector vec[2];
vec[0].set(mx, sx);
vec[1].set(sy, my);
return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
SkScalarSquare(tol));
}
}
// TODO: strokeWidth can't be larger than zero right now.
// It will be fixed when drawPath() can handle strokes.
void GrContext::drawOval(const GrPaint& paint,
const GrRect& rect,
SkScalar strokeWidth) {
DrawCategory category = (DEFER_PATHS) ? kBuffered_DrawCategory :
kUnbuffered_DrawCategory;
GrDrawTarget* target = this->prepareToDraw(paint, category);
GrDrawState* drawState = target->drawState();
GrMatrix vm = drawState->getViewMatrix();
if (!isSimilarityTransformation(vm) ||
!paint.fAntiAlias ||
rect.height() != rect.width()) {
SkPath path;
path.addOval(rect);
GrPathFill fill = (strokeWidth == 0) ?
kHairLine_PathFill : kWinding_PathFill;
this->internalDrawPath(paint, path, fill, NULL);
return;
}
const GrRenderTarget* rt = drawState->getRenderTarget();
if (NULL == rt) {
return;
}
GrDrawTarget::AutoDeviceCoordDraw adcd(target, paint.getActiveStageMask());
GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
layout |= GrDrawTarget::kEdge_VertexLayoutBit;
GrAssert(sizeof(CircleVertex) == GrDrawTarget::VertexSize(layout));
GrPoint center = GrPoint::Make(rect.centerX(), rect.centerY());
GrScalar radius = SkScalarHalf(rect.width());
vm.mapPoints(&center, 1);
radius = vm.mapRadius(radius);
GrScalar outerRadius = radius;
GrScalar innerRadius = 0;
SkScalar halfWidth = 0;
if (strokeWidth == 0) {
halfWidth = SkScalarHalf(SK_Scalar1);
outerRadius += halfWidth;
innerRadius = SkMaxScalar(0, radius - halfWidth);
}
GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0);
if (!geo.succeeded()) {
GrPrintf("Failed to get space for vertices!\n");
return;
}
CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
SkScalar L = center.fX - outerRadius;
SkScalar R = center.fX + outerRadius;
SkScalar T = center.fY - outerRadius;
SkScalar B = center.fY + outerRadius;
verts[0].fPos = SkPoint::Make(L, T);
verts[1].fPos = SkPoint::Make(R, T);
verts[2].fPos = SkPoint::Make(L, B);
verts[3].fPos = SkPoint::Make(R, B);
for (int i = 0; i < 4; ++i) {
// this goes to fragment shader, it should be in y-points-up space.
verts[i].fCenter = SkPoint::Make(center.fX, rt->height() - center.fY);
verts[i].fOuterRadius = outerRadius;
verts[i].fInnerRadius = innerRadius;
}
drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
target->drawNonIndexed(kTriangleStrip_PrimitiveType, 0, 4);
}
@ -1499,6 +1616,22 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
return;
}
SkRect ovalRect;
if (!GrIsFillInverted(fill) && path.isOval(&ovalRect)) {
if (translate) {
ovalRect.offset(*translate);
}
SkScalar width = (fill == kHairLine_PathFill) ? 0 : -1;
this->drawOval(paint, ovalRect, width);
return;
}
internalDrawPath(paint, path, fill, translate);
}
void GrContext::internalDrawPath(const GrPaint& paint, const GrPath& path,
GrPathFill fill, const GrPoint* translate) {
// Note that below we may sw-rasterize the path into a scratch texture.
// Scratch textures can be recycled after they are returned to the texture
// cache. This presents a potential hazard for buffered drawing. However,

View File

@ -539,6 +539,9 @@ public:
* When specifying edges as vertex data this enum specifies what type of
* edges are in use. The edges are always 4 GrScalars in memory, even when
* the edge type requires fewer than 4.
*
* TODO: Fix the fact that HairLine and Circle edge types use y-down coords.
* (either adjust in VS or use origin_upper_left in GLSL)
*/
enum VertexEdgeType {
/* 1-pixel wide line
@ -546,11 +549,15 @@ public:
kHairLine_EdgeType,
/* Quadratic specified by u^2-v canonical coords (only 2
components used). Coverage based on signed distance with negative
being inside, positive outside.*/
being inside, positive outside. Edge specified in window space
(y-down) */
kQuad_EdgeType,
/* Same as above but for hairline quadratics. Uses unsigned distance.
Coverage is min(0, 1-distance). */
kHairQuad_EdgeType,
/* Circle specified as center_x, center_y, outer_radius, inner_radius
all in window space (y-down). */
kCircle_EdgeType,
kVertexEdgeTypeCnt
};

View File

@ -471,10 +471,12 @@ void GrGLProgram::genEdgeCoverage(const GrGLContextInfo& gl,
segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
switch (fProgramDesc.fVertexEdgeType) {
case GrDrawState::kHairLine_EdgeType:
segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
} else if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
break;
case GrDrawState::kQuad_EdgeType:
segments->fFSCode.append("\tfloat edgeAlpha;\n");
// keep the derivative instructions outside the conditional
segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
@ -492,8 +494,8 @@ void GrGLProgram::genEdgeCoverage(const GrGLContextInfo& gl,
if (kES2_GrGLBinding == gl.binding()) {
segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
}
} else {
GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
break;
case GrDrawState::kHairQuad_EdgeType:
segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
@ -505,6 +507,17 @@ void GrGLProgram::genEdgeCoverage(const GrGLContextInfo& gl,
if (kES2_GrGLBinding == gl.binding()) {
segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
}
break;
case GrDrawState::kCircle_EdgeType:
segments->fFSCode.append("\tfloat edgeAlpha;\n");
segments->fFSCode.appendf("\tfloat d = distance(gl_FragCoord.xy, %s.xy);\n", fsName);
segments->fFSCode.appendf("\tfloat outerAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
segments->fFSCode.appendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
segments->fFSCode.append("\tedgeAlpha = outerAlpha * innerAlpha;\n");
break;
default:
GrCrash("Unknown Edge Type!");
break;
}
*coverageVar = "edgeAlpha";
} else {