Transformation.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "Transformation.h"
  8. #include <LibWeb/Painting/PaintableBox.h>
  9. namespace Web::CSS {
  10. Transformation::Transformation(TransformFunction function, Vector<TransformValue>&& values)
  11. : m_function(function)
  12. , m_values(move(values))
  13. {
  14. }
  15. ErrorOr<Gfx::FloatMatrix4x4> Transformation::to_matrix(Optional<Painting::PaintableBox const&> paintable_box) const
  16. {
  17. auto count = m_values.size();
  18. auto value = [&](size_t index, CSSPixels const& reference_length = 0) -> ErrorOr<float> {
  19. return m_values[index].visit(
  20. [&](CSS::LengthPercentage const& value) -> ErrorOr<float> {
  21. if (paintable_box.has_value())
  22. return value.resolved(paintable_box->layout_node(), reference_length).to_px(paintable_box->layout_node()).to_float();
  23. if (value.is_length())
  24. return value.length().absolute_length_to_px().to_float();
  25. return Error::from_string_literal("Transform contains non absolute units");
  26. },
  27. [&](CSS::AngleOrCalculated const& value) -> ErrorOr<float> {
  28. if (paintable_box.has_value())
  29. return value.resolved(paintable_box->layout_node()).to_radians();
  30. if (!value.is_calculated())
  31. return value.value().to_radians();
  32. return Error::from_string_literal("Transform contains non absolute units");
  33. },
  34. [&](CSS::NumberPercentage const& value) -> ErrorOr<float> {
  35. if (value.is_percentage())
  36. return value.percentage().as_fraction();
  37. return value.number().value();
  38. });
  39. };
  40. CSSPixels width = 1;
  41. CSSPixels height = 1;
  42. if (paintable_box.has_value()) {
  43. auto reference_box = paintable_box->absolute_padding_box_rect();
  44. width = reference_box.width();
  45. height = reference_box.height();
  46. }
  47. switch (m_function) {
  48. case CSS::TransformFunction::Perspective:
  49. // https://drafts.csswg.org/css-transforms-2/#perspective
  50. // Count is zero when null parameter
  51. if (count == 1) {
  52. // FIXME: Add support for the 'perspective-origin' CSS property.
  53. auto distance = TRY(value(0));
  54. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  55. 0, 1, 0, 0,
  56. 0, 0, 1, 0,
  57. 0, 0, -1 / (distance <= 0 ? 1 : distance), 1);
  58. } else {
  59. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  60. 0, 1, 0, 0,
  61. 0, 0, 1, 0,
  62. 0, 0, 0, 1);
  63. }
  64. break;
  65. case CSS::TransformFunction::Matrix:
  66. if (count == 6)
  67. return Gfx::FloatMatrix4x4(TRY(value(0)), TRY(value(2)), 0, TRY(value(4)),
  68. TRY(value(1)), TRY(value(3)), 0, TRY(value(5)),
  69. 0, 0, 1, 0,
  70. 0, 0, 0, 1);
  71. break;
  72. case CSS::TransformFunction::Matrix3d:
  73. if (count == 16)
  74. return Gfx::FloatMatrix4x4(TRY(value(0)), TRY(value(4)), TRY(value(8)), TRY(value(12)),
  75. TRY(value(1)), TRY(value(5)), TRY(value(9)), TRY(value(13)),
  76. TRY(value(2)), TRY(value(6)), TRY(value(10)), TRY(value(14)),
  77. TRY(value(3)), TRY(value(7)), TRY(value(11)), TRY(value(15)));
  78. break;
  79. case CSS::TransformFunction::Translate:
  80. if (count == 1)
  81. return Gfx::FloatMatrix4x4(1, 0, 0, TRY(value(0, width)),
  82. 0, 1, 0, 0,
  83. 0, 0, 1, 0,
  84. 0, 0, 0, 1);
  85. if (count == 2)
  86. return Gfx::FloatMatrix4x4(1, 0, 0, TRY(value(0, width)),
  87. 0, 1, 0, TRY(value(1, height)),
  88. 0, 0, 1, 0,
  89. 0, 0, 0, 1);
  90. break;
  91. case CSS::TransformFunction::Translate3d:
  92. return Gfx::FloatMatrix4x4(1, 0, 0, TRY(value(0, width)),
  93. 0, 1, 0, TRY(value(1, height)),
  94. 0, 0, 1, TRY(value(2)),
  95. 0, 0, 0, 1);
  96. break;
  97. case CSS::TransformFunction::TranslateX:
  98. if (count == 1)
  99. return Gfx::FloatMatrix4x4(1, 0, 0, TRY(value(0, width)),
  100. 0, 1, 0, 0,
  101. 0, 0, 1, 0,
  102. 0, 0, 0, 1);
  103. break;
  104. case CSS::TransformFunction::TranslateY:
  105. if (count == 1)
  106. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  107. 0, 1, 0, TRY(value(0, height)),
  108. 0, 0, 1, 0,
  109. 0, 0, 0, 1);
  110. break;
  111. case CSS::TransformFunction::TranslateZ:
  112. if (count == 1)
  113. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  114. 0, 1, 0, 0,
  115. 0, 0, 1, TRY(value(0)),
  116. 0, 0, 0, 1);
  117. break;
  118. case CSS::TransformFunction::Scale:
  119. if (count == 1)
  120. return Gfx::FloatMatrix4x4(TRY(value(0)), 0, 0, 0,
  121. 0, TRY(value(0)), 0, 0,
  122. 0, 0, 1, 0,
  123. 0, 0, 0, 1);
  124. if (count == 2)
  125. return Gfx::FloatMatrix4x4(TRY(value(0)), 0, 0, 0,
  126. 0, TRY(value(1)), 0, 0,
  127. 0, 0, 1, 0,
  128. 0, 0, 0, 1);
  129. break;
  130. case CSS::TransformFunction::Scale3d:
  131. if (count == 3)
  132. return Gfx::FloatMatrix4x4(TRY(value(0)), 0, 0, 0,
  133. 0, TRY(value(1)), 0, 0,
  134. 0, 0, TRY(value(2)), 0,
  135. 0, 0, 0, 1);
  136. break;
  137. case CSS::TransformFunction::ScaleX:
  138. if (count == 1)
  139. return Gfx::FloatMatrix4x4(TRY(value(0)), 0, 0, 0,
  140. 0, 1, 0, 0,
  141. 0, 0, 1, 0,
  142. 0, 0, 0, 1);
  143. break;
  144. case CSS::TransformFunction::ScaleY:
  145. if (count == 1)
  146. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  147. 0, TRY(value(0)), 0, 0,
  148. 0, 0, 1, 0,
  149. 0, 0, 0, 1);
  150. break;
  151. case CSS::TransformFunction::ScaleZ:
  152. if (count == 1)
  153. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  154. 0, 1, 0, 0,
  155. 0, 0, TRY(value(0)), 0,
  156. 0, 0, 0, 1);
  157. break;
  158. case CSS::TransformFunction::Rotate3d:
  159. if (count == 4)
  160. return Gfx::rotation_matrix({ TRY(value(0)), TRY(value(1)), TRY(value(2)) }, TRY(value(3)));
  161. break;
  162. case CSS::TransformFunction::RotateX:
  163. if (count == 1)
  164. return Gfx::rotation_matrix({ 1.0f, 0.0f, 0.0f }, TRY(value(0)));
  165. break;
  166. case CSS::TransformFunction::RotateY:
  167. if (count == 1)
  168. return Gfx::rotation_matrix({ 0.0f, 1.0f, 0.0f }, TRY(value(0)));
  169. break;
  170. case CSS::TransformFunction::Rotate:
  171. case CSS::TransformFunction::RotateZ:
  172. if (count == 1)
  173. return Gfx::rotation_matrix({ 0.0f, 0.0f, 1.0f }, TRY(value(0)));
  174. break;
  175. case CSS::TransformFunction::Skew:
  176. if (count == 1)
  177. return Gfx::FloatMatrix4x4(1, tanf(TRY(value(0))), 0, 0,
  178. 0, 1, 0, 0,
  179. 0, 0, 1, 0,
  180. 0, 0, 0, 1);
  181. if (count == 2)
  182. return Gfx::FloatMatrix4x4(1, tanf(TRY(value(0))), 0, 0,
  183. tanf(TRY(value(1))), 1, 0, 0,
  184. 0, 0, 1, 0,
  185. 0, 0, 0, 1);
  186. break;
  187. case CSS::TransformFunction::SkewX:
  188. if (count == 1)
  189. return Gfx::FloatMatrix4x4(1, tanf(TRY(value(0))), 0, 0,
  190. 0, 1, 0, 0,
  191. 0, 0, 1, 0,
  192. 0, 0, 0, 1);
  193. break;
  194. case CSS::TransformFunction::SkewY:
  195. if (count == 1)
  196. return Gfx::FloatMatrix4x4(1, 0, 0, 0,
  197. tanf(TRY(value(0))), 1, 0, 0,
  198. 0, 0, 1, 0,
  199. 0, 0, 0, 1);
  200. break;
  201. }
  202. dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Unhandled transformation function {} with {} arguments", to_string(m_function), m_values.size());
  203. return Gfx::FloatMatrix4x4::identity();
  204. }
  205. }