xref: /aosp_15_r20/external/skia/tests/ScalarTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPoint.h"
9 #include "include/core/SkRect.h"
10 #include "include/core/SkScalar.h"
11 #include "include/core/SkTypes.h"
12 #include "include/private/base/SkFloatingPoint.h"
13 #include "include/private/base/SkTo.h"
14 #include "src/base/SkFloatBits.h"
15 #include "tests/Test.h"
16 
17 #include <array>
18 #include <cmath>
19 #include <cstddef>
20 #include <cstdint>
21 
test_roundtoint(skiatest::Reporter * reporter)22 static void test_roundtoint(skiatest::Reporter* reporter) {
23     SkScalar x = 0.49999997f;
24     int ix = SkScalarRoundToInt(x);
25     int badIx = (int) floorf(x + 0.5f);
26     // We should get 0, since x < 0.5, but we wouldn't if SkScalarRoundToInt uses the commonly
27     // recommended approach shown in 'badIx' due to float addition rounding up the low
28     // bit after adding 0.5.
29     REPORTER_ASSERT(reporter, 0 == ix);
30     REPORTER_ASSERT(reporter, 1 == badIx);
31 
32     // Additionally, when the float value is between (2^23,2^24], it's precision is equal to
33     // 1 integral value. Adding 0.5f rounds up automatically *before* the floor, so naive
34     // rounding is also incorrect. Float values <= 2^23 and > 2^24 don't have this problem
35     // because either the sum can be represented sufficiently for floor() to do the right thing,
36     // or the sum will always round down to the integer multiple.
37     x = 8388609.f;
38     ix = SkScalarRoundToInt(x);
39     badIx = (int) floorf(x + 0.5f);
40     REPORTER_ASSERT(reporter, 8388609 == ix);
41     REPORTER_ASSERT(reporter, 8388610 == badIx);
42 }
43 
44 struct PointSet {
45     const SkPoint* fPts;
46     size_t         fCount;
47     bool           fIsFinite;
48 };
49 
test_isRectFinite(skiatest::Reporter * reporter)50 static void test_isRectFinite(skiatest::Reporter* reporter) {
51     static const SkPoint gF0[] = {
52         { 0, 0 }, { 1, 1 }
53     };
54     static const SkPoint gF1[] = {
55         { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }
56     };
57 
58     static const SkPoint gI0[] = {
59         { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarNaN, 3 }, { 2, 3 },
60     };
61     static const SkPoint gI1[] = {
62         { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarNaN }, { 2, 3 },
63     };
64     static const SkPoint gI2[] = {
65         { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarInfinity, 3 }, { 2, 3 },
66     };
67     static const SkPoint gI3[] = {
68         { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarInfinity }, { 2, 3 },
69     };
70 
71     static const struct {
72         const SkPoint* fPts;
73         int            fCount;
74         bool           fIsFinite;
75     } gSets[] = {
76         { gF0, std::size(gF0), true },
77         { gF1, std::size(gF1), true },
78 
79         { gI0, std::size(gI0), false },
80         { gI1, std::size(gI1), false },
81         { gI2, std::size(gI2), false },
82         { gI3, std::size(gI3), false },
83     };
84 
85     for (size_t i = 0; i < std::size(gSets); ++i) {
86         SkRect r;
87         r.setBounds(gSets[i].fPts, gSets[i].fCount);
88         bool rectIsFinite = !r.isEmpty();
89         REPORTER_ASSERT(reporter, gSets[i].fIsFinite == rectIsFinite);
90     }
91 }
92 
isFinite_int(float x)93 static bool isFinite_int(float x) {
94     uint32_t bits = SkFloat2Bits(x);    // need unsigned for our shifts
95     int exponent = bits << 1 >> 24;
96     return exponent != 0xFF;
97 }
98 
isFinite_float(float x)99 static bool isFinite_float(float x) {
100     return SkToBool(SkIsFinite(x));
101 }
102 
isFinite_mulzero(float x)103 static bool isFinite_mulzero(float x) {
104     float y = x * 0;
105     return y == y;
106 }
107 
108 // return true if the float is finite
109 typedef bool (*IsFiniteProc1)(float);
110 
isFinite2_and(float x,float y,IsFiniteProc1 proc)111 static bool isFinite2_and(float x, float y, IsFiniteProc1 proc) {
112     return proc(x) && proc(y);
113 }
114 
isFinite2_mulzeroadd(float x,float y,IsFiniteProc1 proc)115 static bool isFinite2_mulzeroadd(float x, float y, IsFiniteProc1 proc) {
116     return proc(x * 0 + y * 0);
117 }
118 
119 // return true if both floats are finite
120 typedef bool (*IsFiniteProc2)(float, float, IsFiniteProc1);
121 
122 enum FloatClass {
123     kFinite,
124     kInfinite,
125     kNaN
126 };
127 
test_floatclass(skiatest::Reporter * reporter,float value,FloatClass fc)128 static void test_floatclass(skiatest::Reporter* reporter, float value, FloatClass fc) {
129     // our sk_float_is... function may return int instead of bool,
130     // hence the double ! to turn it into a bool
131     REPORTER_ASSERT(reporter, !!SkIsFinite(value) == (fc == kFinite));
132     REPORTER_ASSERT(reporter, !!std::isinf(value) == (fc == kInfinite));
133     REPORTER_ASSERT(reporter, !!SkIsNaN(value)    == (fc == kNaN));
134 }
135 
136 #if defined _WIN32
137 #pragma warning(push)
138 // we are intentionally causing an overflow here
139 //      (warning C4756: overflow in constant arithmetic)
140 #pragma warning(disable : 4756)
141 #endif
142 
test_isfinite(skiatest::Reporter * reporter)143 static void test_isfinite(skiatest::Reporter* reporter) {
144     struct Rec {
145         float   fValue;
146         bool    fIsFinite;
147     };
148 
149     float max = 3.402823466e+38f;
150     float inf = max * max;
151     float nan = inf * 0;
152 
153     test_floatclass(reporter,    0, kFinite);
154     test_floatclass(reporter,  max, kFinite);
155     test_floatclass(reporter, -max, kFinite);
156     test_floatclass(reporter,  inf, kInfinite);
157     test_floatclass(reporter, -inf, kInfinite);
158     test_floatclass(reporter,  nan, kNaN);
159     test_floatclass(reporter, -nan, kNaN);
160 
161     const Rec data[] = {
162         {   0,           true    },
163         {   1,           true    },
164         {  -1,           true    },
165         {  max * 0.75f,  true    },
166         {  max,          true    },
167         {  -max * 0.75f, true    },
168         {  -max,         true    },
169         {  inf,          false   },
170         { -inf,          false   },
171         {  nan,          false   },
172     };
173 
174     const IsFiniteProc1 gProc1[] = {
175         isFinite_int,
176         isFinite_float,
177         isFinite_mulzero
178     };
179     const IsFiniteProc2 gProc2[] = {
180         isFinite2_and,
181         isFinite2_mulzeroadd
182     };
183 
184     size_t i, n = std::size(data);
185 
186     for (i = 0; i < n; ++i) {
187         for (size_t k = 0; k < std::size(gProc1); ++k) {
188             const Rec& rec = data[i];
189             bool finite = gProc1[k](rec.fValue);
190             REPORTER_ASSERT(reporter, rec.fIsFinite == finite);
191         }
192     }
193 
194     for (i = 0; i < n; ++i) {
195         const Rec& rec0 = data[i];
196         for (size_t j = 0; j < n; ++j) {
197             const Rec& rec1 = data[j];
198             for (size_t k = 0; k < std::size(gProc1); ++k) {
199                 IsFiniteProc1 proc1 = gProc1[k];
200 
201                 for (size_t m = 0; m < std::size(gProc2); ++m) {
202                     bool finite = gProc2[m](rec0.fValue, rec1.fValue, proc1);
203                     bool finite2 = rec0.fIsFinite && rec1.fIsFinite;
204                     REPORTER_ASSERT(reporter, finite2 == finite);
205                 }
206             }
207         }
208     }
209 
210     test_isRectFinite(reporter);
211 }
212 
213 #if defined _WIN32
214 #pragma warning ( pop )
215 #endif
216 
DEF_TEST(Scalar,reporter)217 DEF_TEST(Scalar, reporter) {
218     test_isfinite(reporter);
219     test_roundtoint(reporter);
220 }
221