diff options
-rw-r--r-- | include/core/SkMatrix.h | 15 | ||||
-rw-r--r-- | src/core/SkMatrix.cpp | 86 | ||||
-rw-r--r-- | tests/MatrixTest.cpp | 31 |
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; |