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 "mozilla/TypeTraits.h" // For IsSame 12 #include "Types.h" 13 #include "BaseCoord.h" 14 15 #include <cmath> 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 typedef decltype(int32_t() + primitive()) type; 43 }; 44 45 template <class units, class F, class primitive> 46 struct CommonType<CoordTyped<units, F>, primitive> { 47 typedef decltype(F() + primitive()) type; 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 typedef typename CommonType<coord, primitive>::type result_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 // Note: 'IntCoordTyped<units>' and 'CoordTyped<units>' do not derive from 89 // 'units' to work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61959. 90 91 template <class units> 92 struct IntCoordTyped 93 : public BaseCoord<int32_t, IntCoordTyped<units> >, 94 public CoordOperatorsHelper<true, IntCoordTyped<units>, float>, 95 public CoordOperatorsHelper<true, IntCoordTyped<units>, double> { 96 static_assert(IsPixel<units>::value, 97 "'units' must be a coordinate system tag"); 98 99 typedef BaseCoord<int32_t, IntCoordTyped<units> > Super; 100 101 constexpr IntCoordTyped() : Super() {} 102 constexpr MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {} 103 }; 104 105 template <class units, class F> 106 struct CoordTyped : public BaseCoord<F, CoordTyped<units, F> >, 107 public CoordOperatorsHelper<!IsSame<F, int32_t>::value, 108 CoordTyped<units, F>, int32_t>, 109 public CoordOperatorsHelper<!IsSame<F, uint32_t>::value, 110 CoordTyped<units, F>, uint32_t>, 111 public CoordOperatorsHelper<!IsSame<F, double>::value, 112 CoordTyped<units, F>, double>, 113 public CoordOperatorsHelper<!IsSame<F, float>::value, 114 CoordTyped<units, F>, float> { 115 static_assert(IsPixel<units>::value, 116 "'units' must be a coordinate system tag"); 117 118 typedef BaseCoord<F, CoordTyped<units, F> > Super; 119 120 constexpr CoordTyped() : Super() {} 121 constexpr MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {} 122 explicit constexpr CoordTyped(const IntCoordTyped<units>& aCoord) 123 : Super(F(aCoord.value)) {} 124 125 void Round() { this->value = floor(this->value + 0.5); } 126 void Truncate() { this->value = int32_t(this->value); } 127 128 IntCoordTyped<units> Rounded() const { 129 return IntCoordTyped<units>(int32_t(floor(this->value + 0.5))); 130 } 131 IntCoordTyped<units> Truncated() const { 132 return IntCoordTyped<units>(int32_t(this->value)); 133 } 134 }; 135 136 } // namespace gfx 137 } // namespace mozilla 138 139 #endif /* MOZILLA_GFX_COORD_H_ */ 140