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