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:
parent
05141c23ed
commit
93c9660cd1
@ -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
|
||||
*/
|
||||
|
@ -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(¢er, 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,
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user