CommandExecutorGPU.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibAccelGfx/GlyphAtlas.h>
  7. #include <LibWeb/Painting/BorderRadiusCornerClipper.h>
  8. #include <LibWeb/Painting/CommandExecutorGPU.h>
  9. namespace Web::Painting {
  10. CommandExecutorGPU::CommandExecutorGPU(AccelGfx::Context& context, Gfx::Bitmap& bitmap)
  11. : m_target_bitmap(bitmap)
  12. , m_context(context)
  13. {
  14. m_context.activate();
  15. auto canvas = AccelGfx::Canvas::create(bitmap.size());
  16. auto painter = AccelGfx::Painter::create(m_context, canvas);
  17. m_stacking_contexts.append({ .canvas = canvas,
  18. .painter = move(painter),
  19. .opacity = 1.0f,
  20. .destination = {},
  21. .transform = {} });
  22. }
  23. CommandExecutorGPU::~CommandExecutorGPU()
  24. {
  25. m_context.activate();
  26. VERIFY(m_stacking_contexts.size() == 1);
  27. painter().flush(m_target_bitmap);
  28. }
  29. CommandResult CommandExecutorGPU::draw_glyph_run(DrawGlyphRun const& command)
  30. {
  31. Vector<Gfx::DrawGlyphOrEmoji> transformed_glyph_run;
  32. auto const& glyphs = command.glyph_run->glyphs();
  33. transformed_glyph_run.ensure_capacity(glyphs.size());
  34. for (auto& glyph : glyphs) {
  35. auto transformed_glyph = glyph;
  36. transformed_glyph.visit([&](auto& glyph) {
  37. glyph.position = glyph.position.scaled(command.scale).translated(command.translation);
  38. glyph.font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(command.scale));
  39. });
  40. transformed_glyph_run.append(transformed_glyph);
  41. }
  42. painter().draw_glyph_run(transformed_glyph_run, command.color);
  43. return CommandResult::Continue;
  44. }
  45. CommandResult CommandExecutorGPU::draw_text(DrawText const&)
  46. {
  47. // FIXME
  48. return CommandResult::Continue;
  49. }
  50. CommandResult CommandExecutorGPU::fill_rect(FillRect const& command)
  51. {
  52. // FIXME: Support clip paths
  53. painter().fill_rect(command.rect, command.color);
  54. return CommandResult::Continue;
  55. }
  56. static AccelGfx::Painter::ScalingMode to_accelgfx_scaling_mode(Gfx::Painter::ScalingMode scaling_mode)
  57. {
  58. switch (scaling_mode) {
  59. case Gfx::Painter::ScalingMode::NearestNeighbor:
  60. case Gfx::Painter::ScalingMode::BoxSampling:
  61. case Gfx::Painter::ScalingMode::SmoothPixels:
  62. case Gfx::Painter::ScalingMode::None:
  63. return AccelGfx::Painter::ScalingMode::NearestNeighbor;
  64. case Gfx::Painter::ScalingMode::BilinearBlend:
  65. return AccelGfx::Painter::ScalingMode::Bilinear;
  66. default:
  67. VERIFY_NOT_REACHED();
  68. }
  69. }
  70. CommandResult CommandExecutorGPU::draw_scaled_bitmap(DrawScaledBitmap const& command)
  71. {
  72. painter().draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect, to_accelgfx_scaling_mode(command.scaling_mode));
  73. return CommandResult::Continue;
  74. }
  75. CommandResult CommandExecutorGPU::draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const& command)
  76. {
  77. // TODO: Support clip paths
  78. painter().draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect, to_accelgfx_scaling_mode(command.scaling_mode));
  79. return CommandResult::Continue;
  80. }
  81. CommandResult CommandExecutorGPU::set_clip_rect(SetClipRect const& command)
  82. {
  83. painter().set_clip_rect(command.rect);
  84. return CommandResult::Continue;
  85. }
  86. CommandResult CommandExecutorGPU::clear_clip_rect(ClearClipRect const&)
  87. {
  88. painter().clear_clip_rect();
  89. return CommandResult::Continue;
  90. }
  91. CommandResult CommandExecutorGPU::push_stacking_context(PushStackingContext const& command)
  92. {
  93. if (command.source_paintable_rect.is_empty())
  94. return CommandResult::SkipStackingContext;
  95. m_stacking_contexts.last().stacking_context_depth++;
  96. painter().save();
  97. if (command.is_fixed_position) {
  98. auto const& translation = painter().transform().translation();
  99. painter().translate(-translation);
  100. }
  101. auto stacking_context_transform = Gfx::extract_2d_affine_transform(command.transform.matrix);
  102. Gfx::AffineTransform inverse_origin_translation;
  103. inverse_origin_translation.translate(-command.transform.origin);
  104. Gfx::AffineTransform origin_translation;
  105. origin_translation.translate(command.transform.origin);
  106. Gfx::AffineTransform final_transform = origin_translation;
  107. final_transform.multiply(stacking_context_transform);
  108. final_transform.multiply(inverse_origin_translation);
  109. if (command.opacity < 1 || !stacking_context_transform.is_identity_or_translation()) {
  110. // If, due to layout mistakes, we encounter an excessively large rectangle here, it must be skipped to prevent
  111. // framebuffer allocation failure.
  112. if (command.source_paintable_rect.width() > 10000 || command.source_paintable_rect.height() > 10000) {
  113. dbgln("FIXME: Skipping stacking context with excessively large paintable rect: {}", command.source_paintable_rect);
  114. return CommandResult::SkipStackingContext;
  115. }
  116. auto canvas = AccelGfx::Canvas::create(command.source_paintable_rect.size());
  117. auto painter = AccelGfx::Painter::create(m_context, canvas);
  118. painter->translate(-command.source_paintable_rect.location().to_type<float>());
  119. painter->clear(Color::Transparent);
  120. m_stacking_contexts.append({ .canvas = canvas,
  121. .painter = move(painter),
  122. .opacity = command.opacity,
  123. .destination = command.source_paintable_rect,
  124. .transform = final_transform });
  125. } else {
  126. painter().translate(stacking_context_transform.translation() + command.post_transform_translation.to_type<float>());
  127. m_stacking_contexts.append({ .canvas = {},
  128. .painter = MaybeOwned(painter()),
  129. .opacity = command.opacity,
  130. .destination = {},
  131. .transform = final_transform });
  132. }
  133. return CommandResult::Continue;
  134. }
  135. CommandResult CommandExecutorGPU::pop_stacking_context(PopStackingContext const&)
  136. {
  137. auto stacking_context = m_stacking_contexts.take_last();
  138. VERIFY(stacking_context.stacking_context_depth == 0);
  139. if (stacking_context.painter.is_owned()) {
  140. painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity, stacking_context.transform);
  141. }
  142. painter().restore();
  143. m_stacking_contexts.last().stacking_context_depth--;
  144. return CommandResult::Continue;
  145. }
  146. CommandResult CommandExecutorGPU::paint_linear_gradient(PaintLinearGradient const& command)
  147. {
  148. // FIXME: Support clip paths
  149. auto const& linear_gradient_data = command.linear_gradient_data;
  150. painter().fill_rect_with_linear_gradient(command.gradient_rect, linear_gradient_data.color_stops.list, linear_gradient_data.gradient_angle, linear_gradient_data.color_stops.repeat_length);
  151. return CommandResult::Continue;
  152. }
  153. CommandResult CommandExecutorGPU::paint_outer_box_shadow(PaintOuterBoxShadow const&)
  154. {
  155. // FIXME
  156. return CommandResult::Continue;
  157. }
  158. CommandResult CommandExecutorGPU::paint_inner_box_shadow(PaintInnerBoxShadow const&)
  159. {
  160. // FIXME
  161. return CommandResult::Continue;
  162. }
  163. CommandResult CommandExecutorGPU::paint_text_shadow(PaintTextShadow const& command)
  164. {
  165. auto text_shadow_canvas = AccelGfx::Canvas::create(command.shadow_bounding_rect.size());
  166. auto text_shadow_painter = AccelGfx::Painter::create(m_context, text_shadow_canvas);
  167. text_shadow_painter->clear(command.color.with_alpha(0));
  168. Gfx::FloatRect const shadow_location { command.draw_location, command.shadow_bounding_rect.size() };
  169. Gfx::IntPoint const baseline_start(command.text_rect.x(), command.text_rect.y() + command.fragment_baseline);
  170. text_shadow_painter->translate(baseline_start.to_type<float>());
  171. text_shadow_painter->draw_glyph_run(command.glyph_run, command.color);
  172. if (command.blur_radius == 0) {
  173. painter().blit_canvas(shadow_location, *text_shadow_canvas);
  174. return CommandResult::Continue;
  175. }
  176. auto horizontal_blur_canvas = AccelGfx::Canvas::create(command.shadow_bounding_rect.size());
  177. auto horizontal_blur_painter = AccelGfx::Painter::create(m_context, horizontal_blur_canvas);
  178. horizontal_blur_painter->clear(command.color.with_alpha(0));
  179. horizontal_blur_painter->blit_blurred_canvas(command.shadow_bounding_rect.to_type<float>(), *text_shadow_canvas, command.blur_radius, AccelGfx::Painter::BlurDirection::Horizontal);
  180. painter().blit_blurred_canvas(shadow_location, *horizontal_blur_canvas, command.blur_radius, AccelGfx::Painter::BlurDirection::Vertical);
  181. return CommandResult::Continue;
  182. }
  183. CommandResult CommandExecutorGPU::fill_rect_with_rounded_corners(FillRectWithRoundedCorners const& command)
  184. {
  185. // FIXME: Support clip paths
  186. painter().fill_rect_with_rounded_corners(
  187. command.rect, command.color,
  188. { static_cast<float>(command.top_left_radius.horizontal_radius), static_cast<float>(command.top_left_radius.vertical_radius) },
  189. { static_cast<float>(command.top_right_radius.horizontal_radius), static_cast<float>(command.top_right_radius.vertical_radius) },
  190. { static_cast<float>(command.bottom_left_radius.horizontal_radius), static_cast<float>(command.bottom_left_radius.vertical_radius) },
  191. { static_cast<float>(command.bottom_right_radius.horizontal_radius), static_cast<float>(command.bottom_right_radius.vertical_radius) });
  192. return CommandResult::Continue;
  193. }
  194. CommandResult CommandExecutorGPU::fill_path_using_color(FillPathUsingColor const&)
  195. {
  196. // FIXME
  197. return CommandResult::Continue;
  198. }
  199. CommandResult CommandExecutorGPU::fill_path_using_paint_style(FillPathUsingPaintStyle const&)
  200. {
  201. // FIXME
  202. return CommandResult::Continue;
  203. }
  204. CommandResult CommandExecutorGPU::stroke_path_using_color(StrokePathUsingColor const&)
  205. {
  206. // FIXME
  207. return CommandResult::Continue;
  208. }
  209. CommandResult CommandExecutorGPU::stroke_path_using_paint_style(StrokePathUsingPaintStyle const&)
  210. {
  211. // FIXME
  212. return CommandResult::Continue;
  213. }
  214. CommandResult CommandExecutorGPU::draw_ellipse(DrawEllipse const&)
  215. {
  216. // FIXME
  217. return CommandResult::Continue;
  218. }
  219. CommandResult CommandExecutorGPU::fill_ellipse(FillEllipse const& command)
  220. {
  221. auto horizontal_radius = static_cast<float>(command.rect.width() / 2);
  222. auto vertical_radius = static_cast<float>(command.rect.height() / 2);
  223. painter().fill_rect_with_rounded_corners(
  224. command.rect, command.color,
  225. { horizontal_radius, vertical_radius },
  226. { horizontal_radius, vertical_radius },
  227. { horizontal_radius, vertical_radius },
  228. { horizontal_radius, vertical_radius });
  229. return CommandResult::Continue;
  230. }
  231. CommandResult CommandExecutorGPU::draw_line(DrawLine const& command)
  232. {
  233. // FIXME: Pass line style and alternate color once AccelGfx::Painter supports it
  234. painter().draw_line(command.from, command.to, command.thickness, command.color);
  235. return CommandResult::Continue;
  236. }
  237. CommandResult CommandExecutorGPU::draw_signed_distance_field(DrawSignedDistanceField const&)
  238. {
  239. // FIXME
  240. return CommandResult::Continue;
  241. }
  242. CommandResult CommandExecutorGPU::apply_backdrop_filter(ApplyBackdropFilter const&)
  243. {
  244. // FIXME
  245. return CommandResult::Continue;
  246. }
  247. CommandResult CommandExecutorGPU::draw_rect(DrawRect const&)
  248. {
  249. // FIXME
  250. return CommandResult::Continue;
  251. }
  252. CommandResult CommandExecutorGPU::paint_radial_gradient(PaintRadialGradient const&)
  253. {
  254. // FIXME
  255. return CommandResult::Continue;
  256. }
  257. CommandResult CommandExecutorGPU::paint_conic_gradient(PaintConicGradient const&)
  258. {
  259. // FIXME
  260. return CommandResult::Continue;
  261. }
  262. CommandResult CommandExecutorGPU::draw_triangle_wave(DrawTriangleWave const&)
  263. {
  264. // FIXME
  265. return CommandResult::Continue;
  266. }
  267. CommandResult CommandExecutorGPU::sample_under_corners(SampleUnderCorners const& command)
  268. {
  269. m_corner_clippers.resize(command.id + 1);
  270. m_corner_clippers[command.id] = make<BorderRadiusCornerClipper>();
  271. auto& corner_clipper = *m_corner_clippers[command.id];
  272. auto const& top_left = command.corner_radii.top_left;
  273. auto const& top_right = command.corner_radii.top_right;
  274. auto const& bottom_right = command.corner_radii.bottom_right;
  275. auto const& bottom_left = command.corner_radii.bottom_left;
  276. auto sampling_config = calculate_border_radius_sampling_config(command.corner_radii, command.border_rect);
  277. auto const& page_locations = sampling_config.page_locations;
  278. auto const& bitmap_locations = sampling_config.bitmap_locations;
  279. auto top_left_corner_size = Gfx::IntSize { top_left.horizontal_radius, top_left.vertical_radius };
  280. auto top_right_corner_size = Gfx::IntSize { top_right.horizontal_radius, top_right.vertical_radius };
  281. auto bottom_right_corner_size = Gfx::IntSize { bottom_right.horizontal_radius, bottom_right.vertical_radius };
  282. auto bottom_left_corner_size = Gfx::IntSize { bottom_left.horizontal_radius, bottom_left.vertical_radius };
  283. corner_clipper.page_top_left_rect = { page_locations.top_left, top_left_corner_size };
  284. corner_clipper.page_top_right_rect = { page_locations.top_right, top_right_corner_size };
  285. corner_clipper.page_bottom_right_rect = { page_locations.bottom_right, bottom_right_corner_size };
  286. corner_clipper.page_bottom_left_rect = { page_locations.bottom_left, bottom_left_corner_size };
  287. corner_clipper.sample_canvas_top_left_rect = { bitmap_locations.top_left, top_left_corner_size };
  288. corner_clipper.sample_canvas_top_right_rect = { bitmap_locations.top_right, top_right_corner_size };
  289. corner_clipper.sample_canvas_bottom_right_rect = { bitmap_locations.bottom_right, bottom_right_corner_size };
  290. corner_clipper.sample_canvas_bottom_left_rect = { bitmap_locations.bottom_left, bottom_left_corner_size };
  291. corner_clipper.corners_sample_canvas = AccelGfx::Canvas::create(sampling_config.corners_bitmap_size);
  292. auto corner_painter = AccelGfx::Painter::create(m_context, *corner_clipper.corners_sample_canvas);
  293. corner_painter->clear(Color::White);
  294. corner_painter->fill_rect_with_rounded_corners(
  295. Gfx::IntRect { { 0, 0 }, sampling_config.corners_bitmap_size },
  296. Color::Transparent,
  297. { static_cast<float>(top_left.horizontal_radius), static_cast<float>(top_left.vertical_radius) },
  298. { static_cast<float>(top_right.horizontal_radius), static_cast<float>(top_right.vertical_radius) },
  299. { static_cast<float>(bottom_right.horizontal_radius), static_cast<float>(bottom_right.vertical_radius) },
  300. { static_cast<float>(bottom_left.horizontal_radius), static_cast<float>(bottom_left.vertical_radius) },
  301. AccelGfx::Painter::BlendingMode::AlphaOverride);
  302. auto const& target_canvas = painter().canvas();
  303. if (!corner_clipper.sample_canvas_top_left_rect.is_empty())
  304. corner_painter->blit_canvas(corner_clipper.sample_canvas_top_left_rect, target_canvas, painter().transform().map(corner_clipper.page_top_left_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve);
  305. if (!corner_clipper.sample_canvas_top_right_rect.is_empty())
  306. corner_painter->blit_canvas(corner_clipper.sample_canvas_top_right_rect, target_canvas, painter().transform().map(corner_clipper.page_top_right_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve);
  307. if (!corner_clipper.sample_canvas_bottom_right_rect.is_empty())
  308. corner_painter->blit_canvas(corner_clipper.sample_canvas_bottom_right_rect, target_canvas, painter().transform().map(corner_clipper.page_bottom_right_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve);
  309. if (!corner_clipper.sample_canvas_bottom_left_rect.is_empty())
  310. corner_painter->blit_canvas(corner_clipper.sample_canvas_bottom_left_rect, target_canvas, painter().transform().map(corner_clipper.page_bottom_left_rect), 1.0f, {}, AccelGfx::Painter::BlendingMode::AlphaPreserve);
  311. return CommandResult::Continue;
  312. }
  313. CommandResult CommandExecutorGPU::blit_corner_clipping(BlitCornerClipping const& command)
  314. {
  315. auto const& corner_clipper = *m_corner_clippers[command.id];
  316. auto const& corner_sample_canvas = *corner_clipper.corners_sample_canvas;
  317. if (!corner_clipper.sample_canvas_top_left_rect.is_empty())
  318. painter().blit_canvas(corner_clipper.page_top_left_rect, corner_sample_canvas, corner_clipper.sample_canvas_top_left_rect);
  319. if (!corner_clipper.sample_canvas_top_right_rect.is_empty())
  320. painter().blit_canvas(corner_clipper.page_top_right_rect, corner_sample_canvas, corner_clipper.sample_canvas_top_right_rect);
  321. if (!corner_clipper.sample_canvas_bottom_right_rect.is_empty())
  322. painter().blit_canvas(corner_clipper.page_bottom_right_rect, corner_sample_canvas, corner_clipper.sample_canvas_bottom_right_rect);
  323. if (!corner_clipper.sample_canvas_bottom_left_rect.is_empty())
  324. painter().blit_canvas(corner_clipper.page_bottom_left_rect, corner_sample_canvas, corner_clipper.sample_canvas_bottom_left_rect);
  325. m_corner_clippers[command.id].clear();
  326. return CommandResult::Continue;
  327. }
  328. bool CommandExecutorGPU::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
  329. {
  330. auto translation = painter().transform().translation().to_type<int>();
  331. return !painter().clip_rect().intersects(rect.translated(translation));
  332. }
  333. void CommandExecutorGPU::prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
  334. {
  335. AccelGfx::GlyphAtlas::the().update(unique_glyphs);
  336. }
  337. void CommandExecutorGPU::prepare_to_execute([[maybe_unused]] size_t corner_clip_max_depth)
  338. {
  339. m_context.activate();
  340. }
  341. void CommandExecutorGPU::update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>& immutable_bitmaps)
  342. {
  343. painter().update_immutable_bitmap_texture_cache(immutable_bitmaps);
  344. }
  345. }