aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-04-25 12:43:45 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-04-25 12:43:45 +0000
commit205d46067ace24bd4b111cf44efc96caff6c4d30 (patch)
treef405973a26a367e566ecbd4c327ad929654d8e1f
parente624caf6c33e1379a7f6f8333a8f0ec25025420e (diff)
Move alpha-ramp AA to GrContext, detect cases when AA is applied via other methods (smooth lines, MSAA) or rect falls on integer coords and skip the alpha ramp path. Use pre-fab index buffer for alpha-ramped fill rects and stroke rects.
Review URL: http://codereview.appspot.com/4449047/ git-svn-id: http://skia.googlecode.com/svn/trunk@1169 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gpu/include/GrContext.h18
-rw-r--r--gpu/include/GrGpu.h7
-rw-r--r--gpu/include/GrMatrix.h24
-rw-r--r--gpu/include/GrPoint.h8
-rw-r--r--gpu/include/GrRect.h24
-rw-r--r--gpu/include/GrScalar.h12
-rw-r--r--gpu/src/GrContext.cpp296
-rw-r--r--gpu/src/GrGpuGL.cpp2
-rw-r--r--gpu/src/GrMatrix.cpp20
-rw-r--r--gyp/skia.gyp1
-rw-r--r--samplecode/SampleAARects.cpp197
-rw-r--r--src/gpu/SkGpuDevice.cpp199
12 files changed, 625 insertions, 183 deletions
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 5eda2b7186..07d76f88e1 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -568,8 +568,26 @@ private:
GrIndexBufferAllocPool* fDrawBufferIBAllocPool;
GrInOrderDrawBuffer* fDrawBuffer;
+ GrIndexBuffer* fAAFillRectIndexBuffer;
+ GrIndexBuffer* fAAStrokeRectIndexBuffer;
+
GrContext(GrGpu* gpu);
+ void fillAARect(GrDrawTarget* target,
+ const GrPaint& paint,
+ const GrRect& devRect);
+
+ void strokeAARect(GrDrawTarget* target,
+ const GrPaint& paint,
+ const GrRect& devRect,
+ const GrVec& devStrokeSize);
+
+ inline int aaFillRectIndexCount() const;
+ GrIndexBuffer* aaFillRectIndexBuffer();
+
+ inline int aaStrokeRectIndexCount() const;
+ GrIndexBuffer* aaStrokeRectIndexBuffer();
+
void setupDrawBuffer();
void flushDrawBuffer();
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
index 7dd9959af9..5bbe85d877 100644
--- a/gpu/include/GrGpu.h
+++ b/gpu/include/GrGpu.h
@@ -273,6 +273,12 @@ public:
bool supportsBufferLocking() const { return fBufferLockSupport; }
/**
+ * Does the 3D API support anti-aliased lines. If so then line primitive
+ * types will use this functionality when the AA state flag is set.
+ */
+ bool supportsAALines() const { return fAALineSupport; }
+
+ /**
* Gets the minimum width of a render target. If a texture/rt is created
* with a width less than this size the GrGpu object will clamp it to this
* value.
@@ -445,6 +451,7 @@ protected:
bool fNPOTRenderTargetSupport;
bool fTwoSidedStencilSupport;
bool fStencilWrapOpsSupport;
+ bool fAALineSupport;
// set by subclass to true if index and vertex buffers can be locked, false
// otherwise.
diff --git a/gpu/include/GrMatrix.h b/gpu/include/GrMatrix.h
index 9a2e660015..1ebc0b4e2a 100644
--- a/gpu/include/GrMatrix.h
+++ b/gpu/include/GrMatrix.h
@@ -135,7 +135,7 @@ public:
GrScalar scaleY,
GrScalar transY,
GrScalar persp0,
- GrScalar persp1,
+ GrScalar persp1,
GrScalar persp2) {
fM[kScaleX] = scaleX;
fM[kSkewX] = skewX;
@@ -253,6 +253,21 @@ public:
start = (GrPoint*)((intptr_t)start + stride);
}
}
+
+ /**
+ * Transforms a vector by the matrix. Doesn't handle cases when a
+ * homogeneous vector maps to a point (i.e. perspective transform).
+ * In this case the desired answer is dependent on where the tail of
+ * the vector is in space.
+ */
+ void mapVec(GrVec* vec) {
+ GrAssert(!this->hasPerspective());
+ if (!this->isIdentity()) {
+ GrScalar x = vec->fX;
+ vec->fX = (*this)[kScaleX] * x + (*this)[kSkewX] * vec->fY;
+ vec->fY = (*this)[kSkewY ] * x + (*this)[kScaleY] * vec->fY;
+ }
+ }
/**
* Transform the 4 corners of the src rect, and return the bounding rect
@@ -278,7 +293,12 @@ public:
* @return true if matrix is idenity
*/
bool isIdentity() const;
-
+
+ /**
+ * Do axis-aligned lines stay axis aligned? May do 90 degree rotation / mirroring.
+ */
+ bool preservesAxisAlignment() const;
+
/**
* Calculates the maximum stretching factor of the matrix. Only defined if
* the matrix does not have perspective.
diff --git a/gpu/include/GrPoint.h b/gpu/include/GrPoint.h
index c07543bb30..8c540f0b99 100644
--- a/gpu/include/GrPoint.h
+++ b/gpu/include/GrPoint.h
@@ -153,6 +153,14 @@ public:
fX = x;
fY = y;
}
+
+ /**
+ * set this to (abs(v.x), abs(v.y))
+ */
+ void setAbs(const GrVec& v) {
+ fX = GrScalarAbs(v.fX);
+ fY = GrScalarAbs(v.fY);
+ }
/**
* set vector to point from a to b.
diff --git a/gpu/include/GrRect.h b/gpu/include/GrRect.h
index 67e366c3bf..a9ff6ec14d 100644
--- a/gpu/include/GrRect.h
+++ b/gpu/include/GrRect.h
@@ -206,6 +206,14 @@ struct GrRect {
}
/**
+ * Returns true if the rects edges are integer-aligned.
+ */
+ bool isIRect() const {
+ return GrScalarIsInt(fLeft) && GrScalarIsInt(fTop) &&
+ GrScalarIsInt(fRight) && GrScalarIsInt(fBottom);
+ }
+
+ /**
* Does this rect contain a point.
*/
bool contains(const GrPoint& point) const {
@@ -363,6 +371,22 @@ struct GrRect {
return pts + 4;
}
+ /**
+ * Swaps (left and right) and/or (top and bottom) if they are inverted
+ */
+ void sort() {
+ if (fLeft > fRight) {
+ GrScalar temp = fLeft;
+ fLeft = fRight;
+ fRight = temp;
+ }
+ if (fTop > fBottom) {
+ GrScalar temp = fTop;
+ fTop = fBottom;
+ fBottom = temp;
+ }
+ }
+
bool operator ==(const GrRect& r) const {
return fLeft == r.fLeft &&
fTop == r.fTop &&
diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h
index 1353fb2148..7aaa43d33f 100644
--- a/gpu/include/GrScalar.h
+++ b/gpu/include/GrScalar.h
@@ -56,11 +56,19 @@
*/
#define GrFloatToFixed(x) ((GrFixed)((x) * GR_Fixed1))
-inline GrFixed GrFixedAbs(GrFixed x) {
+static inline GrFixed GrFixedAbs(GrFixed x) {
int32_t s = (x & 0x80000000) >> 31;
return (GrFixed)(((int32_t)x ^ s) - s);
}
+static inline bool GrFixedIsInt(GrFixed x) {
+ return 0 == (x & 0xffff);
+}
+
+static inline bool GrFloatIsInt(float x) {
+ return x == (float)(int)x;
+}
+
///////////////////////////////////////////////////////////////////////////////
#if GR_SCALAR_IS_FIXED
@@ -72,6 +80,7 @@ inline GrFixed GrFixedAbs(GrFixed x) {
#define GrScalarHalf(x) ((x) >> 1)
#define GrScalarAve(x,y) (((x)+(y)) >> 1)
#define GrScalarAbs(x) GrFixedAbs(x)
+ #define GrScalarIsInt GrFixedIsInt
#define GR_Scalar1 GR_Fixed1
#define GR_ScalarHalf GR_FixedHalf
#define GR_ScalarMax GR_FixedMax
@@ -85,6 +94,7 @@ inline GrFixed GrFixedAbs(GrFixed x) {
#define GrScalarHalf(x) ((x) * 0.5f)
#define GrScalarAbs(x) fabsf(x)
#define GrScalarAve(x,y) (((x) + (y)) * 0.5f)
+ #define GrScalarIsInt GrFloatIsInt
#define GR_Scalar1 1.f
#define GR_ScalarHalf 0.5f
#define GR_ScalarMax (FLT_MAX)
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index f68564a2ba..7cf0cca7cb 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -58,29 +58,38 @@ GrContext* GrContext::CreateGLShaderContext() {
GrContext::~GrContext() {
this->flush();
- fGpu->unref();
delete fTextureCache;
delete fFontCache;
delete fDrawBuffer;
delete fDrawBufferVBAllocPool;
delete fDrawBufferIBAllocPool;
GrSafeUnref(fCustomPathRenderer);
+ GrSafeUnref(fAAFillRectIndexBuffer);
+ GrSafeUnref(fAAStrokeRectIndexBuffer);
+ fGpu->unref();
}
void GrContext::contextLost() {
+ // abandon first to so destructors
+ // don't try to free the resources in the API.
+ fGpu->abandonResources();
+
delete fDrawBuffer;
fDrawBuffer = NULL;
+
delete fDrawBufferVBAllocPool;
fDrawBufferVBAllocPool = NULL;
+
delete fDrawBufferIBAllocPool;
fDrawBufferIBAllocPool = NULL;
+ GrSafeSetNull(fAAFillRectIndexBuffer);
+ GrSafeSetNull(fAAStrokeRectIndexBuffer);
+
fTextureCache->removeAll();
fFontCache->freeAll();
fGpu->markContextDirty();
- fGpu->abandonResources();
-
this->setupDrawBuffer();
}
@@ -348,14 +357,17 @@ void GrContext::drawPaint(const GrPaint& paint) {
this->drawRect(paint, r);
}
+////////////////////////////////////////////////////////////////////////////////
+
/* create a triangle strip that strokes the specified triangle. There are 8
unique vertices, but we repreat the last 2 to close up. Alternatively we
could use an indices array, and then only send 8 verts, but not sure that
would be faster.
*/
-static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect,
+static void setStrokeRectStrip(GrPoint verts[10], GrRect rect,
GrScalar width) {
const GrScalar rad = GrScalarHalf(width);
+ rect.sort();
verts[0].set(rect.fLeft + rad, rect.fTop + rad);
verts[1].set(rect.fLeft - rad, rect.fTop - rad);
@@ -369,6 +381,235 @@ static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect,
verts[9] = verts[1];
}
+static GrColor getColorForMesh(const GrPaint& paint) {
+ if (NULL == paint.getTexture()) {
+ return paint.fColor;
+ } else {
+ unsigned a = GrColorUnpackA(paint.fColor);
+ return GrColorPackRGBA(a, a, a, a);
+ }
+}
+
+static void setInsetFan(GrPoint* pts, size_t stride,
+ const GrRect& r, GrScalar dx, GrScalar dy) {
+ pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
+}
+
+static const uint16_t gFillAARectIdx[] = {
+ 0, 1, 5, 5, 4, 0,
+ 1, 2, 6, 6, 5, 1,
+ 2, 3, 7, 7, 6, 2,
+ 3, 0, 4, 4, 7, 3,
+ 4, 5, 6, 6, 7, 4,
+};
+
+int GrContext::aaFillRectIndexCount() const {
+ return GR_ARRAY_COUNT(gFillAARectIdx);
+}
+
+GrIndexBuffer* GrContext::aaFillRectIndexBuffer() {
+ if (NULL == fAAFillRectIndexBuffer) {
+ fAAFillRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gFillAARectIdx),
+ false);
+ GrAssert(NULL != fAAFillRectIndexBuffer);
+#if GR_DEBUG
+ bool updated =
+#endif
+ fAAFillRectIndexBuffer->updateData(gFillAARectIdx,
+ sizeof(gFillAARectIdx));
+ GR_DEBUGASSERT(updated);
+ }
+ return fAAFillRectIndexBuffer;
+}
+
+static const uint16_t gStrokeAARectIdx[] = {
+ 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
+ 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
+ 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
+ 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
+
+ 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
+ 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
+ 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
+ 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
+
+ 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
+ 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
+ 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
+ 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
+};
+
+int GrContext::aaStrokeRectIndexCount() const {
+ return GR_ARRAY_COUNT(gStrokeAARectIdx);
+}
+
+GrIndexBuffer* GrContext::aaStrokeRectIndexBuffer() {
+ if (NULL == fAAStrokeRectIndexBuffer) {
+ fAAStrokeRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gStrokeAARectIdx),
+ false);
+ GrAssert(NULL != fAAStrokeRectIndexBuffer);
+#if GR_DEBUG
+ bool updated =
+#endif
+ fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
+ sizeof(gStrokeAARectIdx));
+ GR_DEBUGASSERT(updated);
+ }
+ return fAAStrokeRectIndexBuffer;
+}
+
+void GrContext::fillAARect(GrDrawTarget* target,
+ const GrPaint& paint,
+ const GrRect& devRect) {
+
+ GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
+ if (NULL != paint.getTexture()) {
+ layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+ }
+
+ size_t vsize = GrDrawTarget::VertexSize(layout);
+
+ GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0);
+
+ intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
+
+ GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
+ GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
+
+ setInsetFan(fan0Pos, vsize, devRect, -GR_ScalarHalf, -GR_ScalarHalf);
+ setInsetFan(fan1Pos, vsize, devRect, GR_ScalarHalf, GR_ScalarHalf);
+
+ verts += sizeof(GrPoint);
+ for (int i = 0; i < 4; ++i) {
+ *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+ }
+
+ GrColor innerColor = getColorForMesh(paint);
+ verts += 4 * vsize;
+ for (int i = 0; i < 4; ++i) {
+ *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
+ }
+
+ target->setIndexSourceToBuffer(this->aaFillRectIndexBuffer());
+
+ target->drawIndexed(kTriangles_PrimitiveType, 0,
+ 0, 8, this->aaFillRectIndexCount());
+}
+
+void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint,
+ const GrRect& devRect, const GrVec& devStrokeSize) {
+ const GrScalar& dx = devStrokeSize.fX;
+ const GrScalar& dy = devStrokeSize.fY;
+ const GrScalar rx = GrMul(dx, GR_ScalarHalf);
+ const GrScalar ry = GrMul(dy, GR_ScalarHalf);
+
+ GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
+
+ if (NULL != paint.getTexture()) {
+ layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+ }
+
+ GrScalar spare;
+ {
+ GrScalar w = devRect.width() - dx;
+ GrScalar h = devRect.height() - dy;
+ spare = GrMin(w, h);
+ }
+
+ if (spare <= 0) {
+ GrRect r(devRect);
+ r.inset(-rx, -ry);
+ fillAARect(target, paint, r);
+ return;
+ }
+
+ size_t vsize = GrDrawTarget::VertexSize(layout);
+
+ GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
+
+ intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
+
+ GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
+ GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
+ GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
+ GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
+
+ setInsetFan(fan0Pos, vsize, devRect, -rx - GR_ScalarHalf, -ry - GR_ScalarHalf);
+ setInsetFan(fan1Pos, vsize, devRect, -rx + GR_ScalarHalf, -ry + GR_ScalarHalf);
+ setInsetFan(fan2Pos, vsize, devRect, rx - GR_ScalarHalf, ry - GR_ScalarHalf);
+ setInsetFan(fan3Pos, vsize, devRect, rx + GR_ScalarHalf, ry + GR_ScalarHalf);
+
+ verts += sizeof(GrPoint);
+ for (int i = 0; i < 4; ++i) {
+ *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+ }
+
+ GrColor innerColor = getColorForMesh(paint);
+ verts += 4 * vsize;
+ for (int i = 0; i < 8; ++i) {
+ *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
+ }
+
+ verts += 8 * vsize;
+ for (int i = 0; i < 8; ++i) {
+ *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+ }
+
+ target->setIndexSourceToBuffer(aaStrokeRectIndexBuffer());
+ target->drawIndexed(kTriangles_PrimitiveType,
+ 0, 0, 16, aaStrokeRectIndexCount());
+}
+
+static bool apply_aa_to_rect(GrDrawTarget* target,
+ GrGpu* gpu,
+ const GrPaint& paint,
+ const GrRect& rect,
+ GrScalar width,
+ const GrMatrix* matrix,
+ GrMatrix* combinedMatrix,
+ GrRect* devRect) {
+ // we use a simple alpha ramp to do aa on axis-aligned rects
+ // do AA with alpha ramp if the caller requested AA, the rect
+ // will be axis-aligned,the render target is not
+ // multisampled, and the rect won't land on integer coords.
+
+ if (!paint.fAntiAlias) {
+ return false;
+ }
+
+ if (target->getRenderTarget()->isMultisampled()) {
+ return false;
+ }
+
+ if (0 == width && gpu->supportsAALines()) {
+ return false;
+ }
+
+ if (!target->getViewMatrix().preservesAxisAlignment()) {
+ return false;
+ }
+
+ if (NULL != matrix &&
+ !matrix->preservesAxisAlignment()) {
+ return false;
+ }
+
+ *combinedMatrix = target->getViewMatrix();
+ if (NULL != matrix) {
+ combinedMatrix->preConcat(*matrix);
+ GrAssert(combinedMatrix->preservesAxisAlignment());
+ }
+
+ combinedMatrix->mapRect(devRect, rect);
+ devRect->sort();
+
+ if (width < 0) {
+ return !devRect->isIRect();
+ } else {
+ return true;
+ }
+}
+
void GrContext::drawRect(const GrPaint& paint,
const GrRect& rect,
GrScalar width,
@@ -378,13 +619,43 @@ void GrContext::drawRect(const GrPaint& paint,
GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+ GrRect devRect = rect;
+ GrMatrix combinedMatrix;
+ bool doAA = apply_aa_to_rect(target, fGpu, paint, rect, width, matrix,
+ &combinedMatrix, &devRect);
+
+ if (doAA) {
+ GrDrawTarget::AutoViewMatrixRestore avm(target);
+ if (textured) {
+ GrMatrix inv;
+ if (combinedMatrix.invert(&inv)) {
+ target->preConcatSamplerMatrix(0, inv);
+ }
+ }
+ target->setViewMatrix(GrMatrix::I());
+ if (width >= 0) {
+ GrVec strokeSize;;
+ if (width > 0) {
+ strokeSize.set(width, width);
+ combinedMatrix.mapVec(&strokeSize);
+ strokeSize.setAbs(strokeSize);
+ } else {
+ strokeSize.set(GR_Scalar1, GR_Scalar1);
+ }
+ strokeAARect(target, paint, devRect, strokeSize);
+ } else {
+ fillAARect(target, paint, devRect);
+ }
+ return;
+ }
+
if (width >= 0) {
// TODO: consider making static vertex buffers for these cases.
// Hairline could be done by just adding closing vertex to
// unitSquareVertexBuffer()
- GrVertexLayout layout = (textured) ?
- GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
- 0;
+ GrVertexLayout layout = textured ?
+ GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
+ 0;
static const int worstCaseVertCount = 10;
GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
@@ -415,7 +686,9 @@ void GrContext::drawRect(const GrPaint& paint,
if (NULL != matrix) {
avmr.set(target);
target->preConcatViewMatrix(*matrix);
- target->preConcatSamplerMatrix(0, *matrix);
+ if (textured) {
+ target->preConcatSamplerMatrix(0, *matrix);
+ }
}
target->drawNonIndexed(primType, 0, vertCount);
@@ -429,8 +702,8 @@ void GrContext::drawRect(const GrPaint& paint,
GrDrawTarget::AutoViewMatrixRestore avmr(target);
GrMatrix m;
m.setAll(rect.width(), 0, rect.fLeft,
- 0, rect.height(), rect.fTop,
- 0, 0, GrMatrix::I()[8]);
+ 0, rect.height(), rect.fTop,
+ 0, 0, GrMatrix::I()[8]);
if (NULL != matrix) {
m.postConcat(*matrix);
@@ -819,6 +1092,9 @@ GrContext::GrContext(GrGpu* gpu) :
fDrawBufferVBAllocPool = NULL;
fDrawBufferIBAllocPool = NULL;
+ fAAFillRectIndexBuffer = NULL;
+ fAAStrokeRectIndexBuffer = NULL;
+
this->setupDrawBuffer();
}
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 64431a35f8..f2ec450a0f 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -361,6 +361,8 @@ GrGpuGL::GrGpuGL() {
}
}
+ fAALineSupport = GR_GL_SUPPORT_DESKTOP;
+
////////////////////////////////////////////////////////////////////////////
// Experiments to determine limitations that can't be queried. TODO: Make
// these a preprocess that generate some compile time constants.
diff --git a/gpu/src/GrMatrix.cpp b/gpu/src/GrMatrix.cpp
index 0a2d1b2b55..92a38ee1e9 100644
--- a/gpu/src/GrMatrix.cpp
+++ b/gpu/src/GrMatrix.cpp
@@ -259,6 +259,26 @@ bool GrMatrix::isIdentity() const {
}
+bool GrMatrix::preservesAxisAlignment() const {
+
+ // check if matrix is trans and scale only
+ static const int gAllowedMask1 = kScale_TypeBit | kTranslate_TypeBit;
+
+ if (!(~gAllowedMask1 & fTypeMask)) {
+ return true;
+ }
+
+ // check matrix is trans and skew only (0 scale)
+ static const int gAllowedMask2 = kScale_TypeBit | kSkew_TypeBit |
+ kTranslate_TypeBit | kZeroScale_TypeBit;
+
+ if (!(~gAllowedMask2 & fTypeMask) && (kZeroScale_TypeBit & fTypeMask)) {
+ return true;
+ }
+
+ return false;
+}
+
GrScalar GrMatrix::getMaxStretch() const {
if (fTypeMask & kPerspective_TypeBit) {
diff --git a/gyp/skia.gyp b/gyp/skia.gyp
index 322f87cc8f..94ba3cf8b4 100644
--- a/gyp/skia.gyp
+++ b/gyp/skia.gyp
@@ -1444,6 +1444,7 @@
'../samplecode/SamplePicture.cpp',
'../samplecode/SamplePoints.cpp',
'../samplecode/SamplePolyToPoly.cpp',
+ '../samplecode/SampleAARects.cpp',
'../samplecode/SampleRegion.cpp',
'../samplecode/SampleRepeatTile.cpp',
'../samplecode/SampleShaders.cpp',
diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp
new file mode 100644
index 0000000000..94f8ce98a5
--- /dev/null
+++ b/samplecode/SampleAARects.cpp
@@ -0,0 +1,197 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+static SkBitmap createBitmap(int n) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
+ bitmap.allocPixels();
+ bitmap.eraseColor(SK_ColorGREEN);
+
+ SkCanvas canvas(bitmap);
+ SkRect r;
+ r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ paint.setColor(SK_ColorRED);
+ canvas.drawOval(r, paint);
+ paint.setColor(SK_ColorBLUE);
+ paint.setStrokeWidth(SkIntToScalar(n)/15);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas.drawLine(0, 0, r.fRight, r.fBottom, paint);
+ canvas.drawLine(0, r.fBottom, r.fRight, 0, paint);
+
+ return bitmap;
+}
+
+class AARectView : public SkView {
+ SkBitmap fBitmap;
+ enum {
+ N = 64
+ };
+public:
+ AARectView() {
+ fBitmap = createBitmap(N);
+
+ fWidth = N;
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "AA Rects");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+ SkPaint bluePaint;
+ bluePaint.setARGB(0xff, 0x0, 0x0, 0xff);
+ SkPaint bmpPaint;
+ SkShader* bmpShader = SkShader::CreateBitmapShader(fBitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
+ bmpPaint.setShader(bmpShader);
+ bmpShader->unref();
+
+ bluePaint.setStrokeWidth(3);
+ bmpPaint.setStrokeWidth(3);
+
+ SkPaint paints[] = { bluePaint, bmpPaint };
+
+ SkRect rect;
+
+ SkScalar dx = SkIntToScalar(80);
+ SkScalar dy = SkIntToScalar(100);
+ SkMatrix matrix;
+ for (int p = 0; p < SK_ARRAY_COUNT(paints); ++p) {
+ for (int stroke = 0; stroke < 2; ++stroke) {
+ paints[p].setStyle(stroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+ for (int a = 0; a < 3; ++ a) {
+ paints[p].setAntiAlias(a > 0);
+ paints[p].setAlpha(a > 1 ? 0x80 : 0xff);
+
+ canvas->save();
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.f),
+ SkFloatToScalar(0.f),
+ SkFloatToScalar(40.f),
+ SkFloatToScalar(40.f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(40.5f),
+ SkFloatToScalar(40.5f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(40.f),
+ SkFloatToScalar(40.f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.75f),
+ SkFloatToScalar(0.75f),
+ SkFloatToScalar(40.75f),
+ SkFloatToScalar(40.75f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ canvas->translate(SkFloatToScalar(.33f), SkFloatToScalar(.67f));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f),
+ SkFloatToScalar(0.0f),
+ SkFloatToScalar(40.0f),
+ SkFloatToScalar(40.0f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ matrix.setRotate(SkFloatToScalar(45.f));
+ canvas->concat(matrix);
+ canvas->translate(SkFloatToScalar(20.0f / sqrtf(2.f)),
+ SkFloatToScalar(20.0f / sqrtf(2.f)));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(-20.0f),
+ SkFloatToScalar(-20.0f),
+ SkFloatToScalar(20.0f),
+ SkFloatToScalar(20.0f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ canvas->rotate(SkFloatToScalar(90.f));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f),
+ SkFloatToScalar(0.0f),
+ SkFloatToScalar(40.0f),
+ SkFloatToScalar(-40.0f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ canvas->rotate(SkFloatToScalar(90.f));
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(40.5f),
+ SkFloatToScalar(-40.5f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ matrix.setScale(SkFloatToScalar(-1.f), SkFloatToScalar(-1.f));
+ canvas->concat(matrix);
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
+ SkFloatToScalar(0.5f),
+ SkFloatToScalar(-40.5f),
+ SkFloatToScalar(-40.5f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->save();
+ matrix.setScale(SkFloatToScalar(2.1f), SkFloatToScalar(4.1f));
+ canvas->concat(matrix);
+ rect = SkRect::MakeLTRB(SkFloatToScalar(0.1f),
+ SkFloatToScalar(0.1f),
+ SkFloatToScalar(19.1f),
+ SkFloatToScalar(9.1f));
+ canvas->drawRect(rect, paints[p]);
+ canvas->restore();
+ canvas->translate(dx, 0);
+
+ canvas->restore();
+ canvas->translate(0, dy);
+ }
+ }
+ }
+ }
+
+private:
+ int fWidth;
+
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AARectView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index f89d4b18a5..1c797c3b95 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -653,178 +653,37 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
///////////////////////////////////////////////////////////////////////////////
-static void setInsetFan(GrPoint pts[4], const GrRect& r,
- GrScalar dx, GrScalar dy) {
- pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy);
-}
-
-static GrColor getColorForMesh(const GrPaint& paint) {
- if (NULL == paint.getTexture()) {
- return paint.fColor;
- } else {
- unsigned a = GrColorUnpackA(paint.fColor);
- return GrColorPackRGBA(a, a, a, a);
- }
-}
-
-static const uint16_t gFillAARectIdx1[] = {
- 0, 1, 5, 5, 4, 0,
- 1, 2, 6, 6, 5, 1,
- 2, 3, 7, 7, 6, 2,
- 3, 0, 4, 4, 7, 3,
- 4, 5, 6, 6, 7, 4,
-};
-
-static void fillDevAARect(GrContext* ctx, const GrPaint& paint,
- const GrRect& rect) {
- if (rect.isEmpty()) {
- return;
- }
-
- GrAutoMatrix avm(ctx, GrMatrix::I());
-
- GrPoint verts[8];
- GrPoint* texs = NULL;
- GrColor colors[8];
-
- setInsetFan(&verts[ 0], rect, -0.5f, -0.5f);
- setInsetFan(&verts[ 4], rect, 0.5f, 0.5f);
-
- sk_memset32(&colors[ 0], 0, 4);
- sk_memset32(&colors[ 4], getColorForMesh(paint), 4);
-
- ctx->drawVertices(paint, kTriangles_PrimitiveType,
- 8, verts, texs, colors,
- gFillAARectIdx1, SK_ARRAY_COUNT(gFillAARectIdx1));
-}
-
-static const uint16_t gStrokeAARectIdx[] = {
- 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
- 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
- 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
- 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
-
- 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
- 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
- 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
- 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
-
- 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
- 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
- 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
- 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
-};
-
-static void strokeDevAARect(GrContext* ctx, const GrPaint& paint,
- const GrRect& rect, const SkPoint& strokeSize) {
- const GrScalar dx = SkScalarToGrScalar(strokeSize.fX);
- const GrScalar dy = SkScalarToGrScalar(strokeSize.fY);
- const GrScalar rx = dx * 0.5f;
- const GrScalar ry = dy * 0.5f;
-
- GrScalar spare;
- {
- GrScalar w = rect.width() - dx;
- GrScalar h = rect.height() - dy;
- spare = GrMin(w, h);
- }
-
- if (spare <= 0) {
- GrRect r(rect);
- r.inset(-rx, -ry);
- fillDevAARect(ctx, paint, r);
- return;
- }
-
- GrAutoMatrix avm(ctx, GrMatrix::I());
-
- GrPoint verts[16];
- GrPoint* texs = NULL;
- GrColor colors[16];
-
- setInsetFan(&verts[ 0], rect, -rx - 0.5f, -ry - 0.5f);
- setInsetFan(&verts[ 4], rect, -rx + 0.5f, -ry + 0.5f);
- setInsetFan(&verts[ 8], rect, rx - 0.5f, ry - 0.5f);
- setInsetFan(&verts[12], rect, rx + 0.5f, ry + 0.5f);
-
- sk_memset32(&colors[ 0], 0, 4);
- sk_memset32(&colors[ 4], getColorForMesh(paint), 8);
- sk_memset32(&colors[12], 0, 4);
-
- ctx->drawVertices(paint, kTriangles_PrimitiveType,
- 16, verts, texs, colors,
- gStrokeAARectIdx, SK_ARRAY_COUNT(gStrokeAARectIdx));
-}
-
-/*
- * If the paint has a texture, preconcat the ctx's inverse, since when we
- * draw verts which are already in device coordinates, we need to "undo" that
- * before we run our vertex shaders, which expect the coordinates to be local.
- */
-static void preConcatInverseToTextureMatrix(GrContext* ctx, GrPaint* paint) {
- if (paint->getTexture()) {
- GrMatrix inverse;
- if (ctx->getMatrix().invert(&inverse)) {
- paint->fSampler.preConcatMatrix(inverse);
- }
- }
-}
-
void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
const SkPaint& paint) {
- CHECK_SHOULD_DRAW(draw);
-
- const SkMatrix& matrix = *draw.fMatrix;
- SkPoint strokeSize;
- SkDraw::RectType type = SkDraw::ComputeRectType(paint, matrix, &strokeSize);
-
- if (SkDraw::kPath_RectType == type) {
- SkPath path;
- path.addRect(rect);
- this->drawPath(draw, path, paint, NULL, true);
- } else {
- GrPaint grPaint;
- SkAutoCachedTexture act;
- if (!this->skPaint2GrPaintShader(paint, &act, matrix, &grPaint)) {
- return;
- }
-
- bool doAA = paint.isAntiAlias();
-
- if (SkDraw::kHair_RectType == type && doAA) {
- strokeSize.set(SK_Scalar1, SK_Scalar1);
- type = SkDraw::kStroke_RectType;
- }
-
- switch (type) {
- case SkDraw::kHair_RectType:
- SkASSERT(!doAA);
- fContext->drawRect(grPaint, Sk2Gr(rect), 0);
- break;
- case SkDraw::kFill_RectType:
- if (doAA) {
- SkRect devRect;
- matrix.mapRect(&devRect, rect);
- preConcatInverseToTextureMatrix(fContext, &grPaint);
- fillDevAARect(fContext, grPaint, Sk2Gr(devRect));
- } else {
- fContext->drawRect(grPaint, Sk2Gr(rect), -1);
- }
- break;
- case SkDraw::kStroke_RectType:
- if (doAA) {
- SkRect devRect;
- matrix.mapRect(&devRect, rect);
- preConcatInverseToTextureMatrix(fContext, &grPaint);
- strokeDevAARect(fContext, grPaint, Sk2Gr(devRect), strokeSize);
- } else {
- fContext->drawRect(grPaint, Sk2Gr(rect), paint.getStrokeWidth());
- }
- break;
- default:
- SkASSERT(!"bad value for RectType");
- }
- }
+ CHECK_SHOULD_DRAW(draw);
+
+ bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
+ SkScalar width = paint.getStrokeWidth();
+
+ /*
+ We have special code for hairline strokes, miter-strokes, and fills.
+ Anything else we just call our path code.
+ */
+ bool usePath = doStroke && width > 0 &&
+ paint.getStrokeJoin() != SkPaint::kMiter_Join;
+ // another reason we might need to call drawPath...
+ if (paint.getMaskFilter()) {
+ usePath = true;
+ }
+
+ if (usePath) {
+ SkPath path;
+ path.addRect(rect);
+ this->drawPath(draw, path, paint, NULL, true);
+ return;
+ }
+
+ GrPaint grPaint;
+ SkAutoCachedTexture act;
+ if (!this->skPaint2GrPaintShader(paint, &act, *draw.fMatrix, &grPaint)) {
+ return;
+ }
+ fContext->drawRect(grPaint, Sk2Gr(rect), doStroke ? width : -1);
}
#include "SkMaskFilter.h"