Add a map homogenous points to SkMatrix

BUG=
R=bsalomon@google.com

Review URL: https://codereview.chromium.org/22330004

git-svn-id: http://skia.googlecode.com/svn/trunk@10659 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
egdaniel@google.com 2013-08-09 15:07:10 +00:00
parent 3f05c91690
commit 2fae54d945
3 changed files with 186 additions and 1 deletions

View File

@ -423,6 +423,19 @@ public:
}
}
/** Apply this matrix to the array of homogeneous points, specified by src,
where a homogeneous point is defined by 3 contiguous scalar values,
and write the transformed points into the array of scalars specified by dst.
dst[] = M * src[]
@param dst Where the transformed coordinates are written. It must
contain at least 3 * count entries
@param src The original coordinates that are to be transformed. It
must contain at least 3 * count entries
@param count The number of triples (homogeneous points) in src to read,
and then transform into dst.
*/
void mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const;
void mapXY(SkScalar x, SkScalar y, SkPoint* result) const {
SkASSERT(result);
this->getMapXYProc()(*this, x, y, result);

View File

@ -1218,7 +1218,7 @@ const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
};
void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
SkASSERT((dst && src && count > 0) || count == 0);
SkASSERT((dst && src && count > 0) || 0 == count);
// no partial overlap
SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= count);
@ -1227,6 +1227,42 @@ void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
///////////////////////////////////////////////////////////////////////////////
void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
SkASSERT((dst && src && count > 0) || 0 == count);
// no partial overlap
SkASSERT(src == dst || SkAbs32((int32_t)(src - dst)) >= 3*count);
if (count > 0) {
if (this->isIdentity()) {
memcpy(dst, src, 3*count*sizeof(SkScalar));
return;
}
do {
SkScalar sx = src[0];
SkScalar sy = src[1];
SkScalar sw = src[2];
src += 3;
SkScalar x = SkScalarMul(sx, fMat[kMScaleX]) +
SkScalarMul(sy, fMat[kMSkewX]) +
SkScalarMul(sw, fMat[kMTransX]);
SkScalar y = SkScalarMul(sx, fMat[kMSkewY]) +
SkScalarMul(sy, fMat[kMScaleY]) +
SkScalarMul(sw, fMat[kMTransY]);
SkScalar w = SkScalarMul(sx, fMat[kMPersp0]) +
SkScalarMul(sy, fMat[kMPersp1]) +
SkScalarMul(sw, fMat[kMPersp2]);
dst[0] = x;
dst[1] = y;
dst[2] = w;
dst += 3;
} while (--count);
}
}
///////////////////////////////////////////////////////////////////////////////
void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const {
if (this->hasPerspective()) {
SkPoint origin;

View File

@ -592,6 +592,141 @@ static void test_matrix_decomposition(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
}
// For test_matrix_homogeneous, below.
static bool scalar_array_nearly_equal_relative(const SkScalar a[], const SkScalar b[], int count) {
for (int i = 0; i < count; ++i) {
if (!scalar_nearly_equal_relative(a[i], b[i])) {
return false;
}
}
return true;
}
// For test_matrix_homogeneous, below.
// Maps a single triple in src using m and compares results to those in dst
static bool naive_homogeneous_mapping(const SkMatrix& m, const SkScalar src[3],
const SkScalar dst[3]) {
SkScalar res[3];
res[0] = src[0] * m[0] + src[1] * m[1] + src[2] * m[2];
res[1] = src[0] * m[3] + src[1] * m[4] + src[2] * m[5];
res[2] = src[0] * m[6] + src[1] * m[7] + src[2] * m[8];
return scalar_array_nearly_equal_relative(res, dst, 3);
}
static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
SkMatrix mat;
const float kRotation0 = 15.5f;
const float kRotation1 = -50.f;
const float kScale0 = 5000.f;
const int kTripleCount = 1000;
const int kMatrixCount = 1000;
SkRandom rand;
SkScalar randTriples[3*kTripleCount];
for (int i = 0; i < 3*kTripleCount; ++i) {
randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
}
SkMatrix mats[kMatrixCount];
for (int i = 0; i < kMatrixCount; ++i) {
for (int j = 0; j < 9; ++j) {
mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
}
}
// identity
{
mat.reset();
SkScalar dst[3*kTripleCount];
mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
}
// zero matrix
{
mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
SkScalar dst[3*kTripleCount];
mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
SkScalar zeros[3] = {0.f, 0.f, 0.f};
for (int i = 0; i < kTripleCount; ++i) {
REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
}
}
// zero point
{
SkScalar zeros[3] = {0.f, 0.f, 0.f};
for (int i = 0; i < kMatrixCount; ++i) {
SkScalar dst[3];
mats[i].mapHomogeneousPoints(dst, zeros, 1);
REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
}
}
// doesn't crash with null dst, src, count == 0
{
mats[0].mapHomogeneousPoints(NULL, NULL, 0);
}
// uniform scale of point
{
mat.setScale(kScale0, kScale0);
SkScalar dst[3];
SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
SkPoint pnt;
pnt.set(src[0], src[1]);
mat.mapHomogeneousPoints(dst, src, 1);
mat.mapPoints(&pnt, &pnt, 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
}
// rotation of point
{
mat.setRotate(kRotation0);
SkScalar dst[3];
SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
SkPoint pnt;
pnt.set(src[0], src[1]);
mat.mapHomogeneousPoints(dst, src, 1);
mat.mapPoints(&pnt, &pnt, 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
}
// rotation, scale, rotation of point
{
mat.setRotate(kRotation1);
mat.postScale(kScale0, kScale0);
mat.postRotate(kRotation0);
SkScalar dst[3];
SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
SkPoint pnt;
pnt.set(src[0], src[1]);
mat.mapHomogeneousPoints(dst, src, 1);
mat.mapPoints(&pnt, &pnt, 1);
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
}
// compare with naive approach
{
for (int i = 0; i < kMatrixCount; ++i) {
for (int j = 0; j < kTripleCount; ++j) {
SkScalar dst[3];
mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
}
}
}
}
static void TestMatrix(skiatest::Reporter* reporter) {
SkMatrix mat, inverse, iden1, iden2;
@ -713,6 +848,7 @@ static void TestMatrix(skiatest::Reporter* reporter) {
test_matrix_is_similarity(reporter);
test_matrix_recttorect(reporter);
test_matrix_decomposition(reporter);
test_matrix_homogeneous(reporter);
}
#include "TestClassDef.h"