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:
parent
62f60bb1d4
commit
df3695e5c7
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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(¢er, 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,
|
||||
|
@ -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) {
|
||||
|
@ -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() &&
|
||||
|
Loading…
Reference in New Issue
Block a user