aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/PointTest.cpp
blob: 84ea86582721f207dd37b242bc9c612d998bf6cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
// Unit tests for src/core/SkPoint.cpp and its header

#include "SkPoint.h"
#include "SkRect.h"
#include "Test.h"

static void test_casts(skiatest::Reporter* reporter) {
    SkPoint p = { 0, 0 };
    SkRect  r = { 0, 0, 0, 0 };

    const SkScalar* pPtr = SkTCast<const SkScalar*>(&p);
    const SkScalar* rPtr = SkTCast<const SkScalar*>(&r);

    REPORTER_ASSERT(reporter, p.asScalars() == pPtr);
    REPORTER_ASSERT(reporter, r.asScalars() == rPtr);
}

// Tests SkPoint::Normalize() for this (x,y)
static void test_Normalize(skiatest::Reporter* reporter,
                           SkScalar x, SkScalar y) {
    SkPoint point;
    point.set(x, y);
    SkScalar oldLength = point.length();
    SkScalar returned = SkPoint::Normalize(&point);
    SkScalar newLength = point.length();
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(returned, oldLength));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
}

// Tests that SkPoint::length() and SkPoint::Length() both return
// approximately expectedLength for this (x,y).
static void test_length(skiatest::Reporter* reporter, SkScalar x, SkScalar y,
                        SkScalar expectedLength) {
    SkPoint point;
    point.set(x, y);
    SkScalar s1 = point.length();
    SkScalar s2 = SkPoint::Length(x, y);
    //The following should be exactly the same, but need not be.
    //See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLength));

    test_Normalize(reporter, x, y);
}

// Ugh. Windows compiler can dive into other .cpp files, and sometimes
// notices that I will generate an overflow... which is exactly the point
// of this test!
//
// To avoid this warning, I need to convince the compiler that I might not
// use that big value, hence this hacky helper function: reporter is
// ALWAYS non-null. (shhhhhh, don't tell the compiler that).
template <typename T> T get_value(skiatest::Reporter* reporter, T value) {
    return reporter ? value : 0;
}

// On linux gcc, 32bit, we are seeing the compiler propagate up the value
// of SkPoint::length() as a double (which we use sometimes to avoid overflow
// during the computation), even though the signature says float (SkScalar).
//
// force_as_float is meant to capture our latest technique (horrible as
// it is) to force the value to be a float, so we can test whether it was
// finite or not.
static float force_as_float(skiatest::Reporter* reporter, float value) {
    uint32_t storage;
    memcpy(&storage, &value, 4);
    // even the pair of memcpy calls are not sufficient, since those seem to
    // be no-op'd, so we add a runtime tests (just like get_value) to force
    // the compiler to give us an actual float.
    if (nullptr == reporter) {
        storage = ~storage;
    }
    memcpy(&value, &storage, 4);
    return value;
}

// test that we handle very large values correctly. i.e. that we can
// successfully normalize something whose mag overflows a float.
static void test_overflow(skiatest::Reporter* reporter) {
    SkScalar bigFloat = get_value(reporter, 3.4e38f);
    SkPoint pt = { bigFloat, bigFloat };

    SkScalar length = pt.length();
    length = force_as_float(reporter, length);

    // expect this to be non-finite, but dump the results if not.
    if (SkScalarIsFinite(length)) {
        SkDebugf("length(%g, %g) == %g\n", pt.fX, pt.fY, length);
        REPORTER_ASSERT(reporter, !SkScalarIsFinite(length));
    }

    // this should succeed, even though we can't represent length
    REPORTER_ASSERT(reporter, pt.setLength(SK_Scalar1));

    // now that pt is normalized, we check its length
    length = pt.length();
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(length, SK_Scalar1));
}

// test that we handle very small values correctly. i.e. that we can
// report failure if we try to normalize them.
static void test_underflow(skiatest::Reporter* reporter) {
    SkPoint pt = { 1.0e-37f, 1.0e-37f };
    const SkPoint empty = { 0, 0 };

    REPORTER_ASSERT(reporter, 0 == SkPoint::Normalize(&pt));
    REPORTER_ASSERT(reporter, pt == empty);

    REPORTER_ASSERT(reporter, !pt.setLength(SK_Scalar1));
    REPORTER_ASSERT(reporter, pt == empty);
}

DEF_TEST(Point, reporter) {
    test_casts(reporter);

    static const struct {
        SkScalar fX;
        SkScalar fY;
        SkScalar fLength;
    } gRec[] = {
        { SkIntToScalar(3), SkIntToScalar(4), SkIntToScalar(5) },
        { 0.6f, 0.8f, SK_Scalar1 },
    };

    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
        test_length(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fLength);
    }

    test_underflow(reporter);
    test_overflow(reporter);
}

DEF_TEST(Point_setLengthFast, reporter) {
    // Scale a (1,1) point to a bunch of different lengths,
    // making sure the slow and fast paths are within 0.1%.
    const float tests[] = { 1.0f, 0.0f, 1.0e-37f, 3.4e38f, 42.0f, 0.00012f };

    const SkPoint kOne = {1.0f, 1.0f};
    for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); i++) {
        SkPoint slow = kOne, fast = kOne;

        slow.setLength(tests[i]);
        fast.setLengthFast(tests[i]);

        if (slow.length() < FLT_MIN && fast.length() < FLT_MIN) continue;

        SkScalar ratio = slow.length() / fast.length();
        REPORTER_ASSERT(reporter, ratio > 0.999f);
        REPORTER_ASSERT(reporter, ratio < 1.001f);
    }
}