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 MOZILLA_GFX_COORD_H_ 8 #define MOZILLA_GFX_COORD_H_ 9 10 #include "mozilla/Attributes.h" 11 #include "Types.h" 12 #include "BaseCoord.h" 13 14 #include <cmath> 15 #include <type_traits> 16 17 namespace mozilla { 18 19 template <typename> 20 struct IsPixel; 21 22 namespace gfx { 23 24 template <class units> 25 struct IntCoordTyped; 26 template <class units, class F = Float> 27 struct CoordTyped; 28 29 // CommonType<coord, primitive> is a metafunction that returns the type of the 30 // result of an arithmetic operation on the underlying type of a strongly-typed 31 // coordinate type 'coord', and a primitive type 'primitive'. C++ rules for 32 // arithmetic conversions are designed to avoid losing information - for 33 // example, the result of adding an int and a float is a float - and we want 34 // the same behaviour when mixing our coordinate types with primitive types. 35 // We get C++ to compute the desired result type using 'decltype'. 36 37 template <class coord, class primitive> 38 struct CommonType; 39 40 template <class units, class primitive> 41 struct CommonType<IntCoordTyped<units>, primitive> { 42 using type = decltype(int32_t() + primitive()); 43 }; 44 45 template <class units, class F, class primitive> 46 struct CommonType<CoordTyped<units, F>, primitive> { 47 using type = decltype(F() + primitive()); 48 }; 49 50 // This is a base class that provides mixed-type operator overloads between 51 // a strongly-typed Coord and a primitive value. It is needed to avoid 52 // ambiguities at mixed-type call sites, because Coord classes are implicitly 53 // convertible to their underlying value type. As we transition more of our code 54 // to strongly-typed classes, we may be able to remove some or all of these 55 // overloads. 56 57 template <bool B, class coord, class primitive> 58 struct CoordOperatorsHelper { 59 // Using SFINAE (Substitution Failure Is Not An Error) to suppress redundant 60 // operators 61 }; 62 63 template <class coord, class primitive> 64 struct CoordOperatorsHelper<true, coord, primitive> { 65 friend bool operator==(coord aA, primitive aB) { return aA.value == aB; } 66 friend bool operator==(primitive aA, coord aB) { return aA == aB.value; } 67 friend bool operator!=(coord aA, primitive aB) { return aA.value != aB; } 68 friend bool operator!=(primitive aA, coord aB) { return aA != aB.value; } 69 70 using result_type = typename CommonType<coord, primitive>::type; 71 72 friend result_type operator+(coord aA, primitive aB) { return aA.value + aB; } 73 friend result_type operator+(primitive aA, coord aB) { return aA + aB.value; } 74 friend result_type operator-(coord aA, primitive aB) { return aA.value - aB; } 75 friend result_type operator-(primitive aA, coord aB) { return aA - aB.value; } 76 friend result_type operator*(coord aCoord, primitive aScale) { 77 return aCoord.value * aScale; 78 } 79 friend result_type operator*(primitive aScale, coord aCoord) { 80 return aScale * aCoord.value; 81 } 82 friend result_type operator/(coord aCoord, primitive aScale) { 83 return aCoord.value / aScale; 84 } 85 // 'scale / coord' is intentionally omitted because it doesn't make sense. 86 }; 87 88 template <class units> 89 struct IntCoordTyped 90 : public BaseCoord<int32_t, IntCoordTyped<units>>, 91 public units, 92 public CoordOperatorsHelper<true, IntCoordTyped<units>, float>, 93 public CoordOperatorsHelper<true, IntCoordTyped<units>, double> { 94 static_assert(IsPixel<units>::value, 95 "'units' must be a coordinate system tag"); 96 97 using Super = BaseCoord<int32_t, IntCoordTyped<units>>; 98 99 constexpr IntCoordTyped() : Super() {} 100 constexpr MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {} 101 }; 102 103 template <class units, class F> 104 struct CoordTyped : public BaseCoord<F, CoordTyped<units, F>>, 105 public units, 106 public CoordOperatorsHelper<!std::is_same_v<F, int32_t>, 107 CoordTyped<units, F>, int32_t>, 108 public CoordOperatorsHelper<!std::is_same_v<F, uint32_t>, 109 CoordTyped<units, F>, uint32_t>, 110 public CoordOperatorsHelper<!std::is_same_v<F, double>, 111 CoordTyped<units, F>, double>, 112 public CoordOperatorsHelper<!std::is_same_v<F, float>, 113 CoordTyped<units, F>, float> { 114 static_assert(IsPixel<units>::value, 115 "'units' must be a coordinate system tag"); 116 117 using Super = BaseCoord<F, CoordTyped<units, F>>; 118 119 constexpr CoordTyped() : Super() {} 120 constexpr MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {} 121 explicit constexpr CoordTyped(const IntCoordTyped<units>& aCoord) 122 : Super(F(aCoord.value)) {} 123 124 void Round() { this->value = floor(this->value + 0.5); } 125 void Truncate() { this->value = int32_t(this->value); } 126 127 IntCoordTyped<units> Rounded() const { 128 return IntCoordTyped<units>(int32_t(floor(this->value + 0.5))); 129 } 130 IntCoordTyped<units> Truncated() const { 131 return IntCoordTyped<units>(int32_t(this->value)); 132 } 133 }; 134 135 } // namespace gfx 136 } // namespace mozilla 137 138 #endif /* MOZILLA_GFX_COORD_H_ */ 139