Add homogeneous point mapping to Matrix

Adds mapping of homogeneous points (points with three scalar components,
where the last component is not 1). Includes fix for tests when
running on 32 bit debug builds

BUG=
R=bsalomon@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@10755 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 2f16d65..8785730 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -592,6 +592,144 @@
     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];
+    SkScalar ms[9] = {m[0], m[1], m[2],
+                      m[3], m[4], m[5],
+                      m[6], m[7], m[8]};
+    res[0] = src[0] * ms[0] + src[1] * ms[1] + src[2] * ms[2];
+    res[1] = src[0] * ms[3] + src[1] * ms[4] + src[2] * ms[5];
+    res[2] = src[0] * ms[6] + src[1] * ms[7] + src[2] * ms[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 +851,7 @@
     test_matrix_is_similarity(reporter);
     test_matrix_recttorect(reporter);
     test_matrix_decomposition(reporter);
+    test_matrix_homogeneous(reporter);
 }
 
 #include "TestClassDef.h"