PixelUnits.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  3. * Copyright (c) 2012-2023, Apple Inc. All rights reserved.
  4. * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #pragma once
  9. #include <AK/Concepts.h>
  10. #include <AK/DistinctNumeric.h>
  11. #include <AK/Math.h>
  12. #include <AK/Traits.h>
  13. #include <LibGfx/Forward.h>
  14. #include <math.h>
  15. namespace Web {
  16. /// DevicePixels: A position or length on the physical display.
  17. AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(int, DevicePixels, Arithmetic, CastToUnderlying, Comparison, Increment);
  18. template<Integral T>
  19. constexpr bool operator==(DevicePixels left, T right) { return left.value() == right; }
  20. template<Integral T>
  21. constexpr bool operator!=(DevicePixels left, T right) { return left.value() != right; }
  22. template<Integral T>
  23. constexpr bool operator>(DevicePixels left, T right) { return left.value() > right; }
  24. template<Integral T>
  25. constexpr bool operator<(DevicePixels left, T right) { return left.value() < right; }
  26. template<Integral T>
  27. constexpr bool operator>=(DevicePixels left, T right) { return left.value() >= right; }
  28. template<Integral T>
  29. constexpr bool operator<=(DevicePixels left, T right) { return left.value() <= right; }
  30. template<Integral T>
  31. constexpr DevicePixels operator*(DevicePixels left, T right) { return left.value() * right; }
  32. template<Integral T>
  33. constexpr DevicePixels operator*(T left, DevicePixels right) { return right * left; }
  34. template<Integral T>
  35. constexpr DevicePixels operator/(DevicePixels left, T right) { return left.value() / right; }
  36. template<Integral T>
  37. constexpr DevicePixels operator%(DevicePixels left, T right) { return left.value() % right; }
  38. /// CSSPixels: A position or length in CSS "reference pixels", independent of zoom or screen DPI.
  39. /// See https://www.w3.org/TR/css-values-3/#reference-pixel
  40. class CSSPixels {
  41. public:
  42. static constexpr i32 fractional_bits = 6;
  43. static constexpr i32 fixed_point_denominator = 1 << fractional_bits;
  44. static constexpr i32 radix_mask = fixed_point_denominator - 1;
  45. static constexpr i32 max_integer_value = NumericLimits<int>::max() >> fractional_bits;
  46. static constexpr i32 min_integer_value = NumericLimits<int>::min() >> fractional_bits;
  47. constexpr CSSPixels() = default;
  48. template<Signed I>
  49. constexpr CSSPixels(I value)
  50. {
  51. if (value > max_integer_value) [[unlikely]]
  52. m_value = NumericLimits<int>::max();
  53. else if (value < min_integer_value) [[unlikely]]
  54. m_value = NumericLimits<int>::min();
  55. else
  56. m_value = static_cast<int>(value) << fractional_bits;
  57. }
  58. CSSPixels(float value)
  59. {
  60. if (!isnan(value))
  61. m_value = AK::clamp_to_int(value * fixed_point_denominator);
  62. }
  63. CSSPixels(double value)
  64. {
  65. if (!isnan(value))
  66. m_value = AK::clamp_to_int(value * fixed_point_denominator);
  67. }
  68. template<Unsigned U>
  69. constexpr CSSPixels(U value)
  70. {
  71. if (value > max_integer_value) [[unlikely]]
  72. m_value = NumericLimits<int>::max();
  73. else
  74. m_value = static_cast<int>(value) << fractional_bits;
  75. }
  76. static constexpr CSSPixels from_raw(int value)
  77. {
  78. CSSPixels res;
  79. res.set_raw_value(value);
  80. return res;
  81. }
  82. float to_float() const;
  83. double to_double() const;
  84. int to_int() const;
  85. constexpr int raw_value() const { return m_value; }
  86. constexpr void set_raw_value(int value) { m_value = value; }
  87. constexpr bool might_be_saturated() const { return raw_value() == NumericLimits<i32>::max() || raw_value() == NumericLimits<i32>::min(); }
  88. constexpr bool operator==(CSSPixels const& other) const = default;
  89. explicit operator double() const { return to_double(); }
  90. constexpr CSSPixels& operator++()
  91. {
  92. m_value = Checked<int>::saturating_add(m_value, fixed_point_denominator);
  93. return *this;
  94. }
  95. constexpr CSSPixels& operator--()
  96. {
  97. m_value = Checked<int>::saturating_sub(m_value, fixed_point_denominator);
  98. return *this;
  99. }
  100. constexpr int operator<=>(CSSPixels const& other) const
  101. {
  102. return raw_value() > other.raw_value()
  103. ? 1
  104. : raw_value() < other.raw_value()
  105. ? -1
  106. : 0;
  107. }
  108. constexpr CSSPixels operator+() const { return from_raw(+raw_value()); }
  109. constexpr CSSPixels operator-() const { return from_raw(-raw_value()); }
  110. constexpr CSSPixels operator+(CSSPixels const& other) const
  111. {
  112. return from_raw(Checked<int>::saturating_add(raw_value(), other.raw_value()));
  113. }
  114. constexpr CSSPixels operator-(CSSPixels const& other) const
  115. {
  116. return from_raw(Checked<int>::saturating_sub(raw_value(), other.raw_value()));
  117. }
  118. constexpr CSSPixels operator*(CSSPixels const& other) const
  119. {
  120. i64 value = raw_value();
  121. value *= other.raw_value();
  122. int int_value = AK::clamp_to_int(value >> fractional_bits);
  123. // Rounding:
  124. // If last bit cut off was 1:
  125. if (value & (1u << (fractional_bits - 1))) {
  126. // If the bit after was 1 as well
  127. if (value & (radix_mask >> 2u)) {
  128. // We need to round away from 0
  129. int_value = Checked<int>::saturating_add(int_value, 1);
  130. } else {
  131. // Otherwise we round to the next even value
  132. // Which means we add the least significant bit of the raw integer value
  133. int_value = Checked<int>::saturating_add(int_value, int_value & 1);
  134. }
  135. }
  136. return from_raw(int_value);
  137. }
  138. constexpr CSSPixels operator/(CSSPixels const& other) const
  139. {
  140. i64 mult = raw_value();
  141. mult <<= fractional_bits;
  142. mult /= other.raw_value();
  143. int int_value = AK::clamp_to_int(mult);
  144. return from_raw(int_value);
  145. }
  146. constexpr CSSPixels& operator+=(CSSPixels const& other)
  147. {
  148. *this = *this + other;
  149. return *this;
  150. }
  151. constexpr CSSPixels& operator-=(CSSPixels const& other)
  152. {
  153. *this = *this - other;
  154. return *this;
  155. }
  156. constexpr CSSPixels& operator*=(CSSPixels const& other)
  157. {
  158. *this = *this * other;
  159. return *this;
  160. }
  161. constexpr CSSPixels& operator/=(CSSPixels const& other)
  162. {
  163. *this = *this / other;
  164. return *this;
  165. }
  166. constexpr CSSPixels abs() const { return from_raw(::abs(m_value)); }
  167. private:
  168. i32 m_value { 0 };
  169. };
  170. constexpr bool operator==(CSSPixels left, int right) { return left == CSSPixels(right); }
  171. inline bool operator==(CSSPixels left, float right) { return left.to_float() == right; }
  172. inline bool operator==(CSSPixels left, double right) { return left.to_double() == right; }
  173. constexpr bool operator>(CSSPixels left, int right) { return left > CSSPixels(right); }
  174. inline bool operator>(CSSPixels left, float right) { return left.to_float() > right; }
  175. inline bool operator>(CSSPixels left, double right) { return left.to_double() > right; }
  176. constexpr bool operator<(CSSPixels left, int right) { return left < CSSPixels(right); }
  177. inline bool operator<(CSSPixels left, float right) { return left.to_float() < right; }
  178. inline bool operator<(CSSPixels left, double right) { return left.to_double() < right; }
  179. constexpr CSSPixels operator*(CSSPixels left, int right) { return left * CSSPixels(right); }
  180. constexpr CSSPixels operator*(CSSPixels left, unsigned long right) { return left * CSSPixels(right); }
  181. inline float operator*(CSSPixels left, float right) { return left.to_float() * right; }
  182. inline double operator*(CSSPixels left, double right) { return left.to_double() * right; }
  183. constexpr CSSPixels operator*(int left, CSSPixels right) { return right * CSSPixels(left); }
  184. constexpr CSSPixels operator*(unsigned long left, CSSPixels right) { return right * CSSPixels(left); }
  185. inline float operator*(float left, CSSPixels right) { return right.to_float() * left; }
  186. inline double operator*(double left, CSSPixels right) { return right.to_double() * left; }
  187. constexpr CSSPixels operator/(CSSPixels left, int right) { return left / CSSPixels(right); }
  188. constexpr CSSPixels operator/(CSSPixels left, unsigned long right) { return left / CSSPixels(right); }
  189. inline float operator/(CSSPixels left, float right) { return left.to_float() / right; }
  190. inline double operator/(CSSPixels left, double right) { return left.to_double() / right; }
  191. using CSSPixelLine = Gfx::Line<CSSPixels>;
  192. using CSSPixelPoint = Gfx::Point<CSSPixels>;
  193. using CSSPixelRect = Gfx::Rect<CSSPixels>;
  194. using CSSPixelSize = Gfx::Size<CSSPixels>;
  195. using DevicePixelLine = Gfx::Line<DevicePixels>;
  196. using DevicePixelPoint = Gfx::Point<DevicePixels>;
  197. using DevicePixelRect = Gfx::Rect<DevicePixels>;
  198. using DevicePixelSize = Gfx::Size<DevicePixels>;
  199. }
  200. inline Web::CSSPixels abs(Web::CSSPixels const& value)
  201. {
  202. return value.abs();
  203. }
  204. constexpr Web::CSSPixels floor(Web::CSSPixels const& value)
  205. {
  206. return Web::CSSPixels::from_raw(value.raw_value() & ~Web::CSSPixels::radix_mask);
  207. }
  208. constexpr Web::CSSPixels ceil(Web::CSSPixels const& value)
  209. {
  210. auto floor_value = value.raw_value() & ~Web::CSSPixels::radix_mask;
  211. auto ceil_value = floor_value + (value.raw_value() & Web::CSSPixels::radix_mask ? Web::CSSPixels::fixed_point_denominator : 0);
  212. return Web::CSSPixels::from_raw(ceil_value);
  213. }
  214. constexpr Web::CSSPixels round(Web::CSSPixels const& value)
  215. {
  216. // FIXME: Maybe do this with bit-fiddling instead
  217. if (value > 0)
  218. return floor(value + Web::CSSPixels::from_raw(Web::CSSPixels::fixed_point_denominator >> 1 /* 0.5 */));
  219. return ceil(value - Web::CSSPixels::from_raw(Web::CSSPixels::fixed_point_denominator >> 1 /* 0.5 */));
  220. }
  221. constexpr Web::DevicePixels abs(Web::DevicePixels const& value)
  222. {
  223. return AK::abs(value.value());
  224. }
  225. namespace AK {
  226. template<>
  227. struct Traits<Web::CSSPixels> : public GenericTraits<Web::CSSPixels> {
  228. static unsigned hash(Web::CSSPixels const& key)
  229. {
  230. VERIFY(isfinite(key.to_double()));
  231. return Traits<double>::hash(key.to_double());
  232. }
  233. static bool equals(Web::CSSPixels const& a, Web::CSSPixels const& b)
  234. {
  235. return a == b;
  236. }
  237. };
  238. template<>
  239. struct Traits<Web::DevicePixels> : public GenericTraits<Web::DevicePixels> {
  240. static unsigned hash(Web::DevicePixels const& key)
  241. {
  242. return Traits<Web::DevicePixels::Type>::hash(key.value());
  243. }
  244. static bool equals(Web::DevicePixels const& a, Web::DevicePixels const& b)
  245. {
  246. return a == b;
  247. }
  248. };
  249. template<>
  250. struct Formatter<Web::CSSPixels> : Formatter<double> {
  251. ErrorOr<void> format(FormatBuilder& builder, Web::CSSPixels const& value)
  252. {
  253. return Formatter<double>::format(builder, value.to_double());
  254. }
  255. };
  256. template<>
  257. struct Formatter<Web::DevicePixels> : Formatter<Web::DevicePixels::Type> {
  258. ErrorOr<void> format(FormatBuilder& builder, Web::DevicePixels const& value)
  259. {
  260. return Formatter<Web::DevicePixels::Type>::format(builder, value.value());
  261. }
  262. };
  263. }