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