nsCoord.h (11746B)
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 <algorithm> 11 #include <cstdint> 12 #include <cstdlib> 13 #include <math.h> 14 15 #include "mozilla/Assertions.h" 16 #include "mozilla/gfx/Coord.h" 17 #include "nsMathUtils.h" 18 19 /* 20 * Basic type used for the geometry classes. 21 * 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 25 * 96dpi as possible. 26 */ 27 28 using nscoord = int32_t; 29 inline constexpr nscoord nscoord_MAX = (1 << 30) - 1; 30 inline constexpr nscoord nscoord_MIN = -nscoord_MAX; 31 32 namespace mozilla { 33 struct AppUnit {}; 34 35 // Declare AppUnit as a coordinate system tag. 36 template <> 37 struct IsPixel<AppUnit> : std::true_type {}; 38 39 namespace detail { 40 template <typename Rep> 41 struct AuCoordImpl : public gfx::IntCoordTyped<AppUnit, Rep> { 42 using Super = gfx::IntCoordTyped<AppUnit, Rep>; 43 44 constexpr AuCoordImpl() : Super() {} 45 constexpr MOZ_IMPLICIT AuCoordImpl(Rep aValue) : Super(aValue) {} 46 constexpr MOZ_IMPLICIT AuCoordImpl(Super aValue) : Super(aValue) {} 47 48 template <typename F> 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)); 53 } 54 55 template <typename F> 56 static AuCoordImpl FromTruncate(F aValue) { 57 return AuCoordImpl(std::trunc(aValue)); 58 } 59 60 template <typename F> 61 static AuCoordImpl FromCeil(F aValue) { 62 return AuCoordImpl(std::ceil(aValue)); 63 } 64 65 template <typename F> 66 static AuCoordImpl FromFloor(F aValue) { 67 return AuCoordImpl(std::floor(aValue)); 68 } 69 70 // Note: this returns the result of the operation, without modifying the 71 // original value. 72 [[nodiscard]] AuCoordImpl ToMinMaxClamped() const { 73 return std::clamp(this->value, kMin, kMax); 74 } 75 76 static constexpr Rep kMax = nscoord_MAX; 77 static constexpr Rep kMin = nscoord_MIN; 78 }; 79 } // namespace detail 80 81 using AuCoord = detail::AuCoordImpl<int32_t>; 82 using AuCoord64 = detail::AuCoordImpl<int64_t>; 83 84 } // namespace mozilla 85 86 /** 87 * Divide aSpace by aN. Assign the resulting quotient to aQuotient and 88 * return the remainder. 89 */ 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); 94 } 95 96 inline nscoord NSCoordMulDiv(nscoord aMult1, nscoord aMult2, nscoord aDiv) { 97 return int64_t(aMult1) * int64_t(aMult2) / int64_t(aDiv); 98 } 99 100 inline nscoord NSToCoordRound(float aValue) { 101 return nscoord(floorf(aValue + 0.5f)); 102 } 103 104 inline nscoord NSToCoordRound(double aValue) { 105 return nscoord(floor(aValue + 0.5f)); 106 } 107 108 inline nscoord NSToCoordRoundWithClamp(float aValue) { 109 // Bounds-check before converting out of float, to avoid overflow 110 if (aValue >= float(nscoord_MAX)) { 111 return nscoord_MAX; 112 } 113 if (aValue <= float(nscoord_MIN)) { 114 return nscoord_MIN; 115 } 116 return NSToCoordRound(aValue); 117 } 118 119 inline nscoord NSToCoordRoundWithClamp(double aValue) { 120 // Bounds-check before converting out of double, to avoid overflow 121 if (aValue >= double(nscoord_MAX)) { 122 return nscoord_MAX; 123 } 124 if (aValue <= double(nscoord_MIN)) { 125 return nscoord_MIN; 126 } 127 return NSToCoordRound(aValue); 128 } 129 130 /** 131 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as 132 * appropriate for the signs of aCoord and aScale. If requireNotNegative is 133 * true, this method will enforce that aScale is not negative; use that 134 * parametrization to get a check of that fact in debug builds. 135 */ 136 inline nscoord _nscoordSaturatingMultiply(nscoord aCoord, float aScale, 137 bool requireNotNegative) { 138 if (requireNotNegative) { 139 MOZ_ASSERT(aScale >= 0.0f, 140 "negative scaling factors must be handled manually"); 141 } 142 float product = aCoord * aScale; 143 if (requireNotNegative ? aCoord > 0 : (aCoord > 0) == (aScale > 0)) 144 return NSToCoordRoundWithClamp( 145 std::min<float>((float)nscoord_MAX, product)); 146 return NSToCoordRoundWithClamp(std::max<float>((float)nscoord_MIN, product)); 147 } 148 149 /** 150 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as 151 * appropriate for the sign of aCoord. This method requires aScale to not be 152 * negative; use this method when you know that aScale should never be 153 * negative to get a sanity check of that invariant in debug builds. 154 */ 155 inline nscoord NSCoordSaturatingNonnegativeMultiply(nscoord aCoord, 156 float aScale) { 157 return _nscoordSaturatingMultiply(aCoord, aScale, true); 158 } 159 160 /** 161 * Returns aCoord * aScale, capping the product to nscoord_MAX or nscoord_MIN as 162 * appropriate for the signs of aCoord and aScale. 163 */ 164 inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aScale) { 165 return _nscoordSaturatingMultiply(aCoord, aScale, false); 166 } 167 168 /** 169 * Returns a + b, capping the sum to nscoord_MAX. 170 * 171 * This function assumes that neither argument is nscoord_MIN. 172 */ 173 inline nscoord NSCoordSaturatingAdd(nscoord a, nscoord b) { 174 if (a == nscoord_MAX || b == nscoord_MAX) { 175 // infinity + anything = anything + infinity = infinity 176 return nscoord_MAX; 177 } else { 178 // a + b = a + b 179 // Cap the result, just in case we're dealing with numbers near nscoord_MAX 180 return std::min(nscoord_MAX, a + b); 181 } 182 } 183 184 /** 185 * Returns a - b, gracefully handling cases involving nscoord_MAX. 186 * This function assumes that neither argument is nscoord_MIN. 187 * 188 * The behavior is as follows: 189 * 190 * a) infinity - infinity -> infMinusInfResult 191 * b) N - infinity -> 0 (unexpected -- triggers NOTREACHED) 192 * c) infinity - N -> infinity 193 * d) N1 - N2 -> N1 - N2 194 */ 195 inline nscoord NSCoordSaturatingSubtract(nscoord a, nscoord b, 196 nscoord infMinusInfResult) { 197 if (b == nscoord_MAX) { 198 if (a == nscoord_MAX) { 199 // case (a) 200 return infMinusInfResult; 201 } else { 202 // case (b) 203 return 0; 204 } 205 } else { 206 if (a == nscoord_MAX) { 207 // case (c) for integers 208 return nscoord_MAX; 209 } else { 210 // case (d) for integers 211 // Cap the result, in case we're dealing with numbers near nscoord_MAX 212 return std::min(nscoord_MAX, a - b); 213 } 214 } 215 } 216 217 inline float NSCoordToFloat(nscoord aCoord) { return (float)aCoord; } 218 219 /* 220 * Coord Rounding Functions 221 */ 222 inline nscoord NSToCoordFloor(float aValue) { return nscoord(floorf(aValue)); } 223 224 inline nscoord NSToCoordFloor(double aValue) { return nscoord(floor(aValue)); } 225 226 inline nscoord NSToCoordFloorClamped(float aValue) { 227 // Bounds-check before converting out of float, to avoid overflow 228 if (aValue >= float(nscoord_MAX)) { 229 return nscoord_MAX; 230 } 231 if (aValue <= float(nscoord_MIN)) { 232 return nscoord_MIN; 233 } 234 return NSToCoordFloor(aValue); 235 } 236 237 inline nscoord NSToCoordCeil(float aValue) { return nscoord(ceilf(aValue)); } 238 239 inline nscoord NSToCoordCeil(double aValue) { return nscoord(ceil(aValue)); } 240 241 inline nscoord NSToCoordCeilClamped(double aValue) { 242 // Bounds-check before converting out of double, to avoid overflow 243 if (aValue >= nscoord_MAX) { 244 return nscoord_MAX; 245 } 246 if (aValue <= nscoord_MIN) { 247 return nscoord_MIN; 248 } 249 return NSToCoordCeil(aValue); 250 } 251 252 // The NSToCoordTrunc* functions remove the fractional component of 253 // aValue, and are thus equivalent to NSToCoordFloor* for positive 254 // values and NSToCoordCeil* for negative values. 255 256 inline nscoord NSToCoordTrunc(float aValue) { 257 // There's no need to use truncf() since it matches the default 258 // rules for float to integer conversion. 259 return nscoord(aValue); 260 } 261 262 inline nscoord NSToCoordTrunc(double aValue) { 263 // There's no need to use trunc() since it matches the default 264 // rules for float to integer conversion. 265 return nscoord(aValue); 266 } 267 268 inline nscoord NSToCoordTruncClamped(float aValue) { 269 // Bounds-check before converting out of float, to avoid overflow 270 if (aValue >= float(nscoord_MAX)) { 271 return nscoord_MAX; 272 } 273 if (aValue <= float(nscoord_MIN)) { 274 return nscoord_MIN; 275 } 276 return NSToCoordTrunc(aValue); 277 } 278 279 inline nscoord NSToCoordTruncClamped(double aValue) { 280 // Bounds-check before converting out of double, to avoid overflow 281 if (aValue >= float(nscoord_MAX)) { 282 return nscoord_MAX; 283 } 284 if (aValue <= float(nscoord_MIN)) { 285 return nscoord_MIN; 286 } 287 return NSToCoordTrunc(aValue); 288 } 289 290 /* 291 * Int Rounding Functions 292 */ 293 inline int32_t NSToIntFloor(float aValue) { return int32_t(floorf(aValue)); } 294 295 inline int32_t NSToIntCeil(float aValue) { return int32_t(ceilf(aValue)); } 296 297 inline int32_t NSToIntRound(float aValue) { return NS_lroundf(aValue); } 298 299 inline int32_t NSToIntRound(double aValue) { return NS_lround(aValue); } 300 301 inline int32_t NSToIntRoundUp(double aValue) { 302 return int32_t(floor(aValue + 0.5)); 303 } 304 305 /* 306 * App Unit/Pixel conversions 307 */ 308 inline nscoord NSFloatPixelsToAppUnits(float aPixels, float aAppUnitsPerPixel) { 309 return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel); 310 } 311 312 inline nscoord NSDoublePixelsToAppUnits(double aPixels, 313 double aAppUnitsPerPixel) { 314 return NSToCoordRoundWithClamp(aPixels * aAppUnitsPerPixel); 315 } 316 317 inline nscoord NSIntPixelsToAppUnits(int32_t aPixels, 318 int32_t aAppUnitsPerPixel) { 319 // The cast to nscoord makes sure we don't overflow if we ever change 320 // nscoord to float 321 nscoord r = aPixels * (nscoord)aAppUnitsPerPixel; 322 return r; 323 } 324 325 inline float NSAppUnitsToFloatPixels(nscoord aAppUnits, 326 float aAppUnitsPerPixel) { 327 return float(aAppUnits) / aAppUnitsPerPixel; 328 } 329 330 inline double NSAppUnitsToDoublePixels(nscoord aAppUnits, 331 double aAppUnitsPerPixel) { 332 return double(aAppUnits) / aAppUnitsPerPixel; 333 } 334 335 inline int32_t NSAppUnitsToIntPixels(nscoord aAppUnits, 336 float aAppUnitsPerPixel) { 337 return NSToIntRound(float(aAppUnits) / aAppUnitsPerPixel); 338 } 339 340 inline float NSCoordScale(nscoord aCoord, int32_t aFromAPP, int32_t aToAPP) { 341 return (NSCoordToFloat(aCoord) * aToAPP) / aFromAPP; 342 } 343 344 /// handy constants 345 #define TWIPS_PER_POINT_INT 20 346 #define TWIPS_PER_POINT_FLOAT 20.0f 347 #define POINTS_PER_INCH_INT 72 348 #define POINTS_PER_INCH_FLOAT 72.0f 349 #define CM_PER_INCH_FLOAT 2.54f 350 #define MM_PER_INCH_FLOAT 25.4f 351 352 /* 353 * Twips/unit conversions 354 */ 355 inline float NSUnitsToTwips(float aValue, float aPointsPerUnit) { 356 return aValue * aPointsPerUnit * TWIPS_PER_POINT_FLOAT; 357 } 358 359 inline float NSTwipsToUnits(float aTwips, float aUnitsPerPoint) { 360 return aTwips * (aUnitsPerPoint / TWIPS_PER_POINT_FLOAT); 361 } 362 363 /// Unit conversion macros 364 //@{ 365 #define NS_POINTS_TO_TWIPS(x) NSUnitsToTwips((x), 1.0f) 366 #define NS_INCHES_TO_TWIPS(x) \ 367 NSUnitsToTwips((x), POINTS_PER_INCH_FLOAT) // 72 points per inch 368 369 #define NS_MILLIMETERS_TO_TWIPS(x) \ 370 NSUnitsToTwips((x), (POINTS_PER_INCH_FLOAT * 0.03937f)) 371 372 #define NS_POINTS_TO_INT_TWIPS(x) NSToIntRound(NS_POINTS_TO_TWIPS(x)) 373 #define NS_INCHES_TO_INT_TWIPS(x) NSToIntRound(NS_INCHES_TO_TWIPS(x)) 374 375 #define NS_TWIPS_TO_INCHES(x) NSTwipsToUnits((x), 1.0f / POINTS_PER_INCH_FLOAT) 376 377 #define NS_TWIPS_TO_MILLIMETERS(x) \ 378 NSTwipsToUnits((x), 1.0f / (POINTS_PER_INCH_FLOAT * 0.03937f)) 379 //@} 380 381 #endif /* NSCOORD_H */