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
This commit is contained in:
parent
9d5f99bc30
commit
46d3d39e65
@ -122,7 +122,7 @@ public:
|
|||||||
name->append("oval");
|
name->append("oval");
|
||||||
}
|
}
|
||||||
virtual void makePath(SkPath* path) SK_OVERRIDE {
|
virtual void makePath(SkPath* path) SK_OVERRIDE {
|
||||||
SkRect r = { 10, 10, 20, 20 };
|
SkRect r = { 10, 10, 30, 20 };
|
||||||
path->addOval(r);
|
path->addOval(r);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
@ -624,13 +624,14 @@ private:
|
|||||||
class CirclesBench : public SkBenchmark {
|
class CirclesBench : public SkBenchmark {
|
||||||
protected:
|
protected:
|
||||||
SkString fName;
|
SkString fName;
|
||||||
|
Flags fFlags;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
N = SkBENCHLOOP(100)
|
N = SkBENCHLOOP(100)
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
CirclesBench(void* param) : INHERITED(param) {
|
CirclesBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
|
||||||
fName.printf("circles");
|
fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -643,6 +644,9 @@ protected:
|
|||||||
|
|
||||||
paint.setColor(SK_ColorBLACK);
|
paint.setColor(SK_ColorBLACK);
|
||||||
paint.setAntiAlias(true);
|
paint.setAntiAlias(true);
|
||||||
|
if (fFlags & kStroke_Flag) {
|
||||||
|
paint.setStyle(SkPaint::kStroke_Style);
|
||||||
|
}
|
||||||
|
|
||||||
SkRandom rand;
|
SkRandom rand;
|
||||||
|
|
||||||
@ -655,6 +659,10 @@ protected:
|
|||||||
r.fRight = r.fLeft + 2 * radius;
|
r.fRight = r.fLeft + 2 * radius;
|
||||||
r.fBottom = r.fTop + 2 * radius;
|
r.fBottom = r.fTop + 2 * radius;
|
||||||
|
|
||||||
|
if (fFlags & kStroke_Flag) {
|
||||||
|
paint.setStrokeWidth(rand.nextUScalar1() * 5.0f);
|
||||||
|
}
|
||||||
|
|
||||||
SkPath temp;
|
SkPath temp;
|
||||||
|
|
||||||
// mimic how Chrome does circles
|
// mimic how Chrome does circles
|
||||||
@ -671,6 +679,7 @@ private:
|
|||||||
typedef SkBenchmark INHERITED;
|
typedef SkBenchmark INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Chrome creates its own round rects with each corner possibly being different.
|
// Chrome creates its own round rects with each corner possibly being different.
|
||||||
// In its "zero radius" incarnation it creates degenerate round rects.
|
// In its "zero radius" incarnation it creates degenerate round rects.
|
||||||
// Note: PathTest::test_arb_round_rect_is_convex and
|
// Note: PathTest::test_arb_round_rect_is_convex and
|
||||||
@ -959,9 +968,12 @@ static BenchRegistry gRegPathTo(FactPathTo);
|
|||||||
static BenchRegistry gRegReverseAdd(FactReverseAdd);
|
static BenchRegistry gRegReverseAdd(FactReverseAdd);
|
||||||
static BenchRegistry gRegReverseTo(FactReverseTo);
|
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 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 SkBenchmark* ArbRoundRectTest(void* p) { return new ArbRoundRectBench(p, false); }
|
||||||
static BenchRegistry gRegArbRoundRectTest(ArbRoundRectTest);
|
static BenchRegistry gRegArbRoundRectTest(ArbRoundRectTest);
|
||||||
|
|
||||||
|
@ -226,6 +226,8 @@ protected:
|
|||||||
const SkPoint[], const SkPaint& paint);
|
const SkPoint[], const SkPaint& paint);
|
||||||
virtual void drawRect(const SkDraw&, const SkRect& r,
|
virtual void drawRect(const SkDraw&, const SkRect& r,
|
||||||
const SkPaint& paint);
|
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
|
* If pathIsMutable, then the implementation is allowed to cast path to a
|
||||||
* non-const pointer and modify it in place (as an optimization). Canvas
|
* non-const pointer and modify it in place (as an optimization). Canvas
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
kLine_Type,
|
kLine_Type,
|
||||||
kBitmap_Type,
|
kBitmap_Type,
|
||||||
kRect_Type,
|
kRect_Type,
|
||||||
|
kOval_Type,
|
||||||
kPath_Type,
|
kPath_Type,
|
||||||
kText_Type,
|
kText_Type,
|
||||||
};
|
};
|
||||||
|
@ -85,6 +85,11 @@ public:
|
|||||||
kPerspective_Mask);
|
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 {
|
enum {
|
||||||
kMScaleX,
|
kMScaleX,
|
||||||
kMSkewX,
|
kMSkewX,
|
||||||
|
@ -442,15 +442,12 @@ public:
|
|||||||
* Draws an oval.
|
* Draws an oval.
|
||||||
*
|
*
|
||||||
* @param paint describes how to color pixels.
|
* @param paint describes how to color pixels.
|
||||||
* @param rect the bounding rect of the oval.
|
* @param oval the bounding rect of the oval.
|
||||||
* @param strokeWidth if strokeWidth < 0, then the oval is filled, else
|
* @param stroke the stroke information (width, style)
|
||||||
* the rect is stroked based on strokeWidth. If
|
|
||||||
* strokeWidth == 0, then the stroke is always a single
|
|
||||||
* pixel thick.
|
|
||||||
*/
|
*/
|
||||||
void drawOval(const GrPaint& paint,
|
void drawOval(const GrPaint& paint,
|
||||||
const GrRect& rect,
|
const GrRect& oval,
|
||||||
SkScalar strokeWidth);
|
const SkStrokeRec& stroke);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Misc.
|
// Misc.
|
||||||
@ -910,6 +907,9 @@ private:
|
|||||||
|
|
||||||
void internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke);
|
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,
|
GrTexture* createResizedTexture(const GrTextureDesc& desc,
|
||||||
const GrCacheID& cacheID,
|
const GrCacheID& cacheID,
|
||||||
void* srcData,
|
void* srcData,
|
||||||
|
@ -64,6 +64,8 @@ public:
|
|||||||
const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
|
const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
|
||||||
virtual void drawRect(const SkDraw&, const SkRect& r,
|
virtual void drawRect(const SkDraw&, const SkRect& r,
|
||||||
const SkPaint& paint) SK_OVERRIDE;
|
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,
|
virtual void drawPath(const SkDraw&, const SkPath& path,
|
||||||
const SkPaint& paint, const SkMatrix* prePathMatrix,
|
const SkPaint& paint, const SkMatrix* prePathMatrix,
|
||||||
bool pathIsMutable) SK_OVERRIDE;
|
bool pathIsMutable) SK_OVERRIDE;
|
||||||
|
@ -1542,10 +1542,13 @@ void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SkPath path;
|
LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
|
||||||
path.addOval(oval);
|
|
||||||
// call the non-virtual version
|
while (iter.next()) {
|
||||||
this->SkCanvas::drawPath(path, paint);
|
iter.fDevice->drawOval(iter, oval, looper.paint());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOPER_END
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
|
void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
|
||||||
|
@ -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,
|
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);
|
draw.drawPoints(mode, count, pts, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
|
void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
|
||||||
const SkPaint& paint) {
|
|
||||||
CHECK_FOR_NODRAW_ANNOTATION(paint);
|
CHECK_FOR_NODRAW_ANNOTATION(paint);
|
||||||
draw.drawRect(r, 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,
|
void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
|
||||||
const SkPaint& paint, const SkMatrix* prePathMatrix,
|
const SkPaint& paint, const SkMatrix* prePathMatrix,
|
||||||
bool pathIsMutable) {
|
bool pathIsMutable) {
|
||||||
|
@ -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) {
|
void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
|
||||||
if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
|
if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
|
||||||
fMat[kMTransX] = dx;
|
fMat[kMTransX] = dx;
|
||||||
|
@ -935,60 +935,63 @@ struct CircleVertex {
|
|||||||
SkScalar fInnerRadius;
|
SkScalar fInnerRadius;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Returns true if will map a circle to another circle. This can be true
|
inline bool circleStaysCircle(const SkMatrix& m) {
|
||||||
* if the matrix only includes square-scale, rotation, translation.
|
return m.isSimilarity();
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
void GrContext::drawOval(const GrPaint& paint,
|
||||||
const GrRect& rect,
|
const GrRect& oval,
|
||||||
SkScalar strokeWidth) {
|
const SkStrokeRec& stroke) {
|
||||||
GrAssert(strokeWidth <= 0);
|
|
||||||
if (!isSimilarityTransformation(this->getMatrix()) ||
|
if (!canDrawOval(paint, oval, stroke)) {
|
||||||
!paint.isAntiAlias() ||
|
|
||||||
rect.height() != rect.width()) {
|
|
||||||
SkPath path;
|
SkPath path;
|
||||||
path.addOval(rect);
|
path.addOval(oval);
|
||||||
path.setFillType(SkPath::kWinding_FillType);
|
this->drawPath(paint, path, stroke);
|
||||||
SkStrokeRec stroke(0 == strokeWidth ? SkStrokeRec::kHairline_InitStyle :
|
|
||||||
SkStrokeRec::kFill_InitStyle);
|
|
||||||
if (strokeWidth > 0) {
|
|
||||||
stroke.setStrokeStyle(strokeWidth, true);
|
|
||||||
}
|
|
||||||
this->internalDrawPath(paint, path, stroke);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
|
||||||
|
|
||||||
GrDrawState* drawState = target->drawState();
|
GrDrawState* drawState = target->drawState();
|
||||||
@ -1008,22 +1011,6 @@ void GrContext::drawOval(const GrPaint& paint,
|
|||||||
GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
|
GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
|
||||||
GrAssert(sizeof(CircleVertex) == GrDrawTarget::VertexSize(layout));
|
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);
|
GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0);
|
||||||
if (!geo.succeeded()) {
|
if (!geo.succeeded()) {
|
||||||
GrPrintf("Failed to get space for vertices!\n");
|
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());
|
CircleVertex* verts = reinterpret_cast<CircleVertex*>(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
|
// The fragment shader will extend the radius out half a pixel
|
||||||
// to antialias. Expand the drawn rect here so all the pixels
|
// to antialias. Expand the drawn rect here so all the pixels
|
||||||
// will be captured.
|
// will be captured.
|
||||||
SkScalar L = center.fX - outerRadius - SkFloatToScalar(0.5f);
|
L += center.fX - SK_ScalarHalf;
|
||||||
SkScalar R = center.fX + outerRadius + SkFloatToScalar(0.5f);
|
R += center.fX + SK_ScalarHalf;
|
||||||
SkScalar T = center.fY - outerRadius - SkFloatToScalar(0.5f);
|
T += center.fY - SK_ScalarHalf;
|
||||||
SkScalar B = center.fY + outerRadius + SkFloatToScalar(0.5f);
|
B += center.fY + SK_ScalarHalf;
|
||||||
|
|
||||||
verts[0].fPos = SkPoint::Make(L, T);
|
verts[0].fPos = SkPoint::Make(L, T);
|
||||||
verts[1].fPos = SkPoint::Make(R, T);
|
verts[1].fPos = SkPoint::Make(R, T);
|
||||||
verts[2].fPos = SkPoint::Make(L, B);
|
verts[2].fPos = SkPoint::Make(L, B);
|
||||||
verts[3].fPos = SkPoint::Make(R, 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);
|
target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1065,10 +1119,10 @@ void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrok
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkRect ovalRect;
|
SkRect ovalRect;
|
||||||
if ((stroke.isHairlineStyle() || stroke.isFillStyle()) && !path.isInverseFillType() &&
|
bool isOval = path.isOval(&ovalRect);
|
||||||
path.isOval(&ovalRect)) {
|
|
||||||
SkScalar width = stroke.isHairlineStyle() ? 0 : -SK_Scalar1;
|
if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, stroke)) {
|
||||||
this->drawOval(paint, ovalRect, width);
|
this->drawOval(paint, ovalRect, stroke);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,6 +681,9 @@ public:
|
|||||||
/* Circle specified as center_x, center_y, outer_radius, inner_radius
|
/* Circle specified as center_x, center_y, outer_radius, inner_radius
|
||||||
all in window space (y-down). */
|
all in window space (y-down). */
|
||||||
kCircle_EdgeType,
|
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
|
kVertexEdgeTypeCnt
|
||||||
};
|
};
|
||||||
|
@ -674,6 +674,35 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
|
|||||||
fContext->drawRect(grPaint, rect, doStroke ? width : -1);
|
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 "SkMaskFilter.h"
|
||||||
#include "SkBounder.h"
|
#include "SkBounder.h"
|
||||||
|
|
||||||
@ -912,7 +941,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
|
|||||||
return;
|
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)
|
// if we can, we draw lots faster (raster device does this same test)
|
||||||
SkScalar hairlineCoverage;
|
SkScalar hairlineCoverage;
|
||||||
bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
|
bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
|
||||||
|
@ -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.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");
|
builder->fFSCode.append("\tedgeAlpha = outerAlpha * innerAlpha;\n");
|
||||||
break;
|
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:
|
default:
|
||||||
GrCrash("Unknown Edge Type!");
|
GrCrash("Unknown Edge Type!");
|
||||||
break;
|
break;
|
||||||
|
@ -216,106 +216,76 @@ static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is extracted from src/gpu/SkGpuDevice.cpp,
|
static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
|
||||||
// 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) {
|
|
||||||
SkMatrix mat;
|
SkMatrix mat;
|
||||||
|
|
||||||
// identity
|
// identity
|
||||||
mat.setIdentity();
|
mat.setIdentity();
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// translation only
|
// translation only
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
|
mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// scale with same size
|
// scale with same size
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
|
mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// scale with one negative
|
// scale with one negative
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
|
mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// scale with different size
|
// scale with different size
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
|
mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// scale with same size at a pivot point
|
// scale with same size at a pivot point
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
|
mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
|
||||||
SkIntToScalar(2), SkIntToScalar(2));
|
SkIntToScalar(2), SkIntToScalar(2));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// scale with different size at a pivot point
|
// scale with different size at a pivot point
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
|
mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
|
||||||
SkIntToScalar(2), SkIntToScalar(2));
|
SkIntToScalar(2), SkIntToScalar(2));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// skew with same size
|
// skew with same size
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
|
mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// skew with different size
|
// skew with different size
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
|
mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// skew with same size at a pivot point
|
// skew with same size at a pivot point
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
|
mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
|
||||||
SkIntToScalar(2), SkIntToScalar(2));
|
SkIntToScalar(2), SkIntToScalar(2));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// skew with different size at a pivot point
|
// skew with different size at a pivot point
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
|
mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
|
||||||
SkIntToScalar(2), SkIntToScalar(2));
|
SkIntToScalar(2), SkIntToScalar(2));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// perspective x
|
// perspective x
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
|
mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
// perspective y
|
// perspective y
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
|
mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
|
|
||||||
#if SK_SCALAR_IS_FLOAT
|
#if SK_SCALAR_IS_FLOAT
|
||||||
/* We bypass the following tests for SK_SCALAR_IS_FIXED build.
|
/* 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) {
|
for (int angle = 0; angle < 360; ++angle) {
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setRotate(SkIntToScalar(angle));
|
mat.setRotate(SkIntToScalar(angle));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if there are any accumulated precision issues
|
// 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++) {
|
for (int i = 1; i < 360; i++) {
|
||||||
mat.postRotate(SkIntToScalar(1));
|
mat.postRotate(SkIntToScalar(1));
|
||||||
}
|
}
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// rotate + translate
|
// rotate + translate
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setRotate(SkIntToScalar(30));
|
mat.setRotate(SkIntToScalar(30));
|
||||||
mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
|
mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// rotate + uniform scale
|
// rotate + uniform scale
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setRotate(SkIntToScalar(30));
|
mat.setRotate(SkIntToScalar(30));
|
||||||
mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
|
mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
|
|
||||||
// rotate + non-uniform scale
|
// rotate + non-uniform scale
|
||||||
mat.reset();
|
mat.reset();
|
||||||
mat.setRotate(SkIntToScalar(30));
|
mat.setRotate(SkIntToScalar(30));
|
||||||
mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
|
mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
|
||||||
REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, !mat.isSimilarity());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// all zero
|
// all zero
|
||||||
mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
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
|
// all zero except perspective
|
||||||
mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
|
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
|
// scales zero, only skews
|
||||||
mat.setAll(0, SK_Scalar1, 0,
|
mat.setAll(0, SK_Scalar1, 0,
|
||||||
SK_Scalar1, 0, 0,
|
SK_Scalar1, 0, 0,
|
||||||
0, 0, SkMatrix::I()[8]);
|
0, 0, SkMatrix::I()[8]);
|
||||||
REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
|
REPORTER_ASSERT(reporter, mat.isSimilarity());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TestMatrix(skiatest::Reporter* reporter) {
|
static void TestMatrix(skiatest::Reporter* reporter) {
|
||||||
@ -491,7 +461,7 @@ static void TestMatrix(skiatest::Reporter* reporter) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
test_matrix_max_stretch(reporter);
|
test_matrix_max_stretch(reporter);
|
||||||
test_matrix_is_similarity_transform(reporter);
|
test_matrix_is_similarity(reporter);
|
||||||
test_matrix_recttorect(reporter);
|
test_matrix_recttorect(reporter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user