aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-04-09 14:01:44 +0000
committerGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-04-09 14:01:44 +0000
commitdf3695e5c72b3b4401e71ff259827d87bfe8a06f (patch)
treedf996b69d83f3847a1f8a835fd14ec6c0c8c81ac /src
parent62f60bb1d419ac17df5c41162c0d50b7610c4ddf (diff)
First pass at Rect Effect
Diffstat (limited to 'src')
-rw-r--r--src/core/SkMatrix.cpp37
-rw-r--r--src/gpu/GrAARectRenderer.cpp215
-rw-r--r--src/gpu/GrContext.cpp58
-rw-r--r--src/gpu/SkGpuDevice.cpp11
4 files changed, 305 insertions, 16 deletions
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index d9675f3979..ad7b1dfaac 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -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));
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index 6f074e6524..37df53ae70 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -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,
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 1b62062cc1..9755ecfde2 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -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) {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index c1364afe02..356fcac243 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -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() &&