Back out a5a5d2c176f7 (bug 882865) because of Android test failures on a CLOSED TREE
[gecko.git] / gfx / src / nsCoord.h
blob6549e3a2f2a1df3a29baf0540830f0c87ce21d03
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/. */
6 #ifndef NSCOORD_H
7 #define NSCOORD_H
9 #include "nsAlgorithm.h"
10 #include "nscore.h"
11 #include "nsMathUtils.h"
12 #include <math.h>
13 #include <float.h>
15 #include "nsDebug.h"
16 #include <algorithm>
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
24 * 96dpi as possible.
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;
34 return pun.mFloat;
36 inline bool NS_IEEEIsNan(float aF) {
37 union { uint32_t mBits; float mFloat; } pun;
38 pun.mFloat = aF;
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()
46 #else
47 typedef int32_t nscoord;
48 #define nscoord_MAX nscoord(1 << 30)
49 #endif
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");
57 #endif
60 inline nscoord NSToCoordRound(float aValue)
62 #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
63 return NS_lroundup30(aValue);
64 #else
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);
73 #else
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) {
85 return nscoord_MAX;
87 NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
88 "Overflowed nscoord_MIN in conversion to nscoord");
89 if (aValue <= nscoord_MIN) {
90 return nscoord_MIN;
92 #endif
93 return NSToCoordRound(aValue);
96 /**
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);
111 #else
112 // This one's only a warning because it may be possible to trigger it with
113 // valid inputs.
114 NS_WARN_IF_FALSE((requireNotNegative
115 ? aCoord > 0
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));
125 #endif
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)
155 inline nscoord
156 NSCoordSaturatingAdd(nscoord a, nscoord b)
158 VERIFY_COORD(a);
159 VERIFY_COORD(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.
165 return a + b;
166 #else
167 if (a == nscoord_MAX || b == nscoord_MAX) {
168 // infinity + anything = anything + infinity = infinity
169 return nscoord_MAX;
170 } else {
171 // a + b = a + b
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);
184 #endif
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.)
203 inline nscoord
204 NSCoordSaturatingSubtract(nscoord a, nscoord b,
205 nscoord infMinusInfResult)
207 VERIFY_COORD(a);
208 VERIFY_COORD(b);
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) {
214 // case (a)
215 return infMinusInfResult;
216 } else {
217 // case (b)
218 NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]");
219 return 0;
221 } else {
222 #ifdef NS_COORD_IS_FLOAT
223 // case (c) and (d) for floats. (float math handles both)
224 return a - b;
225 #else
226 if (a == nscoord_MAX) {
227 // case (c) for integers
228 return nscoord_MAX;
229 } else {
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);
244 #endif
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");
251 #endif
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) {
275 return nscoord_MAX;
277 NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
278 "Overflowed nscoord_MIN in conversion to nscoord");
279 if (aValue <= nscoord_MIN) {
280 return nscoord_MIN;
282 #endif
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) {
303 return nscoord_MAX;
305 NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
306 "Overflowed nscoord_MIN in conversion to nscoord");
307 if (aValue <= nscoord_MIN) {
308 return nscoord_MIN;
310 #endif
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
353 // nscoord to float
354 nscoord r = aPixels * (nscoord)aAppUnitsPerPixel;
355 VERIFY_COORD(r);
356 return r;
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;
379 /// handy constants
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
401 //@{
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))
413 //@}
415 #endif /* NSCOORD_H */