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