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;
/** 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 {
kMScaleX,
kMSkewX,

View File

@ -15,6 +15,7 @@
class GrGpu;
class GrDrawTarget;
class GrIndexBuffer;
class SkMatrix;
/*
* 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
// 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
// since we now have a coverage vertex attribute
@ -44,6 +45,13 @@ public:
const GrRect& devRect,
bool useVertexCoverage);
void shaderFillAARect(GrGpu* gpu,
GrDrawTarget* target,
const GrRect& rect,
const SkMatrix& combinedMatrix,
const GrRect& devRect,
bool useVertexCoverage);
void strokeAARect(GrGpu* gpu,
GrDrawTarget* target,
const GrRect& devRect,

View File

@ -189,6 +189,7 @@ bool SkMatrix::isSimilarity(SkScalar tol) const {
SkScalar sx = fMat[kMSkewX];
SkScalar sy = fMat[kMSkewY];
// TODO: I (rphillips) think there should be an || in here (see preservesRightAngles)
// degenerate matrix, non-similarity
if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my)
&& SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) {
@ -202,7 +203,41 @@ bool SkMatrix::isSimilarity(SkScalar tol) const {
return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
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));
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -8,9 +8,138 @@
#include "GrAARectRenderer.h"
#include "GrRefCnt.h"
#include "GrGpu.h"
#include "gl/GrGLEffect.h"
#include "GrTBackendEffectFactory.h"
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 {
static void aa_rect_attributes(bool useCoverage, const GrVertexAttrib** attribs, int* count) {
@ -181,6 +310,92 @@ void GrAARectRenderer::fillAARect(GrGpu* gpu,
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,
GrDrawTarget* target,
const GrRect& devRect,

View File

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

View File

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