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