aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-20 17:02:03 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-20 17:02:03 +0000
commit311a3cda9457d50cc7c2a0fc9f153a9ce2c8cb8e (patch)
treea70bb2efc6b0e8093b9df8240135374102aa92c2
parent6379129229b3253811a4bc2311b2fc85a3ea7a20 (diff)
Add function to get both min and max scale factors from matrix
R=reed@google.com, jvanverth@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/298473002 git-svn-id: http://skia.googlecode.com/svn/trunk@14804 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkMatrix.h15
-rw-r--r--src/core/SkMatrix.cpp86
-rw-r--r--tests/MatrixTest.cpp31
3 files changed, 104 insertions, 28 deletions
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index d52ab67f8c..7aa079ebda 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -563,22 +563,29 @@ public:
SK_TO_STRING_NONVIRT()
/**
- * Calculates the minimum scaling factor of the matrix. If the matrix has
- * perspective -1 is returned.
+ * Calculates the minimum scaling factor of the matrix as computed from the SVD of the upper
+ * left 2x2. If the matrix has perspective -1 is returned.
*
* @return minumum scale factor
*/
SkScalar getMinScale() const;
/**
- * Calculates the maximum scale factor of the matrix. If the matrix has
- * perspective -1 is returned.
+ * Calculates the maximum scaling factor of the matrix as computed from the SVD of the upper
+ * left 2x2. If the matrix has perspective -1 is returned.
*
* @return maximum scale factor
*/
SkScalar getMaxScale() const;
/**
+ * Gets both the min and max scale factors. The min scale factor is scaleFactors[0] and the max
+ * is scaleFactors[1]. If the matrix has perspective false will be returned and scaleFactors
+ * will be unchanged.
+ */
+ bool getMinMaxScales(SkScalar scaleFactors[2]) const;
+
+ /**
* Return a reference to a const identity matrix
*/
static const SkMatrix& I();
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 483c6c83e4..40f6e5298d 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1451,27 +1451,40 @@ bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
///////////////////////////////////////////////////////////////////////////////
-enum MinOrMax {
- kMin_MinOrMax,
- kMax_MinOrMax
+enum MinMaxOrBoth {
+ kMin_MinMaxOrBoth,
+ kMax_MinMaxOrBoth,
+ kBoth_MinMaxOrBoth
};
-template <MinOrMax MIN_OR_MAX> SkScalar get_scale_factor(SkMatrix::TypeMask typeMask,
- const SkScalar m[9]) {
+template <MinMaxOrBoth MIN_MAX_OR_BOTH> bool get_scale_factor(SkMatrix::TypeMask typeMask,
+ const SkScalar m[9],
+ SkScalar results[/*1 or 2*/]) {
if (typeMask & SkMatrix::kPerspective_Mask) {
- return -1;
+ return false;
}
if (SkMatrix::kIdentity_Mask == typeMask) {
- return 1;
+ results[0] = SK_Scalar1;
+ if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[1] = SK_Scalar1;
+ }
+ return true;
}
if (!(typeMask & SkMatrix::kAffine_Mask)) {
- if (kMin_MinOrMax == MIN_OR_MAX) {
- return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
- SkScalarAbs(m[SkMatrix::kMScaleY]));
+ if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[0] = SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
+ SkScalarAbs(m[SkMatrix::kMScaleY]));
+ } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[0] = SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
+ SkScalarAbs(m[SkMatrix::kMScaleY]));
} else {
- return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
- SkScalarAbs(m[SkMatrix::kMScaleY]));
+ results[0] = SkScalarAbs(m[SkMatrix::kMScaleX]);
+ results[1] = SkScalarAbs(m[SkMatrix::kMScaleY]);
+ if (results[0] > results[1]) {
+ SkTSwap(results[0], results[1]);
+ }
}
+ return true;
}
// ignore the translation part of the matrix, just look at 2x2 portion.
// compute singular values, take largest or smallest abs value.
@@ -1487,35 +1500,62 @@ template <MinOrMax MIN_OR_MAX> SkScalar get_scale_factor(SkMatrix::TypeMask type
// l^2 - (a + c)l + (ac-b^2)
// solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
// and roots are guaranteed to be pos and real).
- SkScalar chosenRoot;
SkScalar bSqd = b * b;
// if upper left 2x2 is orthogonal save some math
if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
- if (kMin_MinOrMax == MIN_OR_MAX) {
- chosenRoot = SkMinScalar(a, c);
+ if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[0] = SkMinScalar(a, c);
+ } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[0] = SkMaxScalar(a, c);
} else {
- chosenRoot = SkMaxScalar(a, c);
+ results[0] = a;
+ results[1] = c;
+ if (results[0] > results[1]) {
+ SkTSwap(results[0], results[1]);
+ }
}
} else {
SkScalar aminusc = a - c;
SkScalar apluscdiv2 = SkScalarHalf(a + c);
SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
- if (kMin_MinOrMax == MIN_OR_MAX) {
- chosenRoot = apluscdiv2 - x;
+ if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[0] = apluscdiv2 - x;
+ } else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ results[0] = apluscdiv2 + x;
} else {
- chosenRoot = apluscdiv2 + x;
+ results[0] = apluscdiv2 - x;
+ results[1] = apluscdiv2 + x;
}
}
- SkASSERT(chosenRoot >= 0);
- return SkScalarSqrt(chosenRoot);
+ SkASSERT(results[0] >= 0);
+ results[0] = SkScalarSqrt(results[0]);
+ if (kBoth_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
+ SkASSERT(results[1] >= 0);
+ results[1] = SkScalarSqrt(results[1]);
+ }
+ return true;
}
SkScalar SkMatrix::getMinScale() const {
- return get_scale_factor<kMin_MinOrMax>(this->getType(), fMat);
+ SkScalar factor;
+ if (get_scale_factor<kMin_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
+ return factor;
+ } else {
+ return -1;
+ }
}
SkScalar SkMatrix::getMaxScale() const {
- return get_scale_factor<kMax_MinOrMax>(this->getType(), fMat);
+ SkScalar factor;
+ if (get_scale_factor<kMax_MinMaxOrBoth>(this->getType(), fMat, &factor)) {
+ return factor;
+ } else {
+ return -1;
+ }
+}
+
+bool SkMatrix::getMinMaxScales(SkScalar scaleFactors[2]) const {
+ return get_scale_factor<kBoth_MinMaxOrBoth>(this->getType(), fMat, scaleFactors);
}
static void reset_identity_matrix(SkMatrix* identity) {
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 1897554bc8..9c7a520369 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -120,43 +120,67 @@ static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
}
static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
+ SkScalar scales[2];
+ bool success;
+
SkMatrix identity;
identity.reset();
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale());
+ success = identity.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
SkMatrix scale;
scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale());
+ success = scale.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, success && SK_Scalar1 * 2 == scales[0] && SK_Scalar1 * 4 == scales[1]);
SkMatrix rot90Scale;
rot90Scale.setRotate(90 * SK_Scalar1);
rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
+ success = rot90Scale.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, success && SK_Scalar1 / 4 == scales[0] && SK_Scalar1 / 2 == scales[1]);
SkMatrix rotate;
rotate.setRotate(128 * SK_Scalar1);
- REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale() ,SK_ScalarNearlyZero));
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMinScale(), SK_ScalarNearlyZero));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, rotate.getMaxScale(), SK_ScalarNearlyZero));
+ success = rotate.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, success);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[0], SK_ScalarNearlyZero));
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SK_Scalar1, scales[1], SK_ScalarNearlyZero));
SkMatrix translate;
translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale());
+ success = translate.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, success && SK_Scalar1 == scales[0] && SK_Scalar1 == scales[1]);
SkMatrix perspX;
perspX.reset();
perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
+ // Verify that getMinMaxScales() doesn't update the scales array on failure.
+ scales[0] = -5;
+ scales[1] = -5;
+ success = perspX.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]);
SkMatrix perspY;
perspY.reset();
perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
+ scales[0] = -5;
+ scales[1] = -5;
+ success = perspY.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1 == scales[1]);
SkMatrix baseMats[] = {scale, rot90Scale, rotate,
translate, perspX, perspY};
@@ -180,6 +204,11 @@ static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, (minScale < 0) == (maxScale < 0));
REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
+ SkScalar scales[2];
+ bool success = mat.getMinMaxScales(scales);
+ REPORTER_ASSERT(reporter, success == !mat.hasPerspective());
+ REPORTER_ASSERT(reporter, !success || (scales[0] == minScale && scales[1] == maxScale));
+
if (mat.hasPerspective()) {
m -= 1; // try another non-persp matrix
continue;