1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #ifndef NSCOORD_H
39 : #define NSCOORD_H
40 :
41 : #include "nsAlgorithm.h"
42 : #include "nscore.h"
43 : #include "nsMathUtils.h"
44 : #include <math.h>
45 : #include <float.h>
46 :
47 : #include "nsDebug.h"
48 :
49 : /*
50 : * Basic type used for the geometry classes.
51 : *
52 : * Normally all coordinates are maintained in an app unit coordinate
53 : * space. An app unit is 1/60th of a CSS device pixel, which is, in turn
54 : * an integer number of device pixels, such at the CSS DPI is as close to
55 : * 96dpi as possible.
56 : */
57 :
58 : // This controls whether we're using integers or floats for coordinates. We
59 : // want to eventually use floats.
60 : //#define NS_COORD_IS_FLOAT
61 :
62 : inline float NS_IEEEPositiveInfinity() {
63 : union { PRUint32 mPRUint32; float mFloat; } pun;
64 : pun.mPRUint32 = 0x7F800000;
65 : return pun.mFloat;
66 : }
67 : inline bool NS_IEEEIsNan(float aF) {
68 : union { PRUint32 mBits; float mFloat; } pun;
69 : pun.mFloat = aF;
70 : return (pun.mBits & 0x7F800000) == 0x7F800000 &&
71 : (pun.mBits & 0x007FFFFF) != 0;
72 : }
73 :
74 : #ifdef NS_COORD_IS_FLOAT
75 : typedef float nscoord;
76 : #define nscoord_MAX NS_IEEEPositiveInfinity()
77 : #else
78 : typedef PRInt32 nscoord;
79 : #define nscoord_MAX nscoord(1 << 30)
80 : #endif
81 :
82 : #define nscoord_MIN (-nscoord_MAX)
83 :
84 0 : inline void VERIFY_COORD(nscoord aCoord) {
85 : #ifdef NS_COORD_IS_FLOAT
86 : NS_ASSERTION(floorf(aCoord) == aCoord,
87 : "Coords cannot have fractions");
88 : #endif
89 0 : }
90 :
91 0 : inline nscoord NSToCoordRound(float aValue)
92 : {
93 : #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
94 : return NS_lroundup30(aValue);
95 : #else
96 0 : return nscoord(floorf(aValue + 0.5f));
97 : #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
98 : }
99 :
100 0 : inline nscoord NSToCoordRound(double aValue)
101 : {
102 : #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
103 : return NS_lroundup30((float)aValue);
104 : #else
105 0 : return nscoord(floor(aValue + 0.5f));
106 : #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
107 : }
108 :
109 : inline nscoord NSToCoordRoundWithClamp(float aValue)
110 : {
111 : #ifndef NS_COORD_IS_FLOAT
112 : // Bounds-check before converting out of float, to avoid overflow
113 : NS_WARN_IF_FALSE(aValue <= nscoord_MAX,
114 : "Overflowed nscoord_MAX in conversion to nscoord");
115 : if (aValue >= nscoord_MAX) {
116 : return nscoord_MAX;
117 : }
118 : NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
119 : "Overflowed nscoord_MIN in conversion to nscoord");
120 : if (aValue <= nscoord_MIN) {
121 : return nscoord_MIN;
122 : }
123 : #endif
124 : return NSToCoordRound(aValue);
125 : }
126 :
127 : /**
128 : * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
129 : * appropriate for the signs of aCoord and aScale. If requireNotNegative is
130 : * true, this method will enforce that aScale is not negative; use that
131 : * parametrization to get a check of that fact in debug builds.
132 : */
133 : inline nscoord _nscoordSaturatingMultiply(nscoord aCoord, float aScale,
134 : bool requireNotNegative) {
135 : VERIFY_COORD(aCoord);
136 : if (requireNotNegative) {
137 : NS_ABORT_IF_FALSE(aScale >= 0.0f,
138 : "negative scaling factors must be handled manually");
139 : }
140 : #ifdef NS_COORD_IS_FLOAT
141 : return floorf(aCoord * aScale);
142 : #else
143 : // This one's only a warning because it may be possible to trigger it with
144 : // valid inputs.
145 : NS_WARN_IF_FALSE((requireNotNegative
146 : ? aCoord > 0
147 : : (aCoord > 0) == (aScale > 0))
148 : ? floorf(aCoord * aScale) < nscoord_MAX
149 : : ceilf(aCoord * aScale) > nscoord_MIN,
150 : "nscoord multiplication capped");
151 :
152 : float product = aCoord * aScale;
153 : if (requireNotNegative ? aCoord > 0 : (aCoord > 0) == (aScale > 0))
154 : return NSToCoordRoundWithClamp(NS_MIN<float>(nscoord_MAX, product));
155 : return NSToCoordRoundWithClamp(NS_MAX<float>(nscoord_MIN, product));
156 : #endif
157 : }
158 :
159 : /**
160 : * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
161 : * appropriate for the sign of aCoord. This method requires aScale to not be
162 : * negative; use this method when you know that aScale should never be
163 : * negative to get a sanity check of that invariant in debug builds.
164 : */
165 : inline nscoord NSCoordSaturatingNonnegativeMultiply(nscoord aCoord, float aScale) {
166 : return _nscoordSaturatingMultiply(aCoord, aScale, true);
167 : }
168 :
169 : /**
170 : * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
171 : * appropriate for the signs of aCoord and aScale.
172 : */
173 : inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aScale) {
174 : return _nscoordSaturatingMultiply(aCoord, aScale, false);
175 : }
176 :
177 : inline nscoord NSCoordMultiply(nscoord aCoord, PRInt32 aScale) {
178 : VERIFY_COORD(aCoord);
179 : return aCoord * aScale;
180 : }
181 :
182 : inline nscoord NSCoordDivide(nscoord aCoord, float aVal) {
183 : VERIFY_COORD(aCoord);
184 : #ifdef NS_COORD_IS_FLOAT
185 : return floorf(aCoord/aVal);
186 : #else
187 : return (PRInt32)(aCoord/aVal);
188 : #endif
189 : }
190 :
191 : inline nscoord NSCoordDivide(nscoord aCoord, PRInt32 aVal) {
192 : VERIFY_COORD(aCoord);
193 : #ifdef NS_COORD_IS_FLOAT
194 : return floorf(aCoord/aVal);
195 : #else
196 : return aCoord/aVal;
197 : #endif
198 : }
199 :
200 : /**
201 : * Returns a + b, capping the sum to nscoord_MAX.
202 : *
203 : * This function assumes that neither argument is nscoord_MIN.
204 : *
205 : * Note: If/when we start using floats for nscoords, this function won't be as
206 : * necessary. Normal float addition correctly handles adding with infinity,
207 : * assuming we aren't adding nscoord_MIN. (-infinity)
208 : */
209 : inline nscoord
210 : NSCoordSaturatingAdd(nscoord a, nscoord b)
211 : {
212 : VERIFY_COORD(a);
213 : VERIFY_COORD(b);
214 : NS_ASSERTION(a != nscoord_MIN && b != nscoord_MIN,
215 : "NSCoordSaturatingAdd got nscoord_MIN as argument");
216 :
217 : #ifdef NS_COORD_IS_FLOAT
218 : // Float math correctly handles a+b, given that neither is -infinity.
219 : return a + b;
220 : #else
221 : if (a == nscoord_MAX || b == nscoord_MAX) {
222 : // infinity + anything = anything + infinity = infinity
223 : return nscoord_MAX;
224 : } else {
225 : // a + b = a + b
226 : NS_ASSERTION(a < nscoord_MAX && b < nscoord_MAX,
227 : "Doing nscoord addition with values > nscoord_MAX");
228 : NS_ASSERTION((PRInt64)a + (PRInt64)b > (PRInt64)nscoord_MIN,
229 : "nscoord addition will reach or pass nscoord_MIN");
230 : // This one's only a warning because the NS_MIN below means that
231 : // we'll handle this case correctly.
232 : NS_WARN_IF_FALSE((PRInt64)a + (PRInt64)b < (PRInt64)nscoord_MAX,
233 : "nscoord addition capped to nscoord_MAX");
234 :
235 : // Cap the result, just in case we're dealing with numbers near nscoord_MAX
236 : return NS_MIN(nscoord_MAX, a + b);
237 : }
238 : #endif
239 : }
240 :
241 : /**
242 : * Returns a - b, gracefully handling cases involving nscoord_MAX.
243 : * This function assumes that neither argument is nscoord_MIN.
244 : *
245 : * The behavior is as follows:
246 : *
247 : * a) infinity - infinity -> infMinusInfResult
248 : * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED)
249 : * c) infinity - N -> infinity
250 : * d) N1 - N2 -> N1 - N2
251 : *
252 : * Note: For float nscoords, cases (c) and (d) are handled by normal float
253 : * math. We still need to explicitly specify the behavior for cases (a)
254 : * and (b), though. (Under normal float math, those cases would return NaN
255 : * and -infinity, respectively.)
256 : */
257 : inline nscoord
258 : NSCoordSaturatingSubtract(nscoord a, nscoord b,
259 : nscoord infMinusInfResult)
260 : {
261 : VERIFY_COORD(a);
262 : VERIFY_COORD(b);
263 : NS_ASSERTION(a != nscoord_MIN && b != nscoord_MIN,
264 : "NSCoordSaturatingSubtract got nscoord_MIN as argument");
265 :
266 : if (b == nscoord_MAX) {
267 : if (a == nscoord_MAX) {
268 : // case (a)
269 : return infMinusInfResult;
270 : } else {
271 : // case (b)
272 : NS_NOTREACHED("Attempted to subtract [n - nscoord_MAX]");
273 : return 0;
274 : }
275 : } else {
276 : #ifdef NS_COORD_IS_FLOAT
277 : // case (c) and (d) for floats. (float math handles both)
278 : return a - b;
279 : #else
280 : if (a == nscoord_MAX) {
281 : // case (c) for integers
282 : return nscoord_MAX;
283 : } else {
284 : // case (d) for integers
285 : NS_ASSERTION(a < nscoord_MAX && b < nscoord_MAX,
286 : "Doing nscoord subtraction with values > nscoord_MAX");
287 : NS_ASSERTION((PRInt64)a - (PRInt64)b > (PRInt64)nscoord_MIN,
288 : "nscoord subtraction will reach or pass nscoord_MIN");
289 : // This one's only a warning because the NS_MIN below means that
290 : // we'll handle this case correctly.
291 : NS_WARN_IF_FALSE((PRInt64)a - (PRInt64)b < (PRInt64)nscoord_MAX,
292 : "nscoord subtraction capped to nscoord_MAX");
293 :
294 : // Cap the result, in case we're dealing with numbers near nscoord_MAX
295 : return NS_MIN(nscoord_MAX, a - b);
296 : }
297 : }
298 : #endif
299 : }
300 : /** compare against a nscoord "b", which might be unconstrained
301 : * "a" must not be unconstrained.
302 : * Every number is smaller than a unconstrained one
303 : */
304 : inline bool
305 : NSCoordLessThan(nscoord a,nscoord b)
306 : {
307 : NS_ASSERTION(a != nscoord_MAX,
308 : "This coordinate should be constrained");
309 : return ((a < b) || (b == nscoord_MAX));
310 : }
311 :
312 : /** compare against a nscoord "b", which might be unconstrained
313 : * "a" must not be unconstrained
314 : * No number is larger than a unconstrained one.
315 : */
316 : inline bool
317 : NSCoordGreaterThan(nscoord a,nscoord b)
318 : {
319 : NS_ASSERTION(a != nscoord_MAX,
320 : "This coordinate should be constrained");
321 : return ((a > b) && (b != nscoord_MAX));
322 : }
323 :
324 : /**
325 : * Convert an nscoord to a PRInt32. This *does not* do rounding because
326 : * coords are never fractional. They can be out of range, so this does
327 : * clamp out of bounds coord values to PR_INT32_MIN and PR_INT32_MAX.
328 : */
329 : inline PRInt32 NSCoordToInt(nscoord aCoord) {
330 : VERIFY_COORD(aCoord);
331 : #ifdef NS_COORD_IS_FLOAT
332 : NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in int conversion");
333 : if (aCoord < -2147483648.0f) {
334 : // -2147483648 is the smallest 32-bit signed integer that can be
335 : // exactly represented as a float
336 : return PR_INT32_MIN;
337 : } else if (aCoord > 2147483520.0f) {
338 : // 2147483520 is the largest 32-bit signed integer that can be
339 : // exactly represented as an IEEE float
340 : return PR_INT32_MAX;
341 : } else {
342 : return (PRInt32)aCoord;
343 : }
344 : #else
345 : return aCoord;
346 : #endif
347 : }
348 :
349 0 : inline float NSCoordToFloat(nscoord aCoord) {
350 0 : VERIFY_COORD(aCoord);
351 : #ifdef NS_COORD_IS_FLOAT
352 : NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in float conversion");
353 : #endif
354 0 : return (float)aCoord;
355 : }
356 :
357 : /*
358 : * Coord Rounding Functions
359 : */
360 0 : inline nscoord NSToCoordFloor(float aValue)
361 : {
362 0 : return nscoord(floorf(aValue));
363 : }
364 :
365 0 : inline nscoord NSToCoordFloor(double aValue)
366 : {
367 0 : return nscoord(floor(aValue));
368 : }
369 :
370 : inline nscoord NSToCoordFloorClamped(float aValue)
371 : {
372 : #ifndef NS_COORD_IS_FLOAT
373 : // Bounds-check before converting out of float, to avoid overflow
374 : NS_WARN_IF_FALSE(aValue <= nscoord_MAX,
375 : "Overflowed nscoord_MAX in conversion to nscoord");
376 : if (aValue >= nscoord_MAX) {
377 : return nscoord_MAX;
378 : }
379 : NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
380 : "Overflowed nscoord_MIN in conversion to nscoord");
381 : if (aValue <= nscoord_MIN) {
382 : return nscoord_MIN;
383 : }
384 : #endif
385 : return NSToCoordFloor(aValue);
386 : }
387 :
388 0 : inline nscoord NSToCoordCeil(float aValue)
389 : {
390 0 : return nscoord(ceilf(aValue));
391 : }
392 :
393 0 : inline nscoord NSToCoordCeil(double aValue)
394 : {
395 0 : return nscoord(ceil(aValue));
396 : }
397 :
398 : inline nscoord NSToCoordCeilClamped(float aValue)
399 : {
400 : #ifndef NS_COORD_IS_FLOAT
401 : // Bounds-check before converting out of float, to avoid overflow
402 : NS_WARN_IF_FALSE(aValue <= nscoord_MAX,
403 : "Overflowed nscoord_MAX in conversion to nscoord");
404 : if (aValue >= nscoord_MAX) {
405 : return nscoord_MAX;
406 : }
407 : NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
408 : "Overflowed nscoord_MIN in conversion to nscoord");
409 : if (aValue <= nscoord_MIN) {
410 : return nscoord_MIN;
411 : }
412 : #endif
413 : return NSToCoordCeil(aValue);
414 : }
415 :
416 : inline nscoord NSToCoordCeilClamped(double aValue)
417 : {
418 : #ifndef NS_COORD_IS_FLOAT
419 : // Bounds-check before converting out of double, to avoid overflow
420 : NS_WARN_IF_FALSE(aValue <= nscoord_MAX,
421 : "Overflowed nscoord_MAX in conversion to nscoord");
422 : if (aValue >= nscoord_MAX) {
423 : return nscoord_MAX;
424 : }
425 : NS_WARN_IF_FALSE(aValue >= nscoord_MIN,
426 : "Overflowed nscoord_MIN in conversion to nscoord");
427 : if (aValue <= nscoord_MIN) {
428 : return nscoord_MIN;
429 : }
430 : #endif
431 : return NSToCoordCeil(aValue);
432 : }
433 :
434 : /*
435 : * Int Rounding Functions
436 : */
437 0 : inline PRInt32 NSToIntFloor(float aValue)
438 : {
439 0 : return PRInt32(floorf(aValue));
440 : }
441 :
442 0 : inline PRInt32 NSToIntCeil(float aValue)
443 : {
444 0 : return PRInt32(ceilf(aValue));
445 : }
446 :
447 : inline PRInt32 NSToIntRound(float aValue)
448 : {
449 : return NS_lroundf(aValue);
450 : }
451 :
452 : inline PRInt32 NSToIntRound(double aValue)
453 : {
454 : return NS_lround(aValue);
455 : }
456 :
457 : inline PRInt32 NSToIntRoundUp(float aValue)
458 : {
459 : return PRInt32(floorf(aValue + 0.5f));
460 : }
461 :
462 0 : inline PRInt32 NSToIntRoundUp(double aValue)
463 : {
464 0 : return PRInt32(floor(aValue + 0.5));
465 : }
466 :
467 : /*
468 : * App Unit/Pixel conversions
469 : */
470 : inline nscoord NSFloatPixelsToAppUnits(float aPixels, float aAppUnitsPerPixel)
471 : {
472 : return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel);
473 : }
474 :
475 0 : inline nscoord NSIntPixelsToAppUnits(PRInt32 aPixels, PRInt32 aAppUnitsPerPixel)
476 : {
477 : // The cast to nscoord makes sure we don't overflow if we ever change
478 : // nscoord to float
479 0 : nscoord r = aPixels * (nscoord)aAppUnitsPerPixel;
480 0 : VERIFY_COORD(r);
481 0 : return r;
482 : }
483 :
484 0 : inline float NSAppUnitsToFloatPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
485 : {
486 0 : return (float(aAppUnits) / aAppUnitsPerPixel);
487 : }
488 :
489 0 : inline double NSAppUnitsToDoublePixels(nscoord aAppUnits, nscoord aAppUnitsPerPixel)
490 : {
491 0 : return (double(aAppUnits) / double(aAppUnitsPerPixel));
492 : }
493 :
494 : inline double NSAppUnitsToDoublePixels(nscoord aAppUnits, double aAppUnitsPerPixel)
495 : {
496 : return (double(aAppUnits) / aAppUnitsPerPixel);
497 : }
498 :
499 : inline PRInt32 NSAppUnitsToIntPixels(nscoord aAppUnits, float aAppUnitsPerPixel)
500 : {
501 : return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel);
502 : }
503 :
504 0 : inline float NSCoordScale(nscoord aCoord, PRInt32 aFromAPP, PRInt32 aToAPP)
505 : {
506 0 : return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP;
507 : }
508 :
509 : /// handy constants
510 : #define TWIPS_PER_POINT_INT 20
511 : #define TWIPS_PER_POINT_FLOAT 20.0f
512 : #define POINTS_PER_INCH_INT 72
513 : #define POINTS_PER_INCH_FLOAT 72.0f
514 : #define CM_PER_INCH_FLOAT 2.54f
515 : #define MM_PER_INCH_FLOAT 25.4f
516 :
517 : /*
518 : * Twips/unit conversions
519 : */
520 : inline float NSUnitsToTwips(float aValue, float aPointsPerUnit)
521 : {
522 : return aValue * aPointsPerUnit * TWIPS_PER_POINT_FLOAT;
523 : }
524 :
525 : inline float NSTwipsToUnits(float aTwips, float aUnitsPerPoint)
526 : {
527 : return (aTwips * (aUnitsPerPoint / TWIPS_PER_POINT_FLOAT));
528 : }
529 :
530 : /// Unit conversion macros
531 : //@{
532 : #define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f)
533 : #define NS_INCHES_TO_TWIPS(x) NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch
534 :
535 : #define NS_MILLIMETERS_TO_TWIPS(x) NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f))
536 : #define NS_CENTIMETERS_TO_TWIPS(x) NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.3937f))
537 :
538 : #define NS_PICAS_TO_TWIPS(x) NSUnitsToTwips((x), 12.0f) // 12 points per pica
539 :
540 : #define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x))
541 : #define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x))
542 :
543 : #define NS_TWIPS_TO_POINTS(x) NSTwipsToUnits((x), 1.0f)
544 : #define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT)
545 :
546 : #define NS_TWIPS_TO_MILLIMETERS(x) NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f))
547 : #define NS_TWIPS_TO_CENTIMETERS(x) NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.3937f))
548 :
549 : #define NS_TWIPS_TO_PICAS(x) NSTwipsToUnits((x), 1.0f / 12.0f)
550 : //@}
551 :
552 : #endif /* NSCOORD_H */
|