1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef NSCOORD_H
8 #define NSCOORD_H
9 
10 #include "nsAlgorithm.h"
11 #include "nscore.h"
12 #include "nsMathUtils.h"
13 #include <math.h>
14 #include <float.h>
15 #include <stdlib.h>
16 
17 #include "nsDebug.h"
18 #include <algorithm>
19 
20 /*
21  * Basic type used for the geometry classes.
22  *
23  * Normally all coordinates are maintained in an app unit coordinate
24  * space. An app unit is 1/60th of a CSS device pixel, which is, in turn
25  * an integer number of device pixels, such at the CSS DPI is as close to
26  * 96dpi as possible.
27  */
28 
29 // This controls whether we're using integers or floats for coordinates. We
30 // want to eventually use floats.
31 //#define NS_COORD_IS_FLOAT
32 
NS_IEEEPositiveInfinity()33 inline float NS_IEEEPositiveInfinity() {
34   union {
35     uint32_t mPRUint32;
36     float mFloat;
37   } pun;
38   pun.mPRUint32 = 0x7F800000;
39   return pun.mFloat;
40 }
NS_IEEEIsNan(float aF)41 inline bool NS_IEEEIsNan(float aF) {
42   union {
43     uint32_t mBits;
44     float mFloat;
45   } pun;
46   pun.mFloat = aF;
47   return (pun.mBits & 0x7F800000) == 0x7F800000 &&
48          (pun.mBits & 0x007FFFFF) != 0;
49 }
50 
51 #ifdef NS_COORD_IS_FLOAT
52 typedef float nscoord;
53 #define nscoord_MAX NS_IEEEPositiveInfinity()
54 #else
55 typedef int32_t nscoord;
56 #define nscoord_MAX nscoord((1 << 30) - 1)
57 #endif
58 
59 #define nscoord_MIN (-nscoord_MAX)
60 
VERIFY_COORD(nscoord aCoord)61 inline void VERIFY_COORD(nscoord aCoord) {
62 #ifdef NS_COORD_IS_FLOAT
63   NS_ASSERTION(floorf(aCoord) == aCoord, "Coords cannot have fractions");
64 #endif
65 }
66 
67 /**
68  * Divide aSpace by aN.  Assign the resulting quotient to aQuotient and
69  * return the remainder.
70  */
NSCoordDivRem(nscoord aSpace,size_t aN,nscoord * aQuotient)71 inline nscoord NSCoordDivRem(nscoord aSpace, size_t aN, nscoord* aQuotient) {
72 #ifdef NS_COORD_IS_FLOAT
73   *aQuotient = aSpace / aN;
74   return 0.0f;
75 #else
76   div_t result = div(aSpace, aN);
77   *aQuotient = nscoord(result.quot);
78   return nscoord(result.rem);
79 #endif
80 }
81 
NSCoordMulDiv(nscoord aMult1,nscoord aMult2,nscoord aDiv)82 inline nscoord NSCoordMulDiv(nscoord aMult1, nscoord aMult2, nscoord aDiv) {
83 #ifdef NS_COORD_IS_FLOAT
84   return (aMult1 * aMult2 / aDiv);
85 #else
86   return (int64_t(aMult1) * int64_t(aMult2) / int64_t(aDiv));
87 #endif
88 }
89 
NSToCoordRound(float aValue)90 inline nscoord NSToCoordRound(float aValue) {
91 #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && \
92     !defined(__clang__)
93   return NS_lroundup30(aValue);
94 #else
95   return nscoord(floorf(aValue + 0.5f));
96 #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
97 }
98 
NSToCoordRound(double aValue)99 inline nscoord NSToCoordRound(double aValue) {
100 #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) && \
101     !defined(__clang__)
102   return NS_lroundup30((float)aValue);
103 #else
104   return nscoord(floor(aValue + 0.5f));
105 #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
106 }
107 
NSToCoordRoundWithClamp(float aValue)108 inline nscoord NSToCoordRoundWithClamp(float aValue) {
109 #ifndef NS_COORD_IS_FLOAT
110   // Bounds-check before converting out of float, to avoid overflow
111   if (aValue >= nscoord_MAX) {
112     return nscoord_MAX;
113   }
114   if (aValue <= nscoord_MIN) {
115     return nscoord_MIN;
116   }
117 #endif
118   return NSToCoordRound(aValue);
119 }
120 
121 /**
122  * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
123  * appropriate for the signs of aCoord and aScale.  If requireNotNegative is
124  * true, this method will enforce that aScale is not negative; use that
125  * parametrization to get a check of that fact in debug builds.
126  */
_nscoordSaturatingMultiply(nscoord aCoord,float aScale,bool requireNotNegative)127 inline nscoord _nscoordSaturatingMultiply(nscoord aCoord, float aScale,
128                                           bool requireNotNegative) {
129   VERIFY_COORD(aCoord);
130   if (requireNotNegative) {
131     MOZ_ASSERT(aScale >= 0.0f,
132                "negative scaling factors must be handled manually");
133   }
134 #ifdef NS_COORD_IS_FLOAT
135   return floorf(aCoord * aScale);
136 #else
137   float product = aCoord * aScale;
138   if (requireNotNegative ? aCoord > 0 : (aCoord > 0) == (aScale > 0))
139     return NSToCoordRoundWithClamp(
140         std::min<float>((float)nscoord_MAX, product));
141   return NSToCoordRoundWithClamp(std::max<float>((float)nscoord_MIN, product));
142 #endif
143 }
144 
145 /**
146  * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
147  * appropriate for the sign of aCoord.  This method requires aScale to not be
148  * negative; use this method when you know that aScale should never be
149  * negative to get a sanity check of that invariant in debug builds.
150  */
NSCoordSaturatingNonnegativeMultiply(nscoord aCoord,float aScale)151 inline nscoord NSCoordSaturatingNonnegativeMultiply(nscoord aCoord,
152                                                     float aScale) {
153   return _nscoordSaturatingMultiply(aCoord, aScale, true);
154 }
155 
156 /**
157  * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
158  * appropriate for the signs of aCoord and aScale.
159  */
NSCoordSaturatingMultiply(nscoord aCoord,float aScale)160 inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aScale) {
161   return _nscoordSaturatingMultiply(aCoord, aScale, false);
162 }
163 
164 /**
165  * Returns a + b, capping the sum to nscoord_MAX.
166  *
167  * This function assumes that neither argument is nscoord_MIN.
168  *
169  * Note: If/when we start using floats for nscoords, this function won't be as
170  * necessary.  Normal float addition correctly handles adding with infinity,
171  * assuming we aren't adding nscoord_MIN. (-infinity)
172  */
NSCoordSaturatingAdd(nscoord a,nscoord b)173 inline nscoord NSCoordSaturatingAdd(nscoord a, nscoord b) {
174   VERIFY_COORD(a);
175   VERIFY_COORD(b);
176 
177 #ifdef NS_COORD_IS_FLOAT
178   // Float math correctly handles a+b, given that neither is -infinity.
179   return a + b;
180 #else
181   if (a == nscoord_MAX || b == nscoord_MAX) {
182     // infinity + anything = anything + infinity = infinity
183     return nscoord_MAX;
184   } else {
185     // a + b = a + b
186     // Cap the result, just in case we're dealing with numbers near nscoord_MAX
187     return std::min(nscoord_MAX, a + b);
188   }
189 #endif
190 }
191 
192 /**
193  * Returns a - b, gracefully handling cases involving nscoord_MAX.
194  * This function assumes that neither argument is nscoord_MIN.
195  *
196  * The behavior is as follows:
197  *
198  *  a)  infinity - infinity -> infMinusInfResult
199  *  b)  N - infinity        -> 0  (unexpected -- triggers NOTREACHED)
200  *  c)  infinity - N        -> infinity
201  *  d)  N1 - N2             -> N1 - N2
202  *
203  * Note: For float nscoords, cases (c) and (d) are handled by normal float
204  * math.  We still need to explicitly specify the behavior for cases (a)
205  * and (b), though.  (Under normal float math, those cases would return NaN
206  * and -infinity, respectively.)
207  */
NSCoordSaturatingSubtract(nscoord a,nscoord b,nscoord infMinusInfResult)208 inline nscoord NSCoordSaturatingSubtract(nscoord a, nscoord b,
209                                          nscoord infMinusInfResult) {
210   VERIFY_COORD(a);
211   VERIFY_COORD(b);
212 
213   if (b == nscoord_MAX) {
214     if (a == nscoord_MAX) {
215       // case (a)
216       return infMinusInfResult;
217     } else {
218       // case (b)
219       NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]");
220       return 0;
221     }
222   } else {
223 #ifdef NS_COORD_IS_FLOAT
224     // case (c) and (d) for floats.  (float math handles both)
225     return a - b;
226 #else
227     if (a == nscoord_MAX) {
228       // case (c) for integers
229       return nscoord_MAX;
230     } else {
231       // case (d) for integers
232       // Cap the result, in case we're dealing with numbers near nscoord_MAX
233       return std::min(nscoord_MAX, a - b);
234     }
235 #endif
236   }
237 }
238 
NSCoordToFloat(nscoord aCoord)239 inline float NSCoordToFloat(nscoord aCoord) {
240   VERIFY_COORD(aCoord);
241 #ifdef NS_COORD_IS_FLOAT
242   NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in float conversion");
243 #endif
244   return (float)aCoord;
245 }
246 
247 /*
248  * Coord Rounding Functions
249  */
NSToCoordFloor(float aValue)250 inline nscoord NSToCoordFloor(float aValue) { return nscoord(floorf(aValue)); }
251 
NSToCoordFloor(double aValue)252 inline nscoord NSToCoordFloor(double aValue) { return nscoord(floor(aValue)); }
253 
NSToCoordFloorClamped(float aValue)254 inline nscoord NSToCoordFloorClamped(float aValue) {
255 #ifndef NS_COORD_IS_FLOAT
256   // Bounds-check before converting out of float, to avoid overflow
257   if (aValue >= nscoord_MAX) {
258     return nscoord_MAX;
259   }
260   if (aValue <= nscoord_MIN) {
261     return nscoord_MIN;
262   }
263 #endif
264   return NSToCoordFloor(aValue);
265 }
266 
NSToCoordCeil(float aValue)267 inline nscoord NSToCoordCeil(float aValue) { return nscoord(ceilf(aValue)); }
268 
NSToCoordCeil(double aValue)269 inline nscoord NSToCoordCeil(double aValue) { return nscoord(ceil(aValue)); }
270 
NSToCoordCeilClamped(double aValue)271 inline nscoord NSToCoordCeilClamped(double aValue) {
272 #ifndef NS_COORD_IS_FLOAT
273   // Bounds-check before converting out of double, to avoid overflow
274   if (aValue >= nscoord_MAX) {
275     return nscoord_MAX;
276   }
277   if (aValue <= nscoord_MIN) {
278     return nscoord_MIN;
279   }
280 #endif
281   return NSToCoordCeil(aValue);
282 }
283 
284 // The NSToCoordTrunc* functions remove the fractional component of
285 // aValue, and are thus equivalent to NSToCoordFloor* for positive
286 // values and NSToCoordCeil* for negative values.
287 
NSToCoordTrunc(float aValue)288 inline nscoord NSToCoordTrunc(float aValue) {
289   // There's no need to use truncf() since it matches the default
290   // rules for float to integer conversion.
291   return nscoord(aValue);
292 }
293 
NSToCoordTrunc(double aValue)294 inline nscoord NSToCoordTrunc(double aValue) {
295   // There's no need to use trunc() since it matches the default
296   // rules for float to integer conversion.
297   return nscoord(aValue);
298 }
299 
NSToCoordTruncClamped(float aValue)300 inline nscoord NSToCoordTruncClamped(float aValue) {
301 #ifndef NS_COORD_IS_FLOAT
302   // Bounds-check before converting out of float, to avoid overflow
303   if (aValue >= nscoord_MAX) {
304     return nscoord_MAX;
305   }
306   if (aValue <= nscoord_MIN) {
307     return nscoord_MIN;
308   }
309 #endif
310   return NSToCoordTrunc(aValue);
311 }
312 
NSToCoordTruncClamped(double aValue)313 inline nscoord NSToCoordTruncClamped(double aValue) {
314 #ifndef NS_COORD_IS_FLOAT
315   // Bounds-check before converting out of double, to avoid overflow
316   if (aValue >= nscoord_MAX) {
317     return nscoord_MAX;
318   }
319   if (aValue <= nscoord_MIN) {
320     return nscoord_MIN;
321   }
322 #endif
323   return NSToCoordTrunc(aValue);
324 }
325 
326 /*
327  * Int Rounding Functions
328  */
NSToIntFloor(float aValue)329 inline int32_t NSToIntFloor(float aValue) { return int32_t(floorf(aValue)); }
330 
NSToIntCeil(float aValue)331 inline int32_t NSToIntCeil(float aValue) { return int32_t(ceilf(aValue)); }
332 
NSToIntRound(float aValue)333 inline int32_t NSToIntRound(float aValue) { return NS_lroundf(aValue); }
334 
NSToIntRound(double aValue)335 inline int32_t NSToIntRound(double aValue) { return NS_lround(aValue); }
336 
NSToIntRoundUp(double aValue)337 inline int32_t NSToIntRoundUp(double aValue) {
338   return int32_t(floor(aValue + 0.5));
339 }
340 
341 /*
342  * App Unit/Pixel conversions
343  */
NSFloatPixelsToAppUnits(float aPixels,float aAppUnitsPerPixel)344 inline nscoord NSFloatPixelsToAppUnits(float aPixels, float aAppUnitsPerPixel) {
345   return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel);
346 }
347 
NSIntPixelsToAppUnits(int32_t aPixels,int32_t aAppUnitsPerPixel)348 inline nscoord NSIntPixelsToAppUnits(int32_t aPixels,
349                                      int32_t aAppUnitsPerPixel) {
350   // The cast to nscoord makes sure we don't overflow if we ever change
351   // nscoord to float
352   nscoord r = aPixels * (nscoord)aAppUnitsPerPixel;
353   VERIFY_COORD(r);
354   return r;
355 }
356 
NSAppUnitsToFloatPixels(nscoord aAppUnits,float aAppUnitsPerPixel)357 inline float NSAppUnitsToFloatPixels(nscoord aAppUnits,
358                                      float aAppUnitsPerPixel) {
359   return (float(aAppUnits) / aAppUnitsPerPixel);
360 }
361 
NSAppUnitsToDoublePixels(nscoord aAppUnits,double aAppUnitsPerPixel)362 inline double NSAppUnitsToDoublePixels(nscoord aAppUnits,
363                                        double aAppUnitsPerPixel) {
364   return (double(aAppUnits) / aAppUnitsPerPixel);
365 }
366 
NSAppUnitsToIntPixels(nscoord aAppUnits,float aAppUnitsPerPixel)367 inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits,
368                                      float aAppUnitsPerPixel) {
369   return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel);
370 }
371 
NSCoordScale(nscoord aCoord,int32_t aFromAPP,int32_t aToAPP)372 inline float NSCoordScale(nscoord aCoord, int32_t aFromAPP, int32_t aToAPP) {
373   return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP;
374 }
375 
376 /// handy constants
377 #define TWIPS_PER_POINT_INT 20
378 #define TWIPS_PER_POINT_FLOAT 20.0f
379 #define POINTS_PER_INCH_INT 72
380 #define POINTS_PER_INCH_FLOAT 72.0f
381 #define CM_PER_INCH_FLOAT 2.54f
382 #define MM_PER_INCH_FLOAT 25.4f
383 
384 /*
385  * Twips/unit conversions
386  */
NSUnitsToTwips(float aValue,float aPointsPerUnit)387 inline float NSUnitsToTwips(float aValue, float aPointsPerUnit) {
388   return aValue * aPointsPerUnit * TWIPS_PER_POINT_FLOAT;
389 }
390 
NSTwipsToUnits(float aTwips,float aUnitsPerPoint)391 inline float NSTwipsToUnits(float aTwips, float aUnitsPerPoint) {
392   return (aTwips * (aUnitsPerPoint / TWIPS_PER_POINT_FLOAT));
393 }
394 
395 /// Unit conversion macros
396 //@{
397 #define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f)
398 #define NS_INCHES_TO_TWIPS(x) \
399   NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT)  // 72 points per inch
400 
401 #define NS_MILLIMETERS_TO_TWIPS(x) \
402   NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f))
403 
404 #define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x))
405 #define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x))
406 
407 #define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT)
408 
409 #define NS_TWIPS_TO_MILLIMETERS(x) \
410   NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f))
411   //@}
412 
413 #endif /* NSCOORD_H */
414