First pass at Rect Effect

https://codereview.chromium.org/13521006/



git-svn-id: http://skia.googlecode.com/svn/trunk@8571 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
robertphillips@google.com 2013-04-09 14:01:44 +00:00
parent 62f60bb1d4
commit df3695e5c7
6 changed files with 320 additions and 17 deletions

View File

@ -90,6 +90,12 @@ public:
*/ */
bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const; bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
/** Returns true if the matrix contains only translation, rotation or scale
(non-uniform scale is allowed).
Returns false if other transformation types are included or is degenerate
*/
bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const;
enum { enum {
kMScaleX, kMScaleX,
kMSkewX, kMSkewX,

View File

@ -15,6 +15,7 @@
class GrGpu; class GrGpu;
class GrDrawTarget; class GrDrawTarget;
class GrIndexBuffer; class GrIndexBuffer;
class SkMatrix;
/* /*
* This class wraps helper functions that draw AA rects (filled & stroked) * This class wraps helper functions that draw AA rects (filled & stroked)
@ -35,7 +36,7 @@ public:
} }
// TODO: potentialy fuse the fill & stroke methods and differentiate // TODO: potentialy fuse the fill & stroke methods and differentiate
// btween them by passing in strokeWidth (<0 means fill). // between them by passing in strokeWidth (<0 means fill).
// TODO: Remove the useVertexCoverage boolean. Just use it all the time // TODO: Remove the useVertexCoverage boolean. Just use it all the time
// since we now have a coverage vertex attribute // since we now have a coverage vertex attribute
@ -44,6 +45,13 @@ public:
const GrRect& devRect, const GrRect& devRect,
bool useVertexCoverage); bool useVertexCoverage);
void shaderFillAARect(GrGpu* gpu,
GrDrawTarget* target,
const GrRect& rect,
const SkMatrix& combinedMatrix,
const GrRect& devRect,
bool useVertexCoverage);
void strokeAARect(GrGpu* gpu, void strokeAARect(GrGpu* gpu,
GrDrawTarget* target, GrDrawTarget* target,
const GrRect& devRect, const GrRect& devRect,

View File

@ -189,6 +189,7 @@ bool SkMatrix::isSimilarity(SkScalar tol) const {
SkScalar sx = fMat[kMSkewX]; SkScalar sx = fMat[kMSkewX];
SkScalar sy = fMat[kMSkewY]; SkScalar sy = fMat[kMSkewY];
// TODO: I (rphillips) think there should be an || in here (see preservesRightAngles)
// degenerate matrix, non-similarity // degenerate matrix, non-similarity
if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my) if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my)
&& SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) { && SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) {
@ -205,6 +206,40 @@ bool SkMatrix::isSimilarity(SkScalar tol) const {
SkScalarSquare(tol)); SkScalarSquare(tol));
} }
bool SkMatrix::preservesRightAngles(SkScalar tol) const {
TypeMask mask = this->getType();
if (mask <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
// identity, translate and/or scale
return true;
}
if (mask & kPerspective_Mask) {
return false;
}
SkASSERT(mask & kAffine_Mask);
SkScalar mx = fMat[kMScaleX];
SkScalar my = fMat[kMScaleY];
SkScalar sx = fMat[kMSkewX];
SkScalar sy = fMat[kMSkewY];
if ((SkScalarNearlyZero(mx) && SkScalarNearlyZero(sx)) ||
(SkScalarNearlyZero(my) && SkScalarNearlyZero(sy))) {
// degenerate matrix
return false;
}
// it has scales and 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));
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) { void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {

View File

@ -8,9 +8,138 @@
#include "GrAARectRenderer.h" #include "GrAARectRenderer.h"
#include "GrRefCnt.h" #include "GrRefCnt.h"
#include "GrGpu.h" #include "GrGpu.h"
#include "gl/GrGLEffect.h"
#include "GrTBackendEffectFactory.h"
SK_DEFINE_INST_COUNT(GrAARectRenderer) SK_DEFINE_INST_COUNT(GrAARectRenderer)
class GrGLRectEffect;
/**
* The output of this effect is a modulation of the input color and coverage
* for an arbitrarily oriented rect. The rect is specified as:
* Center of the rect
* Unit vector point down the height of the rect
* Half width + 0.5
* Half height + 0.5
* The center and vector are stored in a vec4 varying ("RectEdge") with the
* center in the xy components and the vector in the zw components.
* The munged width and height are stored in a vec2 varying ("WidthHeight")
* with the width in x and the height in y.
*/
class GrRectEffect : public GrEffect {
public:
static GrEffectRef* Create() {
static SkAutoTUnref<GrEffectRef> gRectEffectRef(
CreateEffectRef(AutoEffectUnref(SkNEW(GrRectEffect))));
gRectEffectRef.get()->ref();
return gRectEffectRef;
}
virtual ~GrRectEffect() {}
static const char* Name() { return "RectEdge"; }
virtual void getConstantColorComponents(GrColor* color,
uint32_t* validFlags) const SK_OVERRIDE {
*validFlags = 0;
}
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
return GrTBackendEffectFactory<GrRectEffect>::getInstance();
}
class GLEffect : public GrGLEffect {
public:
GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
: INHERITED (factory) {}
virtual void emitCode(GrGLShaderBuilder* builder,
const GrDrawEffect& drawEffect,
EffectKey key,
const char* outputColor,
const char* inputColor,
const TextureSamplerArray& samplers) SK_OVERRIDE {
// setup the varying for the center point and the unit vector
// that points down the height of the rect
const char *vsRectEdgeName, *fsRectEdgeName;
builder->addVarying(kVec4f_GrSLType, "RectEdge",
&vsRectEdgeName, &fsRectEdgeName);
const SkString* attr0Name =
builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
builder->vsCodeAppendf("\t%s = %s;\n", vsRectEdgeName, attr0Name->c_str());
// setup the varying for width/2+.5 and height/2+.5
const char *vsWidthHeightName, *fsWidthHeightName;
builder->addVarying(kVec2f_GrSLType, "WidthHeight",
&vsWidthHeightName, &fsWidthHeightName);
const SkString* attr1Name =
builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
builder->vsCodeAppendf("\t%s = %s;\n", vsWidthHeightName, attr1Name->c_str());
// TODO: compute these scale factors in the VS
// These scale factors adjust the coverage for < 1 pixel wide/high rects
builder->fsCodeAppendf("\tfloat wScale = max(1.0, 2.0/(0.5+%s.x));\n",
fsWidthHeightName);
builder->fsCodeAppendf("\tfloat hScale = max(1.0, 2.0/(0.5+%s.y));\n",
fsWidthHeightName);
// Compute the coverage for the rect's width
builder->fsCodeAppendf("\tvec2 offset = %s.xy - %s.xy;\n",
builder->fragmentPosition(), fsRectEdgeName);
builder->fsCodeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n",
fsRectEdgeName, fsRectEdgeName);
builder->fsCodeAppendf("\tfloat coverage = clamp(wScale*(%s.x-perpDot), 0.0, 1.0);\n",
fsWidthHeightName);
// Compute the coverage for the rect's height and merge with the width
builder->fsCodeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n",
fsRectEdgeName);
builder->fsCodeAppendf(
"\tcoverage = min(coverage, clamp(hScale*(%s.y-perpDot), 0.0, 1.0));\n",
fsWidthHeightName);
SkString modulate;
GrGLSLModulate4f(&modulate, inputColor, "coverage");
builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
}
static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
return 0;
}
virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {}
private:
typedef GrGLEffect INHERITED;
};
private:
GrRectEffect::GrRectEffect() : GrEffect() {
this->addVertexAttrib(kVec4f_GrSLType);
this->addVertexAttrib(kVec2f_GrSLType);
}
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; }
GR_DECLARE_EFFECT_TEST;
typedef GrEffect INHERITED;
};
GR_DEFINE_EFFECT_TEST(GrRectEffect);
GrEffectRef* GrRectEffect::TestCreate(SkMWCRandom* random,
GrContext* context,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
return GrRectEffect::Create();
}
///////////////////////////////////////////////////////////////////////////////
namespace { namespace {
static void aa_rect_attributes(bool useCoverage, const GrVertexAttrib** attribs, int* count) { static void aa_rect_attributes(bool useCoverage, const GrVertexAttrib** attribs, int* count) {
@ -181,6 +310,92 @@ void GrAARectRenderer::fillAARect(GrGpu* gpu,
target->resetIndexSource(); target->resetIndexSource();
} }
struct RectVertex {
GrPoint fPos;
GrPoint fCenter;
GrPoint fDir;
GrPoint fWidthHeight;
};
void GrAARectRenderer::shaderFillAARect(GrGpu* gpu,
GrDrawTarget* target,
const GrRect& rect,
const SkMatrix& combinedMatrix,
const GrRect& devRect,
bool useVertexCoverage) {
GrDrawState* drawState = target->drawState();
SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
combinedMatrix.mapPoints(&center, 1);
// compute transformed (0, 1) vector
SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] };
dir.normalize();
// compute transformed (width, 0) and (0, height) vectors
SkVector vec[2] = {
{ combinedMatrix[SkMatrix::kMScaleX] * rect.width(),
combinedMatrix[SkMatrix::kMSkewY] * rect.width() },
{ combinedMatrix[SkMatrix::kMSkewX] * rect.height(),
combinedMatrix[SkMatrix::kMScaleY] * rect.height() }
};
SkScalar newWidth = vec[0].length() / 2.0f + 0.5f;
SkScalar newHeight = vec[1].length() / 2.0f + 0.5f;
static const GrVertexAttrib kVertexAttribs[] = {
{ kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
{ kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding },
{ kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding }
};
drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
GrAssert(sizeof(RectVertex) == drawState->getVertexSize());
GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
if (!geo.succeeded()) {
GrPrintf("Failed to get space for vertices!\n");
return;
}
RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
enum {
// the edge effects share this stage with glyph rendering
// (kGlyphMaskStage in GrTextContext) && SW path rendering
// (kPathMaskStage in GrSWMaskHelper)
kEdgeEffectStage = GrPaint::kTotalStages,
};
GrEffectRef* effect = GrRectEffect::Create();
static const int kRectAttrIndex = 1;
static const int kWidthIndex = 2;
drawState->setEffect(kEdgeEffectStage, effect, kRectAttrIndex, kWidthIndex)->unref();
for (int i = 0; i < 4; ++i) {
verts[i].fCenter = center;
verts[i].fDir = dir;
verts[i].fWidthHeight.fX = newWidth;
verts[i].fWidthHeight.fY = newHeight;
}
SkRect devBounds = {
devRect.fLeft - SK_ScalarHalf,
devRect.fTop - SK_ScalarHalf,
devRect.fRight + SK_ScalarHalf,
devRect.fBottom + SK_ScalarHalf
};
verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
target->resetIndexSource();
}
void GrAARectRenderer::strokeAARect(GrGpu* gpu, void GrAARectRenderer::strokeAARect(GrGpu* gpu,
GrDrawTarget* target, GrDrawTarget* target,
const GrRect& devRect, const GrRect& devRect,

View File

@ -682,7 +682,7 @@ static bool isIRect(const GrRect& r) {
static bool apply_aa_to_rect(GrDrawTarget* target, static bool apply_aa_to_rect(GrDrawTarget* target,
const GrRect& rect, const GrRect& rect,
SkScalar width, SkScalar strokeWidth,
const SkMatrix* matrix, const SkMatrix* matrix,
SkMatrix* combinedMatrix, SkMatrix* combinedMatrix,
GrRect* devRect, GrRect* devRect,
@ -711,29 +711,54 @@ static bool apply_aa_to_rect(GrDrawTarget* target,
return false; return false;
} }
if (0 == width && target->willUseHWAALines()) { if (0 == strokeWidth && target->willUseHWAALines()) {
return false; return false;
} }
#ifdef SHADER_AA_FILL_RECT
if (strokeWidth >= 0) {
#endif
if (!drawState.getViewMatrix().preservesAxisAlignment()) { if (!drawState.getViewMatrix().preservesAxisAlignment()) {
return false; return false;
} }
if (NULL != matrix && if (NULL != matrix && !matrix->preservesAxisAlignment()) {
!matrix->preservesAxisAlignment()) {
return false; return false;
} }
#ifdef SHADER_AA_FILL_RECT
} else {
if (!drawState.getViewMatrix().preservesAxisAlignment() &&
!drawState.getViewMatrix().preservesRightAngles()) {
return false;
}
if (NULL != matrix && !matrix->preservesRightAngles()) {
return false;
}
}
#endif
*combinedMatrix = drawState.getViewMatrix(); *combinedMatrix = drawState.getViewMatrix();
if (NULL != matrix) { if (NULL != matrix) {
combinedMatrix->preConcat(*matrix); combinedMatrix->preConcat(*matrix);
#if GR_DEBUG
#ifdef SHADER_AA_FILL_RECT
if (strokeWidth >= 0) {
#endif
GrAssert(combinedMatrix->preservesAxisAlignment()); GrAssert(combinedMatrix->preservesAxisAlignment());
#ifdef SHADER_AA_FILL_RECT
} else {
GrAssert(combinedMatrix->preservesRightAngles());
}
#endif
#endif
} }
combinedMatrix->mapRect(devRect, rect); combinedMatrix->mapRect(devRect, rect);
devRect->sort(); devRect->sort();
if (width < 0) { if (strokeWidth < 0) {
return !isIRect(*devRect); return !isIRect(*devRect);
} else { } else {
return true; return true;
@ -757,7 +782,6 @@ void GrContext::drawRect(const GrPaint& paint,
bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix, bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
&combinedMatrix, &devRect, &combinedMatrix, &devRect,
&useVertexCoverage); &useVertexCoverage);
if (doAA) { if (doAA) {
GrDrawState::AutoDeviceCoordDraw adcd(target->drawState()); GrDrawState::AutoDeviceCoordDraw adcd(target->drawState());
if (!adcd.succeeded()) { if (!adcd.succeeded()) {
@ -775,8 +799,15 @@ void GrContext::drawRect(const GrPaint& paint,
fAARectRenderer->strokeAARect(this->getGpu(), target, devRect, fAARectRenderer->strokeAARect(this->getGpu(), target, devRect,
strokeSize, useVertexCoverage); strokeSize, useVertexCoverage);
} else { } else {
// filled AA rect
#ifdef SHADER_AA_FILL_RECT
fAARectRenderer->shaderFillAARect(this->getGpu(), target,
rect, combinedMatrix, devRect,
useVertexCoverage);
#else
fAARectRenderer->fillAARect(this->getGpu(), target, fAARectRenderer->fillAARect(this->getGpu(), target,
devRect, useVertexCoverage); devRect, useVertexCoverage);
#endif
} }
return; return;
} }
@ -822,6 +853,7 @@ void GrContext::drawRect(const GrPaint& paint,
target->drawNonIndexed(primType, 0, vertCount); target->drawNonIndexed(primType, 0, vertCount);
} else { } else {
// filled BW rect
#if GR_STATIC_RECT_VB #if GR_STATIC_RECT_VB
const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer(); const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
if (NULL == sqVB) { if (NULL == sqVB) {

View File

@ -671,9 +671,16 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
if (paint.getMaskFilter() || paint.getPathEffect()) { if (paint.getMaskFilter() || paint.getPathEffect()) {
usePath = true; usePath = true;
} }
// until we aa rotated rects...
if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) { if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
#ifdef SHADER_AA_FILL_RECT
if (doStroke) {
#endif
usePath = true; usePath = true;
#ifdef SHADER_AA_FILL_RECT
} else {
usePath = !fContext->getMatrix().preservesRightAngles();
}
#endif
} }
// small miter limit means right angles show bevel... // small miter limit means right angles show bevel...
if (SkPaint::kMiter_Join == paint.getStrokeJoin() && if (SkPaint::kMiter_Join == paint.getStrokeJoin() &&