aboutsummaryrefslogtreecommitdiffhomepage
path: root/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'gpu')
-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
9 files changed, 398 insertions, 13 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) {