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