From 46d3d39e65e0b3ea2ad7c91c176ccafb4df0fa24 Mon Sep 17 00:00:00 2001 From: "jvanverth@google.com" Date: Tue, 22 Jan 2013 13:34:01 +0000 Subject: [PATCH] Add GPU support for axis-aligned ovals: - Add drawOval base function to SkDevice, and override in SkGpuDevice - Move isSimilarityMatrix to SkMatrix (renamed to isSimilarity) and fixed up unit test - Since both SkGpuDevice::drawOval() and GrContext::drawPath() can try to draw ovals, added GrContext::canDrawOval() and GrContext::internalDrawOval() to avoid duplicate code - Hooked in axis-aligned oval fill shader - Enabled GPU stroked circles - Added stroked circle bench test Review URL: https://codereview.appspot.com/7137050 git-svn-id: http://skia.googlecode.com/svn/trunk@7304 2bbb7eff-a529-9590-31e7-b0007b416f81 --- bench/PathBench.cpp | 20 ++++- include/core/SkDevice.h | 2 + include/core/SkDrawFilter.h | 1 + include/core/SkMatrix.h | 5 ++ include/gpu/GrContext.h | 14 +-- include/gpu/SkGpuDevice.h | 2 + src/core/SkCanvas.cpp | 11 ++- src/core/SkDevice.cpp | 14 ++- src/core/SkMatrix.cpp | 37 ++++++++ src/gpu/GrContext.cpp | 204 ++++++++++++++++++++++++++++---------------- src/gpu/GrDrawState.h | 3 + src/gpu/SkGpuDevice.cpp | 31 ++++++- src/gpu/gl/GrGLProgram.cpp | 7 ++ tests/MatrixTest.cpp | 76 +++++------------ 14 files changed, 280 insertions(+), 147 deletions(-) diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp index 390c532..ad9ca1d 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 33be2f6..5c32f76 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 3944257..6a50ca7 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 f9b72d7..87599d4 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 b1e9dd6..399b372 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 45b0693..815376f 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 67fbcca..587304b 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 1e7a9ba..b3209b3 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 532a534..eda8f14 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 43dda9c..9739199 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(¢er, 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(geo.vertices()); + GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY()); + vm.mapPoints(¢er, 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 13ed287..4a99ecb 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 0cf3b35..cedb7d4 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 9853af2..26127e4 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 bba2e08..1987f3b 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); } -- 2.7.4