tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */