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()
|
SK_TO_STRING_NONVIRT()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the minimum scaling factor of the matrix. If the matrix has
|
* Calculates the minimum scaling factor of the matrix as computed from the SVD of the upper
|
||||||
* perspective -1 is returned.
|
* left 2x2. If the matrix has perspective -1 is returned.
|
||||||
*
|
*
|
||||||
* @return minumum scale factor
|
* @return minumum scale factor
|
||||||
*/
|
*/
|
||||||
SkScalar getMinScale() const;
|
SkScalar getMinScale() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the maximum scale factor of the matrix. If the matrix has
|
* Calculates the maximum scaling factor of the matrix as computed from the SVD of the upper
|
||||||
* perspective -1 is returned.
|
* left 2x2. If the matrix has perspective -1 is returned.
|
||||||
*
|
*
|
||||||
* @return maximum scale factor
|
* @return maximum scale factor
|
||||||
*/
|
*/
|
||||||
SkScalar getMaxScale() const;
|
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
|
* Return a reference to a const identity matrix
|
||||||
*/
|
*/
|
||||||
|
@ -1451,27 +1451,40 @@ bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[],
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
enum MinOrMax {
|
enum MinMaxOrBoth {
|
||||||
kMin_MinOrMax,
|
kMin_MinMaxOrBoth,
|
||||||
kMax_MinOrMax
|
kMax_MinMaxOrBoth,
|
||||||
|
kBoth_MinMaxOrBoth
|
||||||
};
|
};
|
||||||
|
|
||||||
template <MinOrMax MIN_OR_MAX> SkScalar get_scale_factor(SkMatrix::TypeMask typeMask,
|
template <MinMaxOrBoth MIN_MAX_OR_BOTH> bool get_scale_factor(SkMatrix::TypeMask typeMask,
|
||||||
const SkScalar m[9]) {
|
const SkScalar m[9],
|
||||||
|
SkScalar results[/*1 or 2*/]) {
|
||||||
if (typeMask & SkMatrix::kPerspective_Mask) {
|
if (typeMask & SkMatrix::kPerspective_Mask) {
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
if (SkMatrix::kIdentity_Mask == typeMask) {
|
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 (!(typeMask & SkMatrix::kAffine_Mask)) {
|
||||||
if (kMin_MinOrMax == MIN_OR_MAX) {
|
if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
|
||||||
return SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
|
results[0] = SkMinScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
|
||||||
SkScalarAbs(m[SkMatrix::kMScaleY]));
|
SkScalarAbs(m[SkMatrix::kMScaleY]));
|
||||||
|
} else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
|
||||||
|
results[0] = SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
|
||||||
|
SkScalarAbs(m[SkMatrix::kMScaleY]));
|
||||||
} else {
|
} else {
|
||||||
return SkMaxScalar(SkScalarAbs(m[SkMatrix::kMScaleX]),
|
results[0] = SkScalarAbs(m[SkMatrix::kMScaleX]);
|
||||||
SkScalarAbs(m[SkMatrix::kMScaleY]));
|
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.
|
// ignore the translation part of the matrix, just look at 2x2 portion.
|
||||||
// compute singular values, take largest or smallest abs value.
|
// 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)
|
// l^2 - (a + c)l + (ac-b^2)
|
||||||
// solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
|
// solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
|
||||||
// and roots are guaranteed to be pos and real).
|
// and roots are guaranteed to be pos and real).
|
||||||
SkScalar chosenRoot;
|
|
||||||
SkScalar bSqd = b * b;
|
SkScalar bSqd = b * b;
|
||||||
// if upper left 2x2 is orthogonal save some math
|
// if upper left 2x2 is orthogonal save some math
|
||||||
if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
|
if (bSqd <= SK_ScalarNearlyZero*SK_ScalarNearlyZero) {
|
||||||
if (kMin_MinOrMax == MIN_OR_MAX) {
|
if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
|
||||||
chosenRoot = SkMinScalar(a, c);
|
results[0] = SkMinScalar(a, c);
|
||||||
|
} else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
|
||||||
|
results[0] = SkMaxScalar(a, c);
|
||||||
} else {
|
} else {
|
||||||
chosenRoot = SkMaxScalar(a, c);
|
results[0] = a;
|
||||||
|
results[1] = c;
|
||||||
|
if (results[0] > results[1]) {
|
||||||
|
SkTSwap(results[0], results[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SkScalar aminusc = a - c;
|
SkScalar aminusc = a - c;
|
||||||
SkScalar apluscdiv2 = SkScalarHalf(a + c);
|
SkScalar apluscdiv2 = SkScalarHalf(a + c);
|
||||||
SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
|
SkScalar x = SkScalarHalf(SkScalarSqrt(aminusc * aminusc + 4 * bSqd));
|
||||||
if (kMin_MinOrMax == MIN_OR_MAX) {
|
if (kMin_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
|
||||||
chosenRoot = apluscdiv2 - x;
|
results[0] = apluscdiv2 - x;
|
||||||
|
} else if (kMax_MinMaxOrBoth == MIN_MAX_OR_BOTH) {
|
||||||
|
results[0] = apluscdiv2 + x;
|
||||||
} else {
|
} else {
|
||||||
chosenRoot = apluscdiv2 + x;
|
results[0] = apluscdiv2 - x;
|
||||||
|
results[1] = apluscdiv2 + x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SkASSERT(chosenRoot >= 0);
|
SkASSERT(results[0] >= 0);
|
||||||
return SkScalarSqrt(chosenRoot);
|
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 {
|
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 {
|
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) {
|
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) {
|
static void test_matrix_min_max_scale(skiatest::Reporter* reporter) {
|
||||||
|
SkScalar scales[2];
|
||||||
|
bool success;
|
||||||
|
|
||||||
SkMatrix identity;
|
SkMatrix identity;
|
||||||
identity.reset();
|
identity.reset();
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
|
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMinScale());
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxScale());
|
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;
|
SkMatrix scale;
|
||||||
scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
|
scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
|
REPORTER_ASSERT(reporter, SK_Scalar1 * 2 == scale.getMinScale());
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxScale());
|
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;
|
SkMatrix rot90Scale;
|
||||||
rot90Scale.setRotate(90 * SK_Scalar1);
|
rot90Scale.setRotate(90 * SK_Scalar1);
|
||||||
rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
|
rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
|
REPORTER_ASSERT(reporter, SK_Scalar1 / 4 == rot90Scale.getMinScale());
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxScale());
|
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;
|
SkMatrix rotate;
|
||||||
rotate.setRotate(128 * SK_Scalar1);
|
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));
|
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;
|
SkMatrix translate;
|
||||||
translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
|
translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
|
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMinScale());
|
||||||
REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxScale());
|
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;
|
SkMatrix perspX;
|
||||||
perspX.reset();
|
perspX.reset();
|
||||||
perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
|
perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
|
||||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
|
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
|
||||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
|
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;
|
SkMatrix perspY;
|
||||||
perspY.reset();
|
perspY.reset();
|
||||||
perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
|
perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
|
||||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
|
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
|
||||||
REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
|
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,
|
SkMatrix baseMats[] = {scale, rot90Scale, rotate,
|
||||||
translate, perspX, perspY};
|
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, (minScale < 0) == (maxScale < 0));
|
||||||
REPORTER_ASSERT(reporter, (maxScale < 0) == mat.hasPerspective());
|
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()) {
|
if (mat.hasPerspective()) {
|
||||||
m -= 1; // try another non-persp matrix
|
m -= 1; // try another non-persp matrix
|
||||||
continue;
|
continue;
|
||||||
|
Loading…
Reference in New Issue
Block a user