1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "nsAlgorithm.h"
11 #include "nsMathUtils.h"
19 * Basic type used for the geometry classes.
21 * Normally all coordinates are maintained in an app unit coordinate
22 * space. An app unit is 1/60th of a CSS device pixel, which is, in turn
23 * an integer number of device pixels, such at the CSS DPI is as close to
27 // This controls whether we're using integers or floats for coordinates. We
28 // want to eventually use floats.
29 //#define NS_COORD_IS_FLOAT
31 inline float NS_IEEEPositiveInfinity() {
32 union { uint32_t mPRUint32
; float mFloat
; } pun
;
33 pun
.mPRUint32
= 0x7F800000;
36 inline bool NS_IEEEIsNan(float aF
) {
37 union { uint32_t mBits
; float mFloat
; } pun
;
39 return (pun
.mBits
& 0x7F800000) == 0x7F800000 &&
40 (pun
.mBits
& 0x007FFFFF) != 0;
43 #ifdef NS_COORD_IS_FLOAT
44 typedef float nscoord
;
45 #define nscoord_MAX NS_IEEEPositiveInfinity()
47 typedef int32_t nscoord
;
48 #define nscoord_MAX nscoord(1 << 30)
51 #define nscoord_MIN (-nscoord_MAX)
53 inline void VERIFY_COORD(nscoord aCoord
) {
54 #ifdef NS_COORD_IS_FLOAT
55 NS_ASSERTION(floorf(aCoord
) == aCoord
,
56 "Coords cannot have fractions");
60 inline nscoord
NSToCoordRound(float aValue
)
62 #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
63 return NS_lroundup30(aValue
);
65 return nscoord(floorf(aValue
+ 0.5f
));
66 #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
69 inline nscoord
NSToCoordRound(double aValue
)
71 #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
72 return NS_lroundup30((float)aValue
);
74 return nscoord(floor(aValue
+ 0.5f
));
75 #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
78 inline nscoord
NSToCoordRoundWithClamp(float aValue
)
80 #ifndef NS_COORD_IS_FLOAT
81 // Bounds-check before converting out of float, to avoid overflow
82 NS_WARN_IF_FALSE(aValue
<= nscoord_MAX
,
83 "Overflowed nscoord_MAX in conversion to nscoord");
84 if (aValue
>= nscoord_MAX
) {
87 NS_WARN_IF_FALSE(aValue
>= nscoord_MIN
,
88 "Overflowed nscoord_MIN in conversion to nscoord");
89 if (aValue
<= nscoord_MIN
) {
93 return NSToCoordRound(aValue
);
97 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
98 * appropriate for the signs of aCoord and aScale. If requireNotNegative is
99 * true, this method will enforce that aScale is not negative; use that
100 * parametrization to get a check of that fact in debug builds.
102 inline nscoord
_nscoordSaturatingMultiply(nscoord aCoord
, float aScale
,
103 bool requireNotNegative
) {
104 VERIFY_COORD(aCoord
);
105 if (requireNotNegative
) {
106 NS_ABORT_IF_FALSE(aScale
>= 0.0f
,
107 "negative scaling factors must be handled manually");
109 #ifdef NS_COORD_IS_FLOAT
110 return floorf(aCoord
* aScale
);
112 // This one's only a warning because it may be possible to trigger it with
114 NS_WARN_IF_FALSE((requireNotNegative
116 : (aCoord
> 0) == (aScale
> 0))
117 ? floorf(aCoord
* aScale
) < nscoord_MAX
118 : ceilf(aCoord
* aScale
) > nscoord_MIN
,
119 "nscoord multiplication capped");
121 float product
= aCoord
* aScale
;
122 if (requireNotNegative
? aCoord
> 0 : (aCoord
> 0) == (aScale
> 0))
123 return NSToCoordRoundWithClamp(std::min
<float>(nscoord_MAX
, product
));
124 return NSToCoordRoundWithClamp(std::max
<float>(nscoord_MIN
, product
));
129 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
130 * appropriate for the sign of aCoord. This method requires aScale to not be
131 * negative; use this method when you know that aScale should never be
132 * negative to get a sanity check of that invariant in debug builds.
134 inline nscoord
NSCoordSaturatingNonnegativeMultiply(nscoord aCoord
, float aScale
) {
135 return _nscoordSaturatingMultiply(aCoord
, aScale
, true);
139 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
140 * appropriate for the signs of aCoord and aScale.
142 inline nscoord
NSCoordSaturatingMultiply(nscoord aCoord
, float aScale
) {
143 return _nscoordSaturatingMultiply(aCoord
, aScale
, false);
147 * Returns a + b, capping the sum to nscoord_MAX.
149 * This function assumes that neither argument is nscoord_MIN.
151 * Note: If/when we start using floats for nscoords, this function won't be as
152 * necessary. Normal float addition correctly handles adding with infinity,
153 * assuming we aren't adding nscoord_MIN. (-infinity)
156 NSCoordSaturatingAdd(nscoord a
, nscoord b
)
160 NS_ASSERTION(a
!= nscoord_MIN
&& b
!= nscoord_MIN
,
161 "NSCoordSaturatingAdd got nscoord_MIN as argument");
163 #ifdef NS_COORD_IS_FLOAT
164 // Float math correctly handles a+b, given that neither is -infinity.
167 if (a
== nscoord_MAX
|| b
== nscoord_MAX
) {
168 // infinity + anything = anything + infinity = infinity
172 NS_ASSERTION(a
< nscoord_MAX
&& b
< nscoord_MAX
,
173 "Doing nscoord addition with values > nscoord_MAX");
174 NS_ASSERTION((int64_t)a
+ (int64_t)b
> (int64_t)nscoord_MIN
,
175 "nscoord addition will reach or pass nscoord_MIN");
176 // This one's only a warning because the std::min below means that
177 // we'll handle this case correctly.
178 NS_WARN_IF_FALSE((int64_t)a
+ (int64_t)b
< (int64_t)nscoord_MAX
,
179 "nscoord addition capped to nscoord_MAX");
181 // Cap the result, just in case we're dealing with numbers near nscoord_MAX
182 return std::min(nscoord_MAX
, a
+ b
);
188 * Returns a - b, gracefully handling cases involving nscoord_MAX.
189 * This function assumes that neither argument is nscoord_MIN.
191 * The behavior is as follows:
193 * a) infinity - infinity -> infMinusInfResult
194 * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED)
195 * c) infinity - N -> infinity
196 * d) N1 - N2 -> N1 - N2
198 * Note: For float nscoords, cases (c) and (d) are handled by normal float
199 * math. We still need to explicitly specify the behavior for cases (a)
200 * and (b), though. (Under normal float math, those cases would return NaN
201 * and -infinity, respectively.)
204 NSCoordSaturatingSubtract(nscoord a
, nscoord b
,
205 nscoord infMinusInfResult
)
209 NS_ASSERTION(a
!= nscoord_MIN
&& b
!= nscoord_MIN
,
210 "NSCoordSaturatingSubtract got nscoord_MIN as argument");
212 if (b
== nscoord_MAX
) {
213 if (a
== nscoord_MAX
) {
215 return infMinusInfResult
;
218 NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]");
222 #ifdef NS_COORD_IS_FLOAT
223 // case (c) and (d) for floats. (float math handles both)
226 if (a
== nscoord_MAX
) {
227 // case (c) for integers
230 // case (d) for integers
231 NS_ASSERTION(a
< nscoord_MAX
&& b
< nscoord_MAX
,
232 "Doing nscoord subtraction with values > nscoord_MAX");
233 NS_ASSERTION((int64_t)a
- (int64_t)b
> (int64_t)nscoord_MIN
,
234 "nscoord subtraction will reach or pass nscoord_MIN");
235 // This one's only a warning because the std::min below means that
236 // we'll handle this case correctly.
237 NS_WARN_IF_FALSE((int64_t)a
- (int64_t)b
< (int64_t)nscoord_MAX
,
238 "nscoord subtraction capped to nscoord_MAX");
240 // Cap the result, in case we're dealing with numbers near nscoord_MAX
241 return std::min(nscoord_MAX
, a
- b
);
247 inline float NSCoordToFloat(nscoord aCoord
) {
248 VERIFY_COORD(aCoord
);
249 #ifdef NS_COORD_IS_FLOAT
250 NS_ASSERTION(!NS_IEEEIsNan(aCoord
), "NaN encountered in float conversion");
252 return (float)aCoord
;
256 * Coord Rounding Functions
258 inline nscoord
NSToCoordFloor(float aValue
)
260 return nscoord(floorf(aValue
));
263 inline nscoord
NSToCoordFloor(double aValue
)
265 return nscoord(floor(aValue
));
268 inline nscoord
NSToCoordFloorClamped(float aValue
)
270 #ifndef NS_COORD_IS_FLOAT
271 // Bounds-check before converting out of float, to avoid overflow
272 NS_WARN_IF_FALSE(aValue
<= nscoord_MAX
,
273 "Overflowed nscoord_MAX in conversion to nscoord");
274 if (aValue
>= nscoord_MAX
) {
277 NS_WARN_IF_FALSE(aValue
>= nscoord_MIN
,
278 "Overflowed nscoord_MIN in conversion to nscoord");
279 if (aValue
<= nscoord_MIN
) {
283 return NSToCoordFloor(aValue
);
286 inline nscoord
NSToCoordCeil(float aValue
)
288 return nscoord(ceilf(aValue
));
291 inline nscoord
NSToCoordCeil(double aValue
)
293 return nscoord(ceil(aValue
));
296 inline nscoord
NSToCoordCeilClamped(double aValue
)
298 #ifndef NS_COORD_IS_FLOAT
299 // Bounds-check before converting out of double, to avoid overflow
300 NS_WARN_IF_FALSE(aValue
<= nscoord_MAX
,
301 "Overflowed nscoord_MAX in conversion to nscoord");
302 if (aValue
>= nscoord_MAX
) {
305 NS_WARN_IF_FALSE(aValue
>= nscoord_MIN
,
306 "Overflowed nscoord_MIN in conversion to nscoord");
307 if (aValue
<= nscoord_MIN
) {
311 return NSToCoordCeil(aValue
);
315 * Int Rounding Functions
317 inline int32_t NSToIntFloor(float aValue
)
319 return int32_t(floorf(aValue
));
322 inline int32_t NSToIntCeil(float aValue
)
324 return int32_t(ceilf(aValue
));
327 inline int32_t NSToIntRound(float aValue
)
329 return NS_lroundf(aValue
);
332 inline int32_t NSToIntRound(double aValue
)
334 return NS_lround(aValue
);
337 inline int32_t NSToIntRoundUp(double aValue
)
339 return int32_t(floor(aValue
+ 0.5));
343 * App Unit/Pixel conversions
345 inline nscoord
NSFloatPixelsToAppUnits(float aPixels
, float aAppUnitsPerPixel
)
347 return NSToCoordRoundWithClamp(aPixels
* aAppUnitsPerPixel
);
350 inline nscoord
NSIntPixelsToAppUnits(int32_t aPixels
, int32_t aAppUnitsPerPixel
)
352 // The cast to nscoord makes sure we don't overflow if we ever change
354 nscoord r
= aPixels
* (nscoord
)aAppUnitsPerPixel
;
359 inline float NSAppUnitsToFloatPixels(nscoord aAppUnits
, float aAppUnitsPerPixel
)
361 return (float(aAppUnits
) / aAppUnitsPerPixel
);
364 inline double NSAppUnitsToDoublePixels(nscoord aAppUnits
, double aAppUnitsPerPixel
)
366 return (double(aAppUnits
) / aAppUnitsPerPixel
);
369 inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits
, float aAppUnitsPerPixel
)
371 return NSToIntRound(float(aAppUnits
) / aAppUnitsPerPixel
);
374 inline float NSCoordScale(nscoord aCoord
, int32_t aFromAPP
, int32_t aToAPP
)
376 return (NSCoordToFloat(aCoord
) * aToAPP
) / aFromAPP
;
380 #define TWIPS_PER_POINT_INT 20
381 #define TWIPS_PER_POINT_FLOAT 20.0f
382 #define POINTS_PER_INCH_INT 72
383 #define POINTS_PER_INCH_FLOAT 72.0f
384 #define CM_PER_INCH_FLOAT 2.54f
385 #define MM_PER_INCH_FLOAT 25.4f
388 * Twips/unit conversions
390 inline float NSUnitsToTwips(float aValue
, float aPointsPerUnit
)
392 return aValue
* aPointsPerUnit
* TWIPS_PER_POINT_FLOAT
;
395 inline float NSTwipsToUnits(float aTwips
, float aUnitsPerPoint
)
397 return (aTwips
* (aUnitsPerPoint
/ TWIPS_PER_POINT_FLOAT
));
400 /// Unit conversion macros
402 #define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f)
403 #define NS_INCHES_TO_TWIPS(x) NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch
405 #define NS_MILLIMETERS_TO_TWIPS(x) NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f))
407 #define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x))
408 #define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x))
410 #define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT)
412 #define NS_TWIPS_TO_MILLIMETERS(x) NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f))
415 #endif /* NSCOORD_H */