xref: /aosp_15_r20/external/skia/tests/PointTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPointPriv.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker #include <cfloat>
17*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
18*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
19*c8dee2aaSAndroid Build Coastguard Worker #include <string>
20*c8dee2aaSAndroid Build Coastguard Worker 
test_casts(skiatest::Reporter * reporter)21*c8dee2aaSAndroid Build Coastguard Worker static void test_casts(skiatest::Reporter* reporter) {
22*c8dee2aaSAndroid Build Coastguard Worker     SkPoint p = { 0, 0 };
23*c8dee2aaSAndroid Build Coastguard Worker     SkRect  r = { 0, 0, 0, 0 };
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar* pPtr = reinterpret_cast<const SkScalar*>(&p);
26*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar* rPtr = reinterpret_cast<const SkScalar*>(&r);
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkPointPriv::AsScalars(p) == pPtr);
29*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, r.asScalars() == rPtr);
30*c8dee2aaSAndroid Build Coastguard Worker }
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker // Tests SkPoint::Normalize() for this (x,y)
test_Normalize(skiatest::Reporter * reporter,SkScalar x,SkScalar y)33*c8dee2aaSAndroid Build Coastguard Worker static void test_Normalize(skiatest::Reporter* reporter,
34*c8dee2aaSAndroid Build Coastguard Worker                            SkScalar x, SkScalar y) {
35*c8dee2aaSAndroid Build Coastguard Worker     SkPoint point;
36*c8dee2aaSAndroid Build Coastguard Worker     point.set(x, y);
37*c8dee2aaSAndroid Build Coastguard Worker     SkScalar oldLength = point.length();
38*c8dee2aaSAndroid Build Coastguard Worker     SkScalar returned = SkPoint::Normalize(&point);
39*c8dee2aaSAndroid Build Coastguard Worker     SkScalar newLength = point.length();
40*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(returned, oldLength));
41*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker 
test_normalize_cannormalize_consistent(skiatest::Reporter * reporter)44*c8dee2aaSAndroid Build Coastguard Worker static void test_normalize_cannormalize_consistent(skiatest::Reporter* reporter) {
45*c8dee2aaSAndroid Build Coastguard Worker     const SkScalar values[] = { 1, 1e18f, 1e20f, 1e38f, SK_ScalarInfinity, SK_ScalarNaN };
46*c8dee2aaSAndroid Build Coastguard Worker 
47*c8dee2aaSAndroid Build Coastguard Worker     for (SkScalar val : values) {
48*c8dee2aaSAndroid Build Coastguard Worker         const SkScalar variants[] = { val, -val, SkScalarInvert(val), -SkScalarInvert(val) };
49*c8dee2aaSAndroid Build Coastguard Worker 
50*c8dee2aaSAndroid Build Coastguard Worker         for (SkScalar v : variants) {
51*c8dee2aaSAndroid Build Coastguard Worker             const SkPoint pts[] = { { 0, v }, { v, 0 }, { 1, v }, { v, 1 }, { v, v } };
52*c8dee2aaSAndroid Build Coastguard Worker 
53*c8dee2aaSAndroid Build Coastguard Worker             for (SkPoint p : pts) {
54*c8dee2aaSAndroid Build Coastguard Worker                 bool can = SkPointPriv::CanNormalize(p.fX, p.fY);
55*c8dee2aaSAndroid Build Coastguard Worker                 bool nor = p.normalize();
56*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, can == nor);
57*c8dee2aaSAndroid Build Coastguard Worker             }
58*c8dee2aaSAndroid Build Coastguard Worker         }
59*c8dee2aaSAndroid Build Coastguard Worker     }
60*c8dee2aaSAndroid Build Coastguard Worker }
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker // Tests that SkPoint::length() and SkPoint::Length() both return
63*c8dee2aaSAndroid Build Coastguard Worker // approximately expectedLength for this (x,y).
test_length(skiatest::Reporter * reporter,SkScalar x,SkScalar y,SkScalar expectedLength)64*c8dee2aaSAndroid Build Coastguard Worker static void test_length(skiatest::Reporter* reporter, SkScalar x, SkScalar y,
65*c8dee2aaSAndroid Build Coastguard Worker                         SkScalar expectedLength) {
66*c8dee2aaSAndroid Build Coastguard Worker     SkPoint point;
67*c8dee2aaSAndroid Build Coastguard Worker     point.set(x, y);
68*c8dee2aaSAndroid Build Coastguard Worker     SkScalar s1 = point.length();
69*c8dee2aaSAndroid Build Coastguard Worker     SkScalar s2 = SkPoint::Length(x, y);
70*c8dee2aaSAndroid Build Coastguard Worker     //The following should be exactly the same, but need not be.
71*c8dee2aaSAndroid Build Coastguard Worker     //See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
72*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2));
73*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLength));
74*c8dee2aaSAndroid Build Coastguard Worker 
75*c8dee2aaSAndroid Build Coastguard Worker     test_Normalize(reporter, x, y);
76*c8dee2aaSAndroid Build Coastguard Worker }
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker // Ugh. Windows compiler can dive into other .cpp files, and sometimes
79*c8dee2aaSAndroid Build Coastguard Worker // notices that I will generate an overflow... which is exactly the point
80*c8dee2aaSAndroid Build Coastguard Worker // of this test!
81*c8dee2aaSAndroid Build Coastguard Worker //
82*c8dee2aaSAndroid Build Coastguard Worker // To avoid this warning, I need to convince the compiler that I might not
83*c8dee2aaSAndroid Build Coastguard Worker // use that big value, hence this hacky helper function: reporter is
84*c8dee2aaSAndroid Build Coastguard Worker // ALWAYS non-null. (shhhhhh, don't tell the compiler that).
get_value(skiatest::Reporter * reporter,T value)85*c8dee2aaSAndroid Build Coastguard Worker template <typename T> T get_value(skiatest::Reporter* reporter, T value) {
86*c8dee2aaSAndroid Build Coastguard Worker     return reporter ? value : 0;
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker // On linux gcc, 32bit, we are seeing the compiler propagate up the value
90*c8dee2aaSAndroid Build Coastguard Worker // of SkPoint::length() as a double (which we use sometimes to avoid overflow
91*c8dee2aaSAndroid Build Coastguard Worker // during the computation), even though the signature says float (SkScalar).
92*c8dee2aaSAndroid Build Coastguard Worker //
93*c8dee2aaSAndroid Build Coastguard Worker // force_as_float is meant to capture our latest technique (horrible as
94*c8dee2aaSAndroid Build Coastguard Worker // it is) to force the value to be a float, so we can test whether it was
95*c8dee2aaSAndroid Build Coastguard Worker // finite or not.
force_as_float(skiatest::Reporter * reporter,float value)96*c8dee2aaSAndroid Build Coastguard Worker static float force_as_float(skiatest::Reporter* reporter, float value) {
97*c8dee2aaSAndroid Build Coastguard Worker     uint32_t storage;
98*c8dee2aaSAndroid Build Coastguard Worker     memcpy(&storage, &value, 4);
99*c8dee2aaSAndroid Build Coastguard Worker     // even the pair of memcpy calls are not sufficient, since those seem to
100*c8dee2aaSAndroid Build Coastguard Worker     // be no-op'd, so we add a runtime tests (just like get_value) to force
101*c8dee2aaSAndroid Build Coastguard Worker     // the compiler to give us an actual float.
102*c8dee2aaSAndroid Build Coastguard Worker     if (nullptr == reporter) {
103*c8dee2aaSAndroid Build Coastguard Worker         storage = ~storage;
104*c8dee2aaSAndroid Build Coastguard Worker     }
105*c8dee2aaSAndroid Build Coastguard Worker     memcpy(&value, &storage, 4);
106*c8dee2aaSAndroid Build Coastguard Worker     return value;
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker // test that we handle very large values correctly. i.e. that we can
110*c8dee2aaSAndroid Build Coastguard Worker // successfully normalize something whose mag overflows a float.
test_overflow(skiatest::Reporter * reporter)111*c8dee2aaSAndroid Build Coastguard Worker static void test_overflow(skiatest::Reporter* reporter) {
112*c8dee2aaSAndroid Build Coastguard Worker     SkScalar bigFloat = get_value(reporter, 3.4e38f);
113*c8dee2aaSAndroid Build Coastguard Worker     SkPoint pt = { bigFloat, bigFloat };
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker     SkScalar length = pt.length();
116*c8dee2aaSAndroid Build Coastguard Worker     length = force_as_float(reporter, length);
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker     // expect this to be non-finite, but dump the results if not.
119*c8dee2aaSAndroid Build Coastguard Worker     if (SkIsFinite(length)) {
120*c8dee2aaSAndroid Build Coastguard Worker         SkDebugf("length(%g, %g) == %g\n", pt.fX, pt.fY, length);
121*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, !SkIsFinite(length));
122*c8dee2aaSAndroid Build Coastguard Worker     }
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker     // this should succeed, even though we can't represent length
125*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, pt.setLength(SK_Scalar1));
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker     // now that pt is normalized, we check its length
128*c8dee2aaSAndroid Build Coastguard Worker     length = pt.length();
129*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(length, SK_Scalar1));
130*c8dee2aaSAndroid Build Coastguard Worker }
131*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Point,reporter)132*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Point, reporter) {
133*c8dee2aaSAndroid Build Coastguard Worker     test_casts(reporter);
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker     static const struct {
136*c8dee2aaSAndroid Build Coastguard Worker         SkScalar fX;
137*c8dee2aaSAndroid Build Coastguard Worker         SkScalar fY;
138*c8dee2aaSAndroid Build Coastguard Worker         SkScalar fLength;
139*c8dee2aaSAndroid Build Coastguard Worker     } gRec[] = {
140*c8dee2aaSAndroid Build Coastguard Worker         { SkIntToScalar(3), SkIntToScalar(4), SkIntToScalar(5) },
141*c8dee2aaSAndroid Build Coastguard Worker         { 0.6f, 0.8f, SK_Scalar1 },
142*c8dee2aaSAndroid Build Coastguard Worker     };
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < std::size(gRec); ++i) {
145*c8dee2aaSAndroid Build Coastguard Worker         test_length(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fLength);
146*c8dee2aaSAndroid Build Coastguard Worker     }
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     test_overflow(reporter);
149*c8dee2aaSAndroid Build Coastguard Worker     test_normalize_cannormalize_consistent(reporter);
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(Point_setLengthFast,reporter)152*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Point_setLengthFast, reporter) {
153*c8dee2aaSAndroid Build Coastguard Worker     // Scale a (1,1) point to a bunch of different lengths,
154*c8dee2aaSAndroid Build Coastguard Worker     // making sure the slow and fast paths are within 0.1%.
155*c8dee2aaSAndroid Build Coastguard Worker     const float tests[] = { 1.0f, 0.0f, 1.0e-37f, 3.4e38f, 42.0f, 0.00012f };
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint kOne = {1.0f, 1.0f};
158*c8dee2aaSAndroid Build Coastguard Worker     for (unsigned i = 0; i < std::size(tests); i++) {
159*c8dee2aaSAndroid Build Coastguard Worker         SkPoint slow = kOne, fast = kOne;
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker         slow.setLength(tests[i]);
162*c8dee2aaSAndroid Build Coastguard Worker         SkPointPriv::SetLengthFast(&fast, tests[i]);
163*c8dee2aaSAndroid Build Coastguard Worker 
164*c8dee2aaSAndroid Build Coastguard Worker         if (slow.length() < FLT_MIN && fast.length() < FLT_MIN) continue;
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker         SkScalar ratio = slow.length() / fast.length();
167*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, ratio > 0.999f);
168*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, ratio < 1.001f);
169*c8dee2aaSAndroid Build Coastguard Worker     }
170*c8dee2aaSAndroid Build Coastguard Worker }
171