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/. */
15 #include "mozilla/Assertions.h"
16 #include "mozilla/gfx/Coord.h"
17 #include "nsMathUtils.h"
20 * Basic type used for the geometry classes.
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
28 using nscoord
= int32_t;
29 inline constexpr nscoord nscoord_MAX
= (1 << 30) - 1;
30 inline constexpr nscoord nscoord_MIN
= -nscoord_MAX
;
35 // Declare AppUnit as a coordinate system tag.
37 struct IsPixel
<AppUnit
> : std::true_type
{};
40 template <typename Rep
>
41 struct AuCoordImpl
: public gfx::IntCoordTyped
<AppUnit
, Rep
> {
42 using Super
= gfx::IntCoordTyped
<AppUnit
, Rep
>;
44 constexpr AuCoordImpl() : Super() {}
45 constexpr MOZ_IMPLICIT
AuCoordImpl(Rep aValue
) : Super(aValue
) {}
46 constexpr MOZ_IMPLICIT
AuCoordImpl(Super aValue
) : Super(aValue
) {}
49 static AuCoordImpl
FromRound(F aValue
) {
50 // Note: aValue is *not* rounding to nearest integer if it is negative. See
51 // https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
52 return AuCoordImpl(std::floor(aValue
+ 0.5f
));
56 static AuCoordImpl
FromTruncate(F aValue
) {
57 return AuCoordImpl(std::trunc(aValue
));
61 static AuCoordImpl
FromCeil(F aValue
) {
62 return AuCoordImpl(std::ceil(aValue
));
66 static AuCoordImpl
FromFloor(F aValue
) {
67 return AuCoordImpl(std::floor(aValue
));
70 // Note: this returns the result of the operation, without modifying the
72 [[nodiscard
]] AuCoordImpl
ToMinMaxClamped() const {
73 return std::clamp(this->value
, kMin
, kMax
);
76 static constexpr Rep kMax
= nscoord_MAX
;
77 static constexpr Rep kMin
= nscoord_MIN
;
81 using AuCoord
= detail::AuCoordImpl
<int32_t>;
82 using AuCoord64
= detail::AuCoordImpl
<int64_t>;
84 } // namespace mozilla
87 * Divide aSpace by aN. Assign the resulting quotient to aQuotient and
88 * return the remainder.
90 inline nscoord
NSCoordDivRem(nscoord aSpace
, size_t aN
, nscoord
* aQuotient
) {
91 div_t result
= div(aSpace
, aN
);
92 *aQuotient
= nscoord(result
.quot
);
93 return nscoord(result
.rem
);
96 inline nscoord
NSCoordMulDiv(nscoord aMult1
, nscoord aMult2
, nscoord aDiv
) {
97 return (int64_t(aMult1
) * int64_t(aMult2
) / int64_t(aDiv
));
100 inline nscoord
NSToCoordRound(float aValue
) {
101 #if defined(XP_WIN) && defined(_M_IX86) && !defined(__GNUC__) && \
103 return NS_lroundup30(aValue
);
105 return nscoord(floorf(aValue
+ 0.5f
));
106 #endif /* XP_WIN && _M_IX86 && !__GNUC__ */
109 inline nscoord
NSToCoordRound(double aValue
) {
110 #if defined(XP_WIN) && defined(_M_IX86) && !defined(__GNUC__) && \
112 return NS_lroundup30((float)aValue
);
114 return nscoord(floor(aValue
+ 0.5f
));
115 #endif /* XP_WIN && _M_IX86 && !__GNUC__ */
118 inline nscoord
NSToCoordRoundWithClamp(float aValue
) {
119 // Bounds-check before converting out of float, to avoid overflow
120 if (aValue
>= float(nscoord_MAX
)) {
123 if (aValue
<= float(nscoord_MIN
)) {
126 return NSToCoordRound(aValue
);
129 inline nscoord
NSToCoordRoundWithClamp(double aValue
) {
130 // Bounds-check before converting out of double, to avoid overflow
131 if (aValue
>= double(nscoord_MAX
)) {
134 if (aValue
<= double(nscoord_MIN
)) {
137 return NSToCoordRound(aValue
);
141 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
142 * appropriate for the signs of aCoord and aScale. If requireNotNegative is
143 * true, this method will enforce that aScale is not negative; use that
144 * parametrization to get a check of that fact in debug builds.
146 inline nscoord
_nscoordSaturatingMultiply(nscoord aCoord
, float aScale
,
147 bool requireNotNegative
) {
148 if (requireNotNegative
) {
149 MOZ_ASSERT(aScale
>= 0.0f
,
150 "negative scaling factors must be handled manually");
152 float product
= aCoord
* aScale
;
153 if (requireNotNegative
? aCoord
> 0 : (aCoord
> 0) == (aScale
> 0))
154 return NSToCoordRoundWithClamp(
155 std::min
<float>((float)nscoord_MAX
, product
));
156 return NSToCoordRoundWithClamp(std::max
<float>((float)nscoord_MIN
, product
));
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.
165 inline nscoord
NSCoordSaturatingNonnegativeMultiply(nscoord aCoord
,
167 return _nscoordSaturatingMultiply(aCoord
, aScale
, true);
171 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as
172 * appropriate for the signs of aCoord and aScale.
174 inline nscoord
NSCoordSaturatingMultiply(nscoord aCoord
, float aScale
) {
175 return _nscoordSaturatingMultiply(aCoord
, aScale
, false);
179 * Returns a + b, capping the sum to nscoord_MAX.
181 * This function assumes that neither argument is nscoord_MIN.
183 inline nscoord
NSCoordSaturatingAdd(nscoord a
, nscoord b
) {
184 if (a
== nscoord_MAX
|| b
== nscoord_MAX
) {
185 // infinity + anything = anything + infinity = infinity
189 // Cap the result, just in case we're dealing with numbers near nscoord_MAX
190 return std::min(nscoord_MAX
, a
+ b
);
195 * Returns a - b, gracefully handling cases involving nscoord_MAX.
196 * This function assumes that neither argument is nscoord_MIN.
198 * The behavior is as follows:
200 * a) infinity - infinity -> infMinusInfResult
201 * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED)
202 * c) infinity - N -> infinity
203 * d) N1 - N2 -> N1 - N2
205 inline nscoord
NSCoordSaturatingSubtract(nscoord a
, nscoord b
,
206 nscoord infMinusInfResult
) {
207 if (b
== nscoord_MAX
) {
208 if (a
== nscoord_MAX
) {
210 return infMinusInfResult
;
216 if (a
== nscoord_MAX
) {
217 // case (c) for integers
220 // case (d) for integers
221 // Cap the result, in case we're dealing with numbers near nscoord_MAX
222 return std::min(nscoord_MAX
, a
- b
);
227 inline float NSCoordToFloat(nscoord aCoord
) { return (float)aCoord
; }
230 * Coord Rounding Functions
232 inline nscoord
NSToCoordFloor(float aValue
) { return nscoord(floorf(aValue
)); }
234 inline nscoord
NSToCoordFloor(double aValue
) { return nscoord(floor(aValue
)); }
236 inline nscoord
NSToCoordFloorClamped(float aValue
) {
237 // Bounds-check before converting out of float, to avoid overflow
238 if (aValue
>= float(nscoord_MAX
)) {
241 if (aValue
<= float(nscoord_MIN
)) {
244 return NSToCoordFloor(aValue
);
247 inline nscoord
NSToCoordCeil(float aValue
) { return nscoord(ceilf(aValue
)); }
249 inline nscoord
NSToCoordCeil(double aValue
) { return nscoord(ceil(aValue
)); }
251 inline nscoord
NSToCoordCeilClamped(double aValue
) {
252 // Bounds-check before converting out of double, to avoid overflow
253 if (aValue
>= nscoord_MAX
) {
256 if (aValue
<= nscoord_MIN
) {
259 return NSToCoordCeil(aValue
);
262 // The NSToCoordTrunc* functions remove the fractional component of
263 // aValue, and are thus equivalent to NSToCoordFloor* for positive
264 // values and NSToCoordCeil* for negative values.
266 inline nscoord
NSToCoordTrunc(float aValue
) {
267 // There's no need to use truncf() since it matches the default
268 // rules for float to integer conversion.
269 return nscoord(aValue
);
272 inline nscoord
NSToCoordTrunc(double aValue
) {
273 // There's no need to use trunc() since it matches the default
274 // rules for float to integer conversion.
275 return nscoord(aValue
);
278 inline nscoord
NSToCoordTruncClamped(float aValue
) {
279 // Bounds-check before converting out of float, to avoid overflow
280 if (aValue
>= float(nscoord_MAX
)) {
283 if (aValue
<= float(nscoord_MIN
)) {
286 return NSToCoordTrunc(aValue
);
289 inline nscoord
NSToCoordTruncClamped(double aValue
) {
290 // Bounds-check before converting out of double, to avoid overflow
291 if (aValue
>= float(nscoord_MAX
)) {
294 if (aValue
<= float(nscoord_MIN
)) {
297 return NSToCoordTrunc(aValue
);
301 * Int Rounding Functions
303 inline int32_t NSToIntFloor(float aValue
) { return int32_t(floorf(aValue
)); }
305 inline int32_t NSToIntCeil(float aValue
) { return int32_t(ceilf(aValue
)); }
307 inline int32_t NSToIntRound(float aValue
) { return NS_lroundf(aValue
); }
309 inline int32_t NSToIntRound(double aValue
) { return NS_lround(aValue
); }
311 inline int32_t NSToIntRoundUp(double aValue
) {
312 return int32_t(floor(aValue
+ 0.5));
316 * App Unit/Pixel conversions
318 inline nscoord
NSFloatPixelsToAppUnits(float aPixels
, float aAppUnitsPerPixel
) {
319 return NSToCoordRoundWithClamp(aPixels
* aAppUnitsPerPixel
);
322 inline nscoord
NSIntPixelsToAppUnits(int32_t aPixels
,
323 int32_t aAppUnitsPerPixel
) {
324 // The cast to nscoord makes sure we don't overflow if we ever change
326 nscoord r
= aPixels
* (nscoord
)aAppUnitsPerPixel
;
330 inline float NSAppUnitsToFloatPixels(nscoord aAppUnits
,
331 float aAppUnitsPerPixel
) {
332 return (float(aAppUnits
) / aAppUnitsPerPixel
);
335 inline double NSAppUnitsToDoublePixels(nscoord aAppUnits
,
336 double aAppUnitsPerPixel
) {
337 return (double(aAppUnits
) / aAppUnitsPerPixel
);
340 inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits
,
341 float aAppUnitsPerPixel
) {
342 return NSToIntRound(float(aAppUnits
) / aAppUnitsPerPixel
);
345 inline float NSCoordScale(nscoord aCoord
, int32_t aFromAPP
, int32_t aToAPP
) {
346 return (NSCoordToFloat(aCoord
) * aToAPP
) / aFromAPP
;
350 #define TWIPS_PER_POINT_INT 20
351 #define TWIPS_PER_POINT_FLOAT 20.0f
352 #define POINTS_PER_INCH_INT 72
353 #define POINTS_PER_INCH_FLOAT 72.0f
354 #define CM_PER_INCH_FLOAT 2.54f
355 #define MM_PER_INCH_FLOAT 25.4f
358 * Twips/unit conversions
360 inline float NSUnitsToTwips(float aValue
, float aPointsPerUnit
) {
361 return aValue
* aPointsPerUnit
* TWIPS_PER_POINT_FLOAT
;
364 inline float NSTwipsToUnits(float aTwips
, float aUnitsPerPoint
) {
365 return (aTwips
* (aUnitsPerPoint
/ TWIPS_PER_POINT_FLOAT
));
368 /// Unit conversion macros
370 #define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f)
371 #define NS_INCHES_TO_TWIPS(x) \
372 NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch
374 #define NS_MILLIMETERS_TO_TWIPS(x) \
375 NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f))
377 #define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x))
378 #define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x))
380 #define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT)
382 #define NS_TWIPS_TO_MILLIMETERS(x) \
383 NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f))
386 #endif /* NSCOORD_H */