|
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 |
|
15 #include "nsDebug.h" |
|
16 #include <algorithm> |
|
17 |
|
18 /* |
|
19 * Basic type used for the geometry classes. |
|
20 * |
|
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. |
|
25 */ |
|
26 |
|
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 |
|
30 |
|
31 inline float NS_IEEEPositiveInfinity() { |
|
32 union { uint32_t mPRUint32; float mFloat; } pun; |
|
33 pun.mPRUint32 = 0x7F800000; |
|
34 return pun.mFloat; |
|
35 } |
|
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; |
|
41 } |
|
42 |
|
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 |
|
50 |
|
51 #define nscoord_MIN (-nscoord_MAX) |
|
52 |
|
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 |
|
58 } |
|
59 |
|
60 inline nscoord NSToCoordRound(float aValue) |
|
61 { |
|
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__ */ |
|
67 } |
|
68 |
|
69 inline nscoord NSToCoordRound(double aValue) |
|
70 { |
|
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__ */ |
|
76 } |
|
77 |
|
78 inline nscoord NSToCoordRoundWithClamp(float aValue) |
|
79 { |
|
80 #ifndef NS_COORD_IS_FLOAT |
|
81 // Bounds-check before converting out of float, to avoid overflow |
|
82 if (aValue >= nscoord_MAX) { |
|
83 return nscoord_MAX; |
|
84 } |
|
85 if (aValue <= nscoord_MIN) { |
|
86 return nscoord_MIN; |
|
87 } |
|
88 #endif |
|
89 return NSToCoordRound(aValue); |
|
90 } |
|
91 |
|
92 /** |
|
93 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as |
|
94 * appropriate for the signs of aCoord and aScale. If requireNotNegative is |
|
95 * true, this method will enforce that aScale is not negative; use that |
|
96 * parametrization to get a check of that fact in debug builds. |
|
97 */ |
|
98 inline nscoord _nscoordSaturatingMultiply(nscoord aCoord, float aScale, |
|
99 bool requireNotNegative) { |
|
100 VERIFY_COORD(aCoord); |
|
101 if (requireNotNegative) { |
|
102 NS_ABORT_IF_FALSE(aScale >= 0.0f, |
|
103 "negative scaling factors must be handled manually"); |
|
104 } |
|
105 #ifdef NS_COORD_IS_FLOAT |
|
106 return floorf(aCoord * aScale); |
|
107 #else |
|
108 float product = aCoord * aScale; |
|
109 if (requireNotNegative ? aCoord > 0 : (aCoord > 0) == (aScale > 0)) |
|
110 return NSToCoordRoundWithClamp(std::min<float>(nscoord_MAX, product)); |
|
111 return NSToCoordRoundWithClamp(std::max<float>(nscoord_MIN, product)); |
|
112 #endif |
|
113 } |
|
114 |
|
115 /** |
|
116 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as |
|
117 * appropriate for the sign of aCoord. This method requires aScale to not be |
|
118 * negative; use this method when you know that aScale should never be |
|
119 * negative to get a sanity check of that invariant in debug builds. |
|
120 */ |
|
121 inline nscoord NSCoordSaturatingNonnegativeMultiply(nscoord aCoord, float aScale) { |
|
122 return _nscoordSaturatingMultiply(aCoord, aScale, true); |
|
123 } |
|
124 |
|
125 /** |
|
126 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as |
|
127 * appropriate for the signs of aCoord and aScale. |
|
128 */ |
|
129 inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aScale) { |
|
130 return _nscoordSaturatingMultiply(aCoord, aScale, false); |
|
131 } |
|
132 |
|
133 /** |
|
134 * Returns a + b, capping the sum to nscoord_MAX. |
|
135 * |
|
136 * This function assumes that neither argument is nscoord_MIN. |
|
137 * |
|
138 * Note: If/when we start using floats for nscoords, this function won't be as |
|
139 * necessary. Normal float addition correctly handles adding with infinity, |
|
140 * assuming we aren't adding nscoord_MIN. (-infinity) |
|
141 */ |
|
142 inline nscoord |
|
143 NSCoordSaturatingAdd(nscoord a, nscoord b) |
|
144 { |
|
145 VERIFY_COORD(a); |
|
146 VERIFY_COORD(b); |
|
147 |
|
148 #ifdef NS_COORD_IS_FLOAT |
|
149 // Float math correctly handles a+b, given that neither is -infinity. |
|
150 return a + b; |
|
151 #else |
|
152 if (a == nscoord_MAX || b == nscoord_MAX) { |
|
153 // infinity + anything = anything + infinity = infinity |
|
154 return nscoord_MAX; |
|
155 } else { |
|
156 // a + b = a + b |
|
157 // Cap the result, just in case we're dealing with numbers near nscoord_MAX |
|
158 return std::min(nscoord_MAX, a + b); |
|
159 } |
|
160 #endif |
|
161 } |
|
162 |
|
163 /** |
|
164 * Returns a - b, gracefully handling cases involving nscoord_MAX. |
|
165 * This function assumes that neither argument is nscoord_MIN. |
|
166 * |
|
167 * The behavior is as follows: |
|
168 * |
|
169 * a) infinity - infinity -> infMinusInfResult |
|
170 * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED) |
|
171 * c) infinity - N -> infinity |
|
172 * d) N1 - N2 -> N1 - N2 |
|
173 * |
|
174 * Note: For float nscoords, cases (c) and (d) are handled by normal float |
|
175 * math. We still need to explicitly specify the behavior for cases (a) |
|
176 * and (b), though. (Under normal float math, those cases would return NaN |
|
177 * and -infinity, respectively.) |
|
178 */ |
|
179 inline nscoord |
|
180 NSCoordSaturatingSubtract(nscoord a, nscoord b, |
|
181 nscoord infMinusInfResult) |
|
182 { |
|
183 VERIFY_COORD(a); |
|
184 VERIFY_COORD(b); |
|
185 |
|
186 if (b == nscoord_MAX) { |
|
187 if (a == nscoord_MAX) { |
|
188 // case (a) |
|
189 return infMinusInfResult; |
|
190 } else { |
|
191 // case (b) |
|
192 NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]"); |
|
193 return 0; |
|
194 } |
|
195 } else { |
|
196 #ifdef NS_COORD_IS_FLOAT |
|
197 // case (c) and (d) for floats. (float math handles both) |
|
198 return a - b; |
|
199 #else |
|
200 if (a == nscoord_MAX) { |
|
201 // case (c) for integers |
|
202 return nscoord_MAX; |
|
203 } else { |
|
204 // case (d) for integers |
|
205 // Cap the result, in case we're dealing with numbers near nscoord_MAX |
|
206 return std::min(nscoord_MAX, a - b); |
|
207 } |
|
208 } |
|
209 #endif |
|
210 } |
|
211 |
|
212 inline float NSCoordToFloat(nscoord aCoord) { |
|
213 VERIFY_COORD(aCoord); |
|
214 #ifdef NS_COORD_IS_FLOAT |
|
215 NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in float conversion"); |
|
216 #endif |
|
217 return (float)aCoord; |
|
218 } |
|
219 |
|
220 /* |
|
221 * Coord Rounding Functions |
|
222 */ |
|
223 inline nscoord NSToCoordFloor(float aValue) |
|
224 { |
|
225 return nscoord(floorf(aValue)); |
|
226 } |
|
227 |
|
228 inline nscoord NSToCoordFloor(double aValue) |
|
229 { |
|
230 return nscoord(floor(aValue)); |
|
231 } |
|
232 |
|
233 inline nscoord NSToCoordFloorClamped(float aValue) |
|
234 { |
|
235 #ifndef NS_COORD_IS_FLOAT |
|
236 // Bounds-check before converting out of float, to avoid overflow |
|
237 if (aValue >= nscoord_MAX) { |
|
238 return nscoord_MAX; |
|
239 } |
|
240 if (aValue <= nscoord_MIN) { |
|
241 return nscoord_MIN; |
|
242 } |
|
243 #endif |
|
244 return NSToCoordFloor(aValue); |
|
245 } |
|
246 |
|
247 inline nscoord NSToCoordCeil(float aValue) |
|
248 { |
|
249 return nscoord(ceilf(aValue)); |
|
250 } |
|
251 |
|
252 inline nscoord NSToCoordCeil(double aValue) |
|
253 { |
|
254 return nscoord(ceil(aValue)); |
|
255 } |
|
256 |
|
257 inline nscoord NSToCoordCeilClamped(double aValue) |
|
258 { |
|
259 #ifndef NS_COORD_IS_FLOAT |
|
260 // Bounds-check before converting out of double, to avoid overflow |
|
261 if (aValue >= nscoord_MAX) { |
|
262 return nscoord_MAX; |
|
263 } |
|
264 if (aValue <= nscoord_MIN) { |
|
265 return nscoord_MIN; |
|
266 } |
|
267 #endif |
|
268 return NSToCoordCeil(aValue); |
|
269 } |
|
270 |
|
271 /* |
|
272 * Int Rounding Functions |
|
273 */ |
|
274 inline int32_t NSToIntFloor(float aValue) |
|
275 { |
|
276 return int32_t(floorf(aValue)); |
|
277 } |
|
278 |
|
279 inline int32_t NSToIntCeil(float aValue) |
|
280 { |
|
281 return int32_t(ceilf(aValue)); |
|
282 } |
|
283 |
|
284 inline int32_t NSToIntRound(float aValue) |
|
285 { |
|
286 return NS_lroundf(aValue); |
|
287 } |
|
288 |
|
289 inline int32_t NSToIntRound(double aValue) |
|
290 { |
|
291 return NS_lround(aValue); |
|
292 } |
|
293 |
|
294 inline int32_t NSToIntRoundUp(double aValue) |
|
295 { |
|
296 return int32_t(floor(aValue + 0.5)); |
|
297 } |
|
298 |
|
299 /* |
|
300 * App Unit/Pixel conversions |
|
301 */ |
|
302 inline nscoord NSFloatPixelsToAppUnits(float aPixels, float aAppUnitsPerPixel) |
|
303 { |
|
304 return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel); |
|
305 } |
|
306 |
|
307 inline nscoord NSIntPixelsToAppUnits(int32_t aPixels, int32_t aAppUnitsPerPixel) |
|
308 { |
|
309 // The cast to nscoord makes sure we don't overflow if we ever change |
|
310 // nscoord to float |
|
311 nscoord r = aPixels * (nscoord)aAppUnitsPerPixel; |
|
312 VERIFY_COORD(r); |
|
313 return r; |
|
314 } |
|
315 |
|
316 inline float NSAppUnitsToFloatPixels(nscoord aAppUnits, float aAppUnitsPerPixel) |
|
317 { |
|
318 return (float(aAppUnits) / aAppUnitsPerPixel); |
|
319 } |
|
320 |
|
321 inline double NSAppUnitsToDoublePixels(nscoord aAppUnits, double aAppUnitsPerPixel) |
|
322 { |
|
323 return (double(aAppUnits) / aAppUnitsPerPixel); |
|
324 } |
|
325 |
|
326 inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits, float aAppUnitsPerPixel) |
|
327 { |
|
328 return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel); |
|
329 } |
|
330 |
|
331 inline float NSCoordScale(nscoord aCoord, int32_t aFromAPP, int32_t aToAPP) |
|
332 { |
|
333 return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP; |
|
334 } |
|
335 |
|
336 /// handy constants |
|
337 #define TWIPS_PER_POINT_INT 20 |
|
338 #define TWIPS_PER_POINT_FLOAT 20.0f |
|
339 #define POINTS_PER_INCH_INT 72 |
|
340 #define POINTS_PER_INCH_FLOAT 72.0f |
|
341 #define CM_PER_INCH_FLOAT 2.54f |
|
342 #define MM_PER_INCH_FLOAT 25.4f |
|
343 |
|
344 /* |
|
345 * Twips/unit conversions |
|
346 */ |
|
347 inline float NSUnitsToTwips(float aValue, float aPointsPerUnit) |
|
348 { |
|
349 return aValue * aPointsPerUnit * TWIPS_PER_POINT_FLOAT; |
|
350 } |
|
351 |
|
352 inline float NSTwipsToUnits(float aTwips, float aUnitsPerPoint) |
|
353 { |
|
354 return (aTwips * (aUnitsPerPoint / TWIPS_PER_POINT_FLOAT)); |
|
355 } |
|
356 |
|
357 /// Unit conversion macros |
|
358 //@{ |
|
359 #define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f) |
|
360 #define NS_INCHES_TO_TWIPS(x) NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch |
|
361 |
|
362 #define NS_MILLIMETERS_TO_TWIPS(x) NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f)) |
|
363 |
|
364 #define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x)) |
|
365 #define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x)) |
|
366 |
|
367 #define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT) |
|
368 |
|
369 #define NS_TWIPS_TO_MILLIMETERS(x) NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f)) |
|
370 //@} |
|
371 |
|
372 #endif /* NSCOORD_H */ |