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
This commit is contained in:
parent
6379129229
commit
311a3cda94
@ -563,21 +563,28 @@ 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
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user