aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/PathBench.cpp20
-rw-r--r--include/core/SkDevice.h2
-rw-r--r--include/core/SkDrawFilter.h1
-rw-r--r--include/core/SkMatrix.h5
-rw-r--r--include/gpu/GrContext.h14
-rw-r--r--include/gpu/SkGpuDevice.h2
-rw-r--r--src/core/SkCanvas.cpp11
-rw-r--r--src/core/SkDevice.cpp14
-rw-r--r--src/core/SkMatrix.cpp37
-rw-r--r--src/gpu/GrContext.cpp204
-rw-r--r--src/gpu/GrDrawState.h3
-rw-r--r--src/gpu/SkGpuDevice.cpp31
-rw-r--r--src/gpu/gl/GrGLProgram.cpp7
-rw-r--r--tests/MatrixTest.cpp76
14 files changed, 280 insertions, 147 deletions
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index 390c5326ec..ad9ca1d967 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -122,7 +122,7 @@ public:
name->append("oval");
}
virtual void makePath(SkPath* path) SK_OVERRIDE {
- SkRect r = { 10, 10, 20, 20 };
+ SkRect r = { 10, 10, 30, 20 };
path->addOval(r);
}
private:
@@ -624,13 +624,14 @@ private:
class CirclesBench : public SkBenchmark {
protected:
SkString fName;
+ Flags fFlags;
enum {
N = SkBENCHLOOP(100)
};
public:
- CirclesBench(void* param) : INHERITED(param) {
- fName.printf("circles");
+ CirclesBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
+ fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
}
protected:
@@ -643,6 +644,9 @@ protected:
paint.setColor(SK_ColorBLACK);
paint.setAntiAlias(true);
+ if (fFlags & kStroke_Flag) {
+ paint.setStyle(SkPaint::kStroke_Style);
+ }
SkRandom rand;
@@ -655,6 +659,10 @@ protected:
r.fRight = r.fLeft + 2 * radius;
r.fBottom = r.fTop + 2 * radius;
+ if (fFlags & kStroke_Flag) {
+ paint.setStrokeWidth(rand.nextUScalar1() * 5.0f);
+ }
+
SkPath temp;
// mimic how Chrome does circles
@@ -671,6 +679,7 @@ private:
typedef SkBenchmark INHERITED;
};
+
// Chrome creates its own round rects with each corner possibly being different.
// In its "zero radius" incarnation it creates degenerate round rects.
// Note: PathTest::test_arb_round_rect_is_convex and
@@ -959,9 +968,12 @@ static BenchRegistry gRegPathTo(FactPathTo);
static BenchRegistry gRegReverseAdd(FactReverseAdd);
static BenchRegistry gRegReverseTo(FactReverseTo);
-static SkBenchmark* CirclesTest(void* p) { return new CirclesBench(p); }
+static SkBenchmark* CirclesTest(void* p) { return new CirclesBench(p, FLAGS00); }
static BenchRegistry gRegCirclesTest(CirclesTest);
+static SkBenchmark* CirclesStrokeTest(void* p) { return new CirclesBench(p, FLAGS01); }
+static BenchRegistry gRegCirclesStrokeTest(CirclesStrokeTest);
+
static SkBenchmark* ArbRoundRectTest(void* p) { return new ArbRoundRectBench(p, false); }
static BenchRegistry gRegArbRoundRectTest(ArbRoundRectTest);
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 33be2f672a..5c32f768a5 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -226,6 +226,8 @@ protected:
const SkPoint[], const SkPaint& paint);
virtual void drawRect(const SkDraw&, const SkRect& r,
const SkPaint& paint);
+ virtual void drawOval(const SkDraw&, const SkRect& oval,
+ const SkPaint& paint);
/**
* If pathIsMutable, then the implementation is allowed to cast path to a
* non-const pointer and modify it in place (as an optimization). Canvas
diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h
index 3944257851..6a50ca7ac6 100644
--- a/include/core/SkDrawFilter.h
+++ b/include/core/SkDrawFilter.h
@@ -31,6 +31,7 @@ public:
kLine_Type,
kBitmap_Type,
kRect_Type,
+ kOval_Type,
kPath_Type,
kText_Type,
};
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index f9b72d7aeb..87599d4cfb 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -85,6 +85,11 @@ public:
kPerspective_Mask);
}
+ /** Returns true if the matrix contains only translation, rotation or uniform scale
+ Returns false if other transformation types are included or is degenerate
+ */
+ bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
+
enum {
kMScaleX,
kMSkewX,
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index b1e9dd6193..399b372e77 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -442,15 +442,12 @@ public:
* Draws an oval.
*
* @param paint describes how to color pixels.
- * @param rect the bounding rect of the oval.
- * @param strokeWidth if strokeWidth < 0, then the oval is filled, else
- * the rect is stroked based on strokeWidth. If
- * strokeWidth == 0, then the stroke is always a single
- * pixel thick.
+ * @param oval the bounding rect of the oval.
+ * @param stroke the stroke information (width, style)
*/
void drawOval(const GrPaint& paint,
- const GrRect& rect,
- SkScalar strokeWidth);
+ const GrRect& oval,
+ const SkStrokeRec& stroke);
///////////////////////////////////////////////////////////////////////////
// Misc.
@@ -910,6 +907,9 @@ private:
void internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke);
+ void internalDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke);
+ bool canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const;
+
GrTexture* createResizedTexture(const GrTextureDesc& desc,
const GrCacheID& cacheID,
void* srcData,
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 45b069397e..815376fbfa 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -64,6 +64,8 @@ public:
const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
virtual void drawRect(const SkDraw&, const SkRect& r,
const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawOval(const SkDraw&, const SkRect& oval,
+ const SkPaint& paint) SK_OVERRIDE;
virtual void drawPath(const SkDraw&, const SkPath& path,
const SkPaint& paint, const SkMatrix* prePathMatrix,
bool pathIsMutable) SK_OVERRIDE;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 67fbcca1ee..587304bc35 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1542,10 +1542,13 @@ void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
}
}
- SkPath path;
- path.addOval(oval);
- // call the non-virtual version
- this->SkCanvas::drawPath(path, paint);
+ LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawOval(iter, oval, looper.paint());
+ }
+
+ LOOPER_END
}
void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 1e7a9ba8f5..b3209b37b7 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -317,16 +317,24 @@ void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
}
void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
- const SkPoint pts[], const SkPaint& paint) {
+ const SkPoint pts[], const SkPaint& paint) {
draw.drawPoints(mode, count, pts, paint);
}
-void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
- const SkPaint& paint) {
+void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
CHECK_FOR_NODRAW_ANNOTATION(paint);
draw.drawRect(r, paint);
}
+void SkDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
+ CHECK_FOR_NODRAW_ANNOTATION(paint);
+
+ SkPath path;
+ path.addOval(oval);
+ // call the non-virtual version
+ this->SkDevice::drawPath(draw, path, paint, NULL, true);
+}
+
void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
const SkPaint& paint, const SkMatrix* prePathMatrix,
bool pathIsMutable) {
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 532a53449b..eda8f14c07 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -170,6 +170,43 @@ bool operator==(const SkMatrix& a, const SkMatrix& b) {
///////////////////////////////////////////////////////////////////////////////
+bool SkMatrix::isSimilarity(SkScalar tol) const {
+ // if identity or translate matrix
+ TypeMask mask = this->getType();
+ if (mask <= kTranslate_Mask) {
+ return true;
+ }
+ if (mask & kPerspective_Mask) {
+ return false;
+ }
+
+ SkScalar mx = fMat[kMScaleX];
+ SkScalar my = fMat[kMScaleY];
+ // if no skew, can just compare scale factors
+ if (!(mask & kAffine_Mask)) {
+ return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
+ }
+ SkScalar sx = fMat[kMSkewX];
+ SkScalar sy = fMat[kMSkewY];
+
+ // degenerate matrix, non-similarity
+ if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my)
+ && SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) {
+ 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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
fMat[kMTransX] = dx;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 43dda9c75c..97391997fc 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -935,59 +935,62 @@ struct CircleVertex {
SkScalar fInnerRadius;
};
-/* Returns true if will map a circle to another circle. This can be true
- * if the matrix only includes square-scale, rotation, translation.
- */
-inline bool isSimilarityTransformation(const SkMatrix& matrix,
- SkScalar tol = SK_ScalarNearlyZero) {
- if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
- return true;
- }
- if (matrix.hasPerspective()) {
- return false;
- }
+inline bool circleStaysCircle(const SkMatrix& m) {
+ return m.isSimilarity();
+}
- SkScalar mx = matrix.get(SkMatrix::kMScaleX);
- SkScalar sx = matrix.get(SkMatrix::kMSkewX);
- SkScalar my = matrix.get(SkMatrix::kMScaleY);
- SkScalar sy = matrix.get(SkMatrix::kMSkewY);
+}
- if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
- return false;
- }
+void GrContext::drawOval(const GrPaint& paint,
+ const GrRect& oval,
+ const SkStrokeRec& stroke) {
- // it has scales or skews, but it could also be rotation, check it out.
- SkVector vec[2];
- vec[0].set(mx, sx);
- vec[1].set(sy, my);
+ if (!canDrawOval(paint, oval, stroke)) {
+ SkPath path;
+ path.addOval(oval);
+ this->drawPath(paint, path, stroke);
+ return;
+ }
- return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
- SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
- SkScalarSquare(tol));
+ internalDrawOval(paint, oval, stroke);
}
+bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const {
+
+ if (!paint.isAntiAlias()) {
+ return false;
+ }
+
+ // we can draw circles in any style
+ bool isCircle = SkScalarNearlyEqual(oval.width(), oval.height())
+ && circleStaysCircle(this->getMatrix());
+ // and for now, axis-aligned ellipses only with fill or stroke-and-fill
+ SkStrokeRec::Style style = stroke.getStyle();
+ bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
+ bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
+
+ return isCircle || isFilledAxisAlignedEllipse;
}
-// TODO: strokeWidth can't be larger than zero right now.
-// It will be fixed when drawPath() can handle strokes.
-void GrContext::drawOval(const GrPaint& paint,
- const GrRect& rect,
- SkScalar strokeWidth) {
- GrAssert(strokeWidth <= 0);
- if (!isSimilarityTransformation(this->getMatrix()) ||
- !paint.isAntiAlias() ||
- rect.height() != rect.width()) {
- SkPath path;
- path.addOval(rect);
- path.setFillType(SkPath::kWinding_FillType);
- SkStrokeRec stroke(0 == strokeWidth ? SkStrokeRec::kHairline_InitStyle :
- SkStrokeRec::kFill_InitStyle);
- if (strokeWidth > 0) {
- stroke.setStrokeStyle(strokeWidth, true);
- }
- this->internalDrawPath(paint, path, stroke);
- return;
+void GrContext::internalDrawOval(const GrPaint& paint,
+ const GrRect& oval,
+ const SkStrokeRec& stroke) {
+
+ SkScalar xRadius = SkScalarHalf(oval.width());
+ SkScalar yRadius = SkScalarHalf(oval.height());
+
+ SkScalar strokeWidth = stroke.getWidth();
+ SkStrokeRec::Style style = stroke.getStyle();
+
+ bool isCircle = SkScalarNearlyEqual(xRadius, yRadius) && circleStaysCircle(this->getMatrix());
+#ifdef SK_DEBUG
+ {
+ // we should have checked for this previously
+ bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
+ bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
+ SkASSERT(paint.isAntiAlias() && (isCircle || isFilledAxisAlignedEllipse));
}
+#endif
GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
@@ -1008,22 +1011,6 @@ void GrContext::drawOval(const GrPaint& paint,
GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
GrAssert(sizeof(CircleVertex) == GrDrawTarget::VertexSize(layout));
- GrPoint center = GrPoint::Make(rect.centerX(), rect.centerY());
- SkScalar radius = SkScalarHalf(rect.width());
-
- vm.mapPoints(&center, 1);
- radius = vm.mapRadius(radius);
-
- SkScalar outerRadius = radius;
- SkScalar innerRadius = 0;
- SkScalar halfWidth = 0;
- if (strokeWidth == 0) {
- halfWidth = SkScalarHalf(SK_Scalar1);
-
- outerRadius += halfWidth;
- innerRadius = SkMaxScalar(0, radius - halfWidth);
- }
-
GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0);
if (!geo.succeeded()) {
GrPrintf("Failed to get space for vertices!\n");
@@ -1032,26 +1019,93 @@ void GrContext::drawOval(const GrPaint& paint,
CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
+ GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY());
+ vm.mapPoints(&center, 1);
+
+ SkScalar L;
+ SkScalar R;
+ SkScalar T;
+ SkScalar B;
+
+ if (isCircle) {
+ drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
+
+ xRadius = vm.mapRadius(xRadius);
+
+ SkScalar outerRadius = xRadius;
+ SkScalar innerRadius = 0;
+ SkScalar halfWidth = 0;
+ if (style != SkStrokeRec::kFill_Style) {
+ strokeWidth = vm.mapRadius(strokeWidth);
+ if (SkScalarNearlyZero(strokeWidth)) {
+ halfWidth = SK_ScalarHalf;
+ } else {
+ halfWidth = SkScalarHalf(strokeWidth);
+ }
+
+ outerRadius += halfWidth;
+ if (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style) {
+ innerRadius = SkMaxScalar(0, xRadius - halfWidth);
+ }
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ verts[i].fCenter = center;
+ verts[i].fOuterRadius = outerRadius;
+ verts[i].fInnerRadius = innerRadius;
+ }
+
+ L = -outerRadius;
+ R = +outerRadius;
+ T = -outerRadius;
+ B = +outerRadius;
+ } else { // is axis-aligned ellipse
+ drawState->setVertexEdgeType(GrDrawState::kEllipse_EdgeType);
+
+ SkRect xformedRect;
+ vm.mapRect(&xformedRect, oval);
+
+ xRadius = SkScalarHalf(xformedRect.width());
+ yRadius = SkScalarHalf(xformedRect.height());
+
+ if (style == SkStrokeRec::kStrokeAndFill_Style && strokeWidth > 0.0f) {
+ SkScalar halfWidth = SkScalarHalf(strokeWidth);
+ // do (potentially) anisotropic mapping
+ SkVector scaledStroke;
+ scaledStroke.set(halfWidth, halfWidth);
+ vm.mapVectors(&scaledStroke, 1);
+ // this is legit only if scale & translation (which should be the case at the moment)
+ xRadius += scaledStroke.fX;
+ yRadius += scaledStroke.fY;
+ }
+
+ SkScalar ratio = SkScalarDiv(xRadius, yRadius);
+
+ for (int i = 0; i < 4; ++i) {
+ verts[i].fCenter = center;
+ verts[i].fOuterRadius = xRadius;
+ verts[i].fInnerRadius = ratio;
+ }
+
+ L = -xRadius;
+ R = +xRadius;
+ T = -yRadius;
+ B = +yRadius;
+ }
+
// The fragment shader will extend the radius out half a pixel
// to antialias. Expand the drawn rect here so all the pixels
// will be captured.
- SkScalar L = center.fX - outerRadius - SkFloatToScalar(0.5f);
- SkScalar R = center.fX + outerRadius + SkFloatToScalar(0.5f);
- SkScalar T = center.fY - outerRadius - SkFloatToScalar(0.5f);
- SkScalar B = center.fY + outerRadius + SkFloatToScalar(0.5f);
+ L += center.fX - SK_ScalarHalf;
+ R += center.fX + SK_ScalarHalf;
+ T += center.fY - SK_ScalarHalf;
+ B += center.fY + SK_ScalarHalf;
verts[0].fPos = SkPoint::Make(L, T);
verts[1].fPos = SkPoint::Make(R, T);
verts[2].fPos = SkPoint::Make(L, B);
verts[3].fPos = SkPoint::Make(R, B);
- for (int i = 0; i < 4; ++i) {
- verts[i].fCenter = center;
- verts[i].fOuterRadius = outerRadius;
- verts[i].fInnerRadius = innerRadius;
- }
-
- drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
}
@@ -1065,10 +1119,10 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrok
}
SkRect ovalRect;
- if ((stroke.isHairlineStyle() || stroke.isFillStyle()) && !path.isInverseFillType() &&
- path.isOval(&ovalRect)) {
- SkScalar width = stroke.isHairlineStyle() ? 0 : -SK_Scalar1;
- this->drawOval(paint, ovalRect, width);
+ bool isOval = path.isOval(&ovalRect);
+
+ if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, stroke)) {
+ this->drawOval(paint, ovalRect, stroke);
return;
}
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 13ed287803..4a99ecb942 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -681,6 +681,9 @@ public:
/* Circle specified as center_x, center_y, outer_radius, inner_radius
all in window space (y-down). */
kCircle_EdgeType,
+ /* Axis-aligned ellipse specified as center_x, center_y, x_radius, x_radius/y_radius
+ all in window space (y-down). */
+ kEllipse_EdgeType,
kVertexEdgeTypeCnt
};
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 0cf3b35f2a..cedb7d4e8a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -674,6 +674,35 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
fContext->drawRect(grPaint, rect, doStroke ? width : -1);
}
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
+ const SkPaint& paint) {
+ CHECK_FOR_NODRAW_ANNOTATION(paint);
+ CHECK_SHOULD_DRAW(draw, false);
+
+ bool usePath = false;
+ // some basic reasons we might need to call drawPath...
+ if (paint.getMaskFilter() || paint.getPathEffect()) {
+ usePath = true;
+ }
+
+ if (usePath) {
+ SkPath path;
+ path.addOval(oval);
+ this->drawPath(draw, path, paint, NULL, true);
+ return;
+ }
+
+ GrPaint grPaint;
+ if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+ return;
+ }
+ SkStrokeRec stroke(paint);
+
+ fContext->drawOval(grPaint, oval, stroke);
+}
+
#include "SkMaskFilter.h"
#include "SkBounder.h"
@@ -912,7 +941,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
return;
}
- // can we cheat, and threat a thin stroke as a hairline w/ coverage
+ // can we cheat, and treat a thin stroke as a hairline w/ coverage
// if we can, we draw lots faster (raster device does this same test)
SkScalar hairlineCoverage;
bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 9853af246d..26127e4afd 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -276,6 +276,13 @@ bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
builder->fFSCode.appendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
builder->fFSCode.append("\tedgeAlpha = outerAlpha * innerAlpha;\n");
break;
+ case GrDrawState::kEllipse_EdgeType:
+ builder->fFSCode.append("\tfloat edgeAlpha;\n");
+ builder->fFSCode.appendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName);
+ builder->fFSCode.appendf("\toffset.y *= %s.w;\n", fsName);
+ builder->fFSCode.append("\tfloat d = length(offset);\n");
+ builder->fFSCode.appendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
+ break;
default:
GrCrash("Unknown Edge Type!");
break;
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index bba2e08683..1987f3bd32 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -216,106 +216,76 @@ static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
}
}
-// This function is extracted from src/gpu/SkGpuDevice.cpp,
-// in order to make sure this function works correctly.
-static bool isSimilarityTransformation(const SkMatrix& matrix,
- SkScalar tol = SK_ScalarNearlyZero) {
- if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
- return true;
- }
- if (matrix.hasPerspective()) {
- return false;
- }
-
- SkScalar mx = matrix.get(SkMatrix::kMScaleX);
- SkScalar sx = matrix.get(SkMatrix::kMSkewX);
- SkScalar my = matrix.get(SkMatrix::kMScaleY);
- SkScalar sy = matrix.get(SkMatrix::kMSkewY);
-
- if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
- return false;
- }
-
- // it has scales or 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));
-}
-
-static void test_matrix_is_similarity_transform(skiatest::Reporter* reporter) {
+static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
SkMatrix mat;
// identity
mat.setIdentity();
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// translation only
mat.reset();
mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// scale with same size
mat.reset();
mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// scale with one negative
mat.reset();
mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// scale with different size
mat.reset();
mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// scale with same size at a pivot point
mat.reset();
mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
SkIntToScalar(2), SkIntToScalar(2));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// scale with different size at a pivot point
mat.reset();
mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
SkIntToScalar(2), SkIntToScalar(2));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// skew with same size
mat.reset();
mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// skew with different size
mat.reset();
mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// skew with same size at a pivot point
mat.reset();
mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
SkIntToScalar(2), SkIntToScalar(2));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// skew with different size at a pivot point
mat.reset();
mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
SkIntToScalar(2), SkIntToScalar(2));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// perspective x
mat.reset();
mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// perspective y
mat.reset();
mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
#if SK_SCALAR_IS_FLOAT
/* We bypass the following tests for SK_SCALAR_IS_FIXED build.
@@ -331,7 +301,7 @@ static void test_matrix_is_similarity_transform(skiatest::Reporter* reporter) {
for (int angle = 0; angle < 360; ++angle) {
mat.reset();
mat.setRotate(SkIntToScalar(angle));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
}
// see if there are any accumulated precision issues
@@ -339,40 +309,40 @@ static void test_matrix_is_similarity_transform(skiatest::Reporter* reporter) {
for (int i = 1; i < 360; i++) {
mat.postRotate(SkIntToScalar(1));
}
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// rotate + translate
mat.reset();
mat.setRotate(SkIntToScalar(30));
mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// rotate + uniform scale
mat.reset();
mat.setRotate(SkIntToScalar(30));
mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
// rotate + non-uniform scale
mat.reset();
mat.setRotate(SkIntToScalar(30));
mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
#endif
// all zero
mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// all zero except perspective
mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
- REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, !mat.isSimilarity());
// scales zero, only skews
mat.setAll(0, SK_Scalar1, 0,
SK_Scalar1, 0, 0,
0, 0, SkMatrix::I()[8]);
- REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+ REPORTER_ASSERT(reporter, mat.isSimilarity());
}
static void TestMatrix(skiatest::Reporter* reporter) {
@@ -491,7 +461,7 @@ static void TestMatrix(skiatest::Reporter* reporter) {
#endif
test_matrix_max_stretch(reporter);
- test_matrix_is_similarity_transform(reporter);
+ test_matrix_is_similarity(reporter);
test_matrix_recttorect(reporter);
}