AffineCommandExecutorCPU.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Copyright (c) 2024, MacDue <macdue@dueutil.tech>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Painting/AffineCommandExecutorCPU.h>
  7. namespace Web::Painting {
  8. // This executor is hopes to handle (at least) 2D CSS transforms. All commands
  9. // implemented here are required to support affine transformations, if that is
  10. // not possible the implementation should say in CommandExecutorCPU. Note: The
  11. // transform can be assumed to be non-identity or translation, so there's no
  12. // need to add fast paths here (those will be handled in the normal executor).
  13. static Gfx::Path rect_path(Gfx::FloatRect const& rect)
  14. {
  15. Gfx::Path path;
  16. path.move_to({ rect.x(), rect.y() });
  17. path.line_to({ rect.x() + rect.width(), rect.y() });
  18. path.line_to({ rect.x() + rect.width(), rect.y() + rect.height() });
  19. path.line_to({ rect.x(), rect.y() + rect.height() });
  20. path.close();
  21. return path;
  22. }
  23. AffineCommandExecutorCPU::AffineCommandExecutorCPU(Gfx::Bitmap& bitmap, Gfx::AffineTransform transform, Gfx::IntRect clip)
  24. : m_painter(bitmap)
  25. {
  26. auto clip_quad = Gfx::AffineTransform {}.map_to_quad(clip.to_type<float>());
  27. m_stacking_contexts.append(StackingContext { transform, clip_quad, clip_quad.bounding_rect() });
  28. }
  29. CommandResult AffineCommandExecutorCPU::draw_glyph_run(DrawGlyphRun const&)
  30. {
  31. // FIXME: Implement.
  32. return CommandResult::Continue;
  33. }
  34. CommandResult AffineCommandExecutorCPU::draw_text(DrawText const&)
  35. {
  36. // FIXME: Implement.
  37. return CommandResult::Continue;
  38. }
  39. CommandResult AffineCommandExecutorCPU::fill_rect(FillRect const& command)
  40. {
  41. // FIXME: Somehow support clip_paths?
  42. auto path = rect_path(command.rect.to_type<float>()).copy_transformed(stacking_context().transform);
  43. aa_painter().fill_path(path, command.color, Gfx::WindingRule::EvenOdd);
  44. return CommandResult::Continue;
  45. }
  46. CommandResult AffineCommandExecutorCPU::draw_scaled_bitmap(DrawScaledBitmap const& command)
  47. {
  48. m_painter.draw_scaled_bitmap_with_transform(command.dst_rect, command.bitmap, command.src_rect.to_type<float>(), stacking_context().transform, 1.0f, command.scaling_mode);
  49. return CommandResult::Continue;
  50. }
  51. CommandResult AffineCommandExecutorCPU::draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const& command)
  52. {
  53. m_painter.draw_scaled_bitmap_with_transform(command.dst_rect, command.bitmap->bitmap(), command.src_rect.to_type<float>(), stacking_context().transform, 1.0f, command.scaling_mode);
  54. return CommandResult::Continue;
  55. }
  56. CommandResult AffineCommandExecutorCPU::save(Save const&)
  57. {
  58. m_painter.save();
  59. return CommandResult::Continue;
  60. }
  61. CommandResult AffineCommandExecutorCPU::restore(Restore const&)
  62. {
  63. m_painter.restore();
  64. return CommandResult::Continue;
  65. }
  66. CommandResult AffineCommandExecutorCPU::add_clip_rect(AddClipRect const&)
  67. {
  68. // FIXME: Implement. The plan here is to implement https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
  69. // within the rasterizer (which should work as the clip quadrilateral will always be convex).
  70. return CommandResult::Continue;
  71. }
  72. CommandResult AffineCommandExecutorCPU::push_stacking_context(PushStackingContext const& command)
  73. {
  74. // FIXME: Support opacity.
  75. // FIXME: Support masks.
  76. // Note: Image rendering is not relevant as this does not transform via a bitmap.
  77. // Note: `position: fixed` does not apply when CSS transforms are involved.
  78. // FIXME: Attempt to support 3D transforms... Somehow?
  79. auto affine_transform = Gfx::extract_2d_affine_transform(command.transform.matrix);
  80. auto new_transform = Gfx::AffineTransform {}
  81. .set_translation(command.post_transform_translation.to_type<float>())
  82. .translate(command.transform.origin)
  83. .multiply(affine_transform)
  84. .translate(-command.transform.origin);
  85. auto const& current_stacking_context = stacking_context();
  86. m_stacking_contexts.append(StackingContext {
  87. .transform = Gfx::AffineTransform(current_stacking_context.transform).multiply(new_transform),
  88. .clip = current_stacking_context.clip,
  89. .clip_bounds = current_stacking_context.clip_bounds });
  90. return CommandResult::Continue;
  91. }
  92. CommandResult AffineCommandExecutorCPU::pop_stacking_context(PopStackingContext const&)
  93. {
  94. m_stacking_contexts.take_last();
  95. if (m_stacking_contexts.size() == 0)
  96. return CommandResult::ContinueWithParentExecutor;
  97. return CommandResult::Continue;
  98. }
  99. CommandResult AffineCommandExecutorCPU::paint_linear_gradient(PaintLinearGradient const&)
  100. {
  101. // FIXME: Implement.
  102. return CommandResult::Continue;
  103. }
  104. CommandResult AffineCommandExecutorCPU::paint_outer_box_shadow(PaintOuterBoxShadow const&)
  105. {
  106. // FIXME: Implement.
  107. return CommandResult::Continue;
  108. }
  109. CommandResult AffineCommandExecutorCPU::paint_inner_box_shadow(PaintInnerBoxShadow const&)
  110. {
  111. // FIXME: Implement.
  112. return CommandResult::Continue;
  113. }
  114. CommandResult AffineCommandExecutorCPU::paint_text_shadow(PaintTextShadow const&)
  115. {
  116. // FIXME: Implement.
  117. return CommandResult::Continue;
  118. }
  119. CommandResult AffineCommandExecutorCPU::fill_rect_with_rounded_corners(FillRectWithRoundedCorners const& command)
  120. {
  121. Gfx::Path path;
  122. auto x = command.rect.x();
  123. auto y = command.rect.y();
  124. auto width = command.rect.width();
  125. auto height = command.rect.height();
  126. if (command.top_left_radius)
  127. path.move_to({ x + command.top_left_radius.horizontal_radius, y });
  128. else
  129. path.move_to({ x, y });
  130. if (command.top_right_radius) {
  131. path.horizontal_line_to(x + width - command.top_right_radius.horizontal_radius);
  132. path.elliptical_arc_to({ x + width, y + command.top_right_radius.horizontal_radius }, { command.top_right_radius.horizontal_radius, command.top_right_radius.vertical_radius }, 0, false, true);
  133. } else {
  134. path.horizontal_line_to(x + width);
  135. }
  136. if (command.bottom_right_radius) {
  137. path.vertical_line_to(y + height - command.bottom_right_radius.vertical_radius);
  138. path.elliptical_arc_to({ x + width - command.bottom_right_radius.horizontal_radius, y + height }, { command.bottom_right_radius.horizontal_radius, command.bottom_right_radius.vertical_radius }, 0, false, true);
  139. } else {
  140. path.vertical_line_to(y + height);
  141. }
  142. if (command.bottom_left_radius) {
  143. path.horizontal_line_to(x + command.bottom_left_radius.horizontal_radius);
  144. path.elliptical_arc_to({ x, y + height - command.bottom_left_radius.vertical_radius }, { command.bottom_left_radius.horizontal_radius, command.bottom_left_radius.vertical_radius }, 0, false, true);
  145. } else {
  146. path.horizontal_line_to(x);
  147. }
  148. if (command.top_left_radius) {
  149. path.vertical_line_to(y + command.top_left_radius.vertical_radius);
  150. path.elliptical_arc_to({ x + command.top_left_radius.horizontal_radius, y }, { command.top_left_radius.horizontal_radius, command.top_left_radius.vertical_radius }, 0, false, true);
  151. } else {
  152. path.vertical_line_to(y);
  153. }
  154. path = path.copy_transformed(stacking_context().transform);
  155. aa_painter().fill_path(path, command.color, Gfx::WindingRule::EvenOdd);
  156. return CommandResult::Continue;
  157. }
  158. CommandResult AffineCommandExecutorCPU::fill_path_using_color(FillPathUsingColor const& command)
  159. {
  160. auto path_transform = Gfx::AffineTransform(stacking_context().transform).multiply(Gfx::AffineTransform {}.set_translation(command.aa_translation));
  161. aa_painter().fill_path(command.path.copy_transformed(path_transform), command.color, command.winding_rule);
  162. return CommandResult::Continue;
  163. }
  164. CommandResult AffineCommandExecutorCPU::fill_path_using_paint_style(FillPathUsingPaintStyle const&)
  165. {
  166. // FIXME: Implement.
  167. return CommandResult::Continue;
  168. }
  169. CommandResult AffineCommandExecutorCPU::stroke_path_using_color(StrokePathUsingColor const& command)
  170. {
  171. auto path_transform = Gfx::AffineTransform(stacking_context().transform).multiply(Gfx::AffineTransform {}.set_translation(command.aa_translation));
  172. aa_painter().stroke_path(command.path.copy_transformed(path_transform), command.color, command.thickness);
  173. return CommandResult::Continue;
  174. }
  175. CommandResult AffineCommandExecutorCPU::stroke_path_using_paint_style(StrokePathUsingPaintStyle const&)
  176. {
  177. // FIXME: Implement.
  178. return CommandResult::Continue;
  179. }
  180. CommandResult AffineCommandExecutorCPU::draw_ellipse(DrawEllipse const&)
  181. {
  182. // FIXME: Implement.
  183. return CommandResult::Continue;
  184. }
  185. CommandResult AffineCommandExecutorCPU::fill_ellipse(FillEllipse const&)
  186. {
  187. // FIXME: Implement.
  188. return CommandResult::Continue;
  189. }
  190. CommandResult AffineCommandExecutorCPU::draw_line(DrawLine const& command)
  191. {
  192. // FIXME: Implement other line styles.
  193. Gfx::Path path;
  194. path.move_to(command.from.to_type<float>());
  195. path.line_to(command.to.to_type<float>());
  196. aa_painter().stroke_path(path, command.color, command.thickness);
  197. return CommandResult::Continue;
  198. }
  199. CommandResult AffineCommandExecutorCPU::apply_backdrop_filter(ApplyBackdropFilter const&)
  200. {
  201. // FIXME: Implement.
  202. return CommandResult::Continue;
  203. }
  204. CommandResult AffineCommandExecutorCPU::draw_rect(DrawRect const& command)
  205. {
  206. auto path = rect_path(command.rect.to_type<float>()).copy_transformed(stacking_context().transform);
  207. aa_painter().stroke_path(path, command.color, 1);
  208. return CommandResult::Continue;
  209. }
  210. CommandResult AffineCommandExecutorCPU::paint_radial_gradient(PaintRadialGradient const&)
  211. {
  212. // FIXME: Implement.
  213. return CommandResult::Continue;
  214. }
  215. CommandResult AffineCommandExecutorCPU::paint_conic_gradient(PaintConicGradient const&)
  216. {
  217. // FIXME: Implement.
  218. return CommandResult::Continue;
  219. }
  220. CommandResult AffineCommandExecutorCPU::draw_triangle_wave(DrawTriangleWave const&)
  221. {
  222. // FIXME: Implement.
  223. return CommandResult::Continue;
  224. }
  225. CommandResult AffineCommandExecutorCPU::sample_under_corners(SampleUnderCorners const&)
  226. {
  227. // FIXME: Implement? -- Likely not a good approach for transforms.
  228. return CommandResult::Continue;
  229. }
  230. CommandResult AffineCommandExecutorCPU::blit_corner_clipping(BlitCornerClipping const&)
  231. {
  232. // FIXME: Implement? -- Likely not a good approach for transforms.
  233. return CommandResult::Continue;
  234. }
  235. bool AffineCommandExecutorCPU::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
  236. {
  237. auto const& current_stacking_context = stacking_context();
  238. auto transformed_rect = current_stacking_context.transform.map(rect.to_type<float>());
  239. return transformed_rect.intersected(current_stacking_context.clip_bounds).is_empty();
  240. }
  241. }