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