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