PaintingCommandExecutorCPU.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*
  2. * Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Filters/StackBlurFilter.h>
  7. #include <LibGfx/StylePainter.h>
  8. #include <LibWeb/Painting/BorderRadiusCornerClipper.h>
  9. #include <LibWeb/Painting/FilterPainting.h>
  10. #include <LibWeb/Painting/PaintingCommandExecutorCPU.h>
  11. #include <LibWeb/Painting/RecordingPainter.h>
  12. #include <LibWeb/Painting/ShadowPainting.h>
  13. namespace Web::Painting {
  14. PaintingCommandExecutorCPU::PaintingCommandExecutorCPU(Gfx::Bitmap& bitmap)
  15. : m_target_bitmap(bitmap)
  16. {
  17. stacking_contexts.append({ Gfx::Painter(bitmap), {}, 1.0f });
  18. }
  19. CommandResult PaintingCommandExecutorCPU::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color)
  20. {
  21. auto& painter = this->painter();
  22. for (auto& glyph_or_emoji : glyph_run) {
  23. if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
  24. auto& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
  25. painter.draw_glyph(glyph.position, glyph.code_point, *glyph.font, color);
  26. } else {
  27. auto& emoji = glyph_or_emoji.get<Gfx::DrawEmoji>();
  28. painter.draw_emoji(emoji.position, *emoji.emoji, *emoji.font);
  29. }
  30. }
  31. return CommandResult::Continue;
  32. }
  33. CommandResult PaintingCommandExecutorCPU::draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const& color, Gfx::TextElision elision, Gfx::TextWrapping wrapping, Optional<NonnullRefPtr<Gfx::Font>> const& font)
  34. {
  35. auto& painter = this->painter();
  36. if (font.has_value()) {
  37. painter.draw_text(rect, raw_text, *font, alignment, color, elision, wrapping);
  38. } else {
  39. painter.draw_text(rect, raw_text, alignment, color, elision, wrapping);
  40. }
  41. return CommandResult::Continue;
  42. }
  43. CommandResult PaintingCommandExecutorCPU::fill_rect(Gfx::IntRect const& rect, Color const& color)
  44. {
  45. auto& painter = this->painter();
  46. painter.fill_rect(rect, color);
  47. return CommandResult::Continue;
  48. }
  49. CommandResult PaintingCommandExecutorCPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, float opacity, Gfx::Painter::ScalingMode scaling_mode)
  50. {
  51. auto& painter = this->painter();
  52. painter.draw_scaled_bitmap(dst_rect, bitmap, src_rect, opacity, scaling_mode);
  53. return CommandResult::Continue;
  54. }
  55. CommandResult PaintingCommandExecutorCPU::set_clip_rect(Gfx::IntRect const& rect)
  56. {
  57. auto& painter = this->painter();
  58. painter.clear_clip_rect();
  59. painter.add_clip_rect(rect);
  60. return CommandResult::Continue;
  61. }
  62. CommandResult PaintingCommandExecutorCPU::clear_clip_rect()
  63. {
  64. auto& painter = this->painter();
  65. painter.clear_clip_rect();
  66. return CommandResult::Continue;
  67. }
  68. CommandResult PaintingCommandExecutorCPU::set_font(Gfx::Font const& font)
  69. {
  70. auto& painter = this->painter();
  71. painter.set_font(font);
  72. return CommandResult::Continue;
  73. }
  74. CommandResult PaintingCommandExecutorCPU::push_stacking_context(bool semitransparent_or_has_non_identity_transform, float opacity, Gfx::FloatRect const& source_rect, Gfx::FloatRect const& transformed_destination_rect, Gfx::IntPoint const& painter_location)
  75. {
  76. auto& painter = this->painter();
  77. if (semitransparent_or_has_non_identity_transform) {
  78. auto destination_rect = transformed_destination_rect.to_rounded<int>();
  79. // FIXME: We should find a way to scale the paintable, rather than paint into a separate bitmap,
  80. // then scale it. This snippet now copies the background at the destination, then scales it down/up
  81. // to the size of the source (which could add some artefacts, though just scaling the bitmap already does that).
  82. // We need to copy the background at the destination because a bunch of our rendering effects now rely on
  83. // being able to sample the painter (see border radii, shadows, filters, etc).
  84. Gfx::FloatPoint destination_clipped_fixup {};
  85. auto try_get_scaled_destination_bitmap = [&]() -> ErrorOr<NonnullRefPtr<Gfx::Bitmap>> {
  86. Gfx::IntRect actual_destination_rect;
  87. auto bitmap = TRY(painter.get_region_bitmap(destination_rect, Gfx::BitmapFormat::BGRA8888, actual_destination_rect));
  88. // get_region_bitmap() may clip to a smaller region if the requested rect goes outside the painter, so we need to account for that.
  89. destination_clipped_fixup = Gfx::FloatPoint { destination_rect.location() - actual_destination_rect.location() };
  90. destination_rect = actual_destination_rect;
  91. if (source_rect.size() != transformed_destination_rect.size()) {
  92. auto sx = static_cast<float>(source_rect.width()) / transformed_destination_rect.width();
  93. auto sy = static_cast<float>(source_rect.height()) / transformed_destination_rect.height();
  94. bitmap = TRY(bitmap->scaled(sx, sy));
  95. destination_clipped_fixup.scale_by(sx, sy);
  96. }
  97. return bitmap;
  98. };
  99. auto bitmap_or_error = try_get_scaled_destination_bitmap();
  100. if (bitmap_or_error.is_error()) {
  101. // NOTE: If the creation of the bitmap fails, we need to skip all painting commands that belong to this stacking context.
  102. // We don't interrupt the execution of painting commands because get_region_bitmap() returns an error if the requested
  103. // region is outside of the viewport (mmap fails to allocate a zero-size region), which means we can safely proceed
  104. // with execution of commands outside of this stacking context.
  105. // FIXME: Change the get_region_bitmap() API to return ErrorOr<Optional<Bitmap>> and exit the execution of commands here
  106. // if we run out of memory.
  107. return CommandResult::SkipStackingContext;
  108. }
  109. auto bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors();
  110. Gfx::Painter stacking_context_painter(bitmap);
  111. stacking_context_painter.translate(painter_location + destination_clipped_fixup.to_type<int>());
  112. stacking_contexts.append(StackingContext {
  113. .painter = stacking_context_painter,
  114. .destination = destination_rect,
  115. .opacity = opacity,
  116. });
  117. } else {
  118. painter.save();
  119. }
  120. return CommandResult::Continue;
  121. }
  122. CommandResult PaintingCommandExecutorCPU::pop_stacking_context(bool semitransparent_or_has_non_identity_transform, Gfx::Painter::ScalingMode scaling_mode)
  123. {
  124. if (semitransparent_or_has_non_identity_transform) {
  125. auto stacking_context = stacking_contexts.take_last();
  126. auto bitmap = stacking_context.painter.target();
  127. auto destination_rect = stacking_context.destination;
  128. if (destination_rect.size() == bitmap->size()) {
  129. painter().blit(destination_rect.location(), *bitmap, bitmap->rect(), stacking_context.opacity);
  130. } else {
  131. painter().draw_scaled_bitmap(destination_rect, *bitmap, bitmap->rect(), stacking_context.opacity, scaling_mode);
  132. }
  133. } else {
  134. painter().restore();
  135. }
  136. return CommandResult::Continue;
  137. }
  138. CommandResult PaintingCommandExecutorCPU::push_stacking_context_with_mask(Gfx::IntRect const& paint_rect)
  139. {
  140. auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, paint_rect.size());
  141. if (bitmap_or_error.is_error())
  142. return CommandResult::Continue;
  143. auto bitmap = bitmap_or_error.release_value();
  144. Gfx::Painter stacking_context_painter(bitmap);
  145. stacking_context_painter.translate(-paint_rect.location());
  146. stacking_contexts.append(StackingContext {
  147. .painter = stacking_context_painter,
  148. .destination = {},
  149. .opacity = 1,
  150. });
  151. return CommandResult::Continue;
  152. }
  153. CommandResult PaintingCommandExecutorCPU::pop_stacking_context_with_mask(Gfx::IntRect const& paint_rect, RefPtr<Gfx::Bitmap> const& mask_bitmap, Gfx::Bitmap::MaskKind mask_kind, float opacity)
  154. {
  155. auto stacking_context = stacking_contexts.take_last();
  156. auto bitmap = stacking_context.painter.target();
  157. if (mask_bitmap)
  158. bitmap->apply_mask(*mask_bitmap, mask_kind);
  159. painter().blit(paint_rect.location(), *bitmap, bitmap->rect(), opacity);
  160. return CommandResult::Continue;
  161. }
  162. CommandResult PaintingCommandExecutorCPU::paint_linear_gradient(Gfx::IntRect const& gradient_rect, Web::Painting::LinearGradientData const& linear_gradient_data)
  163. {
  164. auto const& data = linear_gradient_data;
  165. painter().fill_rect_with_linear_gradient(
  166. gradient_rect, data.color_stops.list,
  167. data.gradient_angle, data.color_stops.repeat_length);
  168. return CommandResult::Continue;
  169. }
  170. CommandResult PaintingCommandExecutorCPU::paint_outer_box_shadow(PaintOuterBoxShadowParams const& outer_box_shadow_params)
  171. {
  172. auto& painter = this->painter();
  173. Web::Painting::paint_outer_box_shadow(painter, outer_box_shadow_params);
  174. return CommandResult::Continue;
  175. }
  176. CommandResult PaintingCommandExecutorCPU::paint_inner_box_shadow(PaintOuterBoxShadowParams const& outer_box_shadow_params)
  177. {
  178. auto& painter = this->painter();
  179. Web::Painting::paint_inner_box_shadow(painter, outer_box_shadow_params);
  180. return CommandResult::Continue;
  181. }
  182. CommandResult PaintingCommandExecutorCPU::paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, String const& text, Gfx::Font const& font, Color const& color, int fragment_baseline, Gfx::IntPoint const& draw_location)
  183. {
  184. // FIXME: Figure out the maximum bitmap size for all shadows and then allocate it once and reuse it?
  185. auto maybe_shadow_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, shadow_bounding_rect.size());
  186. if (maybe_shadow_bitmap.is_error()) {
  187. dbgln("Unable to allocate temporary bitmap {} for text-shadow rendering: {}", shadow_bounding_rect.size(), maybe_shadow_bitmap.error());
  188. return CommandResult::Continue;
  189. }
  190. auto shadow_bitmap = maybe_shadow_bitmap.release_value();
  191. Gfx::Painter shadow_painter { *shadow_bitmap };
  192. // FIXME: "Spread" the shadow somehow.
  193. Gfx::IntPoint baseline_start(text_rect.x(), text_rect.y() + fragment_baseline);
  194. shadow_painter.draw_text_run(baseline_start, Utf8View(text), font, color);
  195. // Blur
  196. Gfx::StackBlurFilter filter(*shadow_bitmap);
  197. filter.process_rgba(blur_radius, color);
  198. painter().blit(draw_location, *shadow_bitmap, shadow_bounding_rect);
  199. return CommandResult::Continue;
  200. }
  201. CommandResult PaintingCommandExecutorCPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Optional<Gfx::FloatPoint> const& aa_translation)
  202. {
  203. Gfx::AntiAliasingPainter aa_painter(painter());
  204. if (aa_translation.has_value())
  205. aa_painter.translate(*aa_translation);
  206. aa_painter.fill_rect_with_rounded_corners(
  207. rect,
  208. color,
  209. top_left_radius,
  210. top_right_radius,
  211. bottom_right_radius,
  212. bottom_left_radius);
  213. return CommandResult::Continue;
  214. }
  215. CommandResult PaintingCommandExecutorCPU::fill_path_using_color(Gfx::Path const& path, Color const& color, Gfx::Painter::WindingRule winding_rule, Optional<Gfx::FloatPoint> const& aa_translation)
  216. {
  217. Gfx::AntiAliasingPainter aa_painter(painter());
  218. if (aa_translation.has_value())
  219. aa_painter.translate(*aa_translation);
  220. aa_painter.fill_path(path, color, winding_rule);
  221. return CommandResult::Continue;
  222. }
  223. CommandResult PaintingCommandExecutorCPU::fill_path_using_paint_style(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Optional<Gfx::FloatPoint> const& aa_translation)
  224. {
  225. Gfx::AntiAliasingPainter aa_painter(painter());
  226. if (aa_translation.has_value())
  227. aa_painter.translate(*aa_translation);
  228. aa_painter.fill_path(path, paint_style, opacity, winding_rule);
  229. return CommandResult::Continue;
  230. }
  231. CommandResult PaintingCommandExecutorCPU::stroke_path_using_color(Gfx::Path const& path, Color const& color, float thickness, Optional<Gfx::FloatPoint> const& aa_translation)
  232. {
  233. Gfx::AntiAliasingPainter aa_painter(painter());
  234. if (aa_translation.has_value())
  235. aa_painter.translate(*aa_translation);
  236. aa_painter.stroke_path(path, color, thickness);
  237. return CommandResult::Continue;
  238. }
  239. CommandResult PaintingCommandExecutorCPU::stroke_path_using_paint_style(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, float thickness, float opacity, Optional<Gfx::FloatPoint> const& aa_translation)
  240. {
  241. Gfx::AntiAliasingPainter aa_painter(painter());
  242. if (aa_translation.has_value())
  243. aa_painter.translate(*aa_translation);
  244. aa_painter.stroke_path(path, paint_style, thickness, opacity);
  245. return CommandResult::Continue;
  246. }
  247. CommandResult PaintingCommandExecutorCPU::draw_ellipse(Gfx::IntRect const& rect, Color const& color, int thickness)
  248. {
  249. Gfx::AntiAliasingPainter aa_painter(painter());
  250. aa_painter.draw_ellipse(rect, color, thickness);
  251. return CommandResult::Continue;
  252. }
  253. CommandResult PaintingCommandExecutorCPU::fill_ellipse(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::BlendMode blend_mode)
  254. {
  255. Gfx::AntiAliasingPainter aa_painter(painter());
  256. aa_painter.fill_ellipse(rect, color, blend_mode);
  257. return CommandResult::Continue;
  258. }
  259. CommandResult PaintingCommandExecutorCPU::draw_line(Color const& color, Gfx::IntPoint const& from, Gfx::IntPoint const& to, int thickness, Gfx::Painter::LineStyle style, Color const& alternate_color)
  260. {
  261. if (style == Gfx::Painter::LineStyle::Dotted) {
  262. Gfx::AntiAliasingPainter aa_painter(painter());
  263. aa_painter.draw_line(from, to, color, thickness, style, alternate_color);
  264. } else {
  265. painter().draw_line(from, to, color, thickness, style, alternate_color);
  266. }
  267. return CommandResult::Continue;
  268. }
  269. CommandResult PaintingCommandExecutorCPU::draw_signed_distance_field(Gfx::IntRect const& rect, Color const& color, Gfx::GrayscaleBitmap const& sdf, float smoothing)
  270. {
  271. painter().draw_signed_distance_field(rect, color, sdf, smoothing);
  272. return CommandResult::Continue;
  273. }
  274. CommandResult PaintingCommandExecutorCPU::paint_progressbar(Gfx::IntRect const& frame_rect, Gfx::IntRect const& progress_rect, Palette const& palette, int min, int max, int value, StringView const& text)
  275. {
  276. auto& painter = this->painter();
  277. Gfx::StylePainter::paint_progressbar(painter, progress_rect, palette, min, max, value, text);
  278. Gfx::StylePainter::paint_frame(painter, frame_rect, palette, Gfx::FrameStyle::RaisedBox);
  279. return CommandResult::Continue;
  280. }
  281. CommandResult PaintingCommandExecutorCPU::paint_frame(Gfx::IntRect const& rect, Palette const& palette, Gfx::FrameStyle style)
  282. {
  283. Gfx::StylePainter::paint_frame(painter(), rect, palette, style);
  284. return CommandResult::Continue;
  285. }
  286. CommandResult PaintingCommandExecutorCPU::apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter)
  287. {
  288. auto& painter = this->painter();
  289. // This performs the backdrop filter operation: https://drafts.fxtf.org/filter-effects-2/#backdrop-filter-operation
  290. // Note: The region bitmap can be smaller than the backdrop_region if it's at the edge of canvas.
  291. // Note: This is in DevicePixels, but we use an IntRect because `get_region_bitmap()` below writes to it.
  292. // FIXME: Go through the steps to find the "Backdrop Root Image"
  293. // https://drafts.fxtf.org/filter-effects-2/#BackdropRoot
  294. // 1. Copy the Backdrop Root Image into a temporary buffer, such as a raster image. Call this buffer T’.
  295. Gfx::IntRect actual_region {};
  296. auto maybe_backdrop_bitmap = painter.get_region_bitmap(backdrop_region, Gfx::BitmapFormat::BGRA8888, actual_region);
  297. if (actual_region.is_empty())
  298. return CommandResult::Continue;
  299. if (maybe_backdrop_bitmap.is_error()) {
  300. dbgln("Failed get region bitmap for backdrop-filter");
  301. return CommandResult::Continue;
  302. }
  303. auto backdrop_bitmap = maybe_backdrop_bitmap.release_value();
  304. // 2. Apply the backdrop-filter’s filter operations to the entire contents of T'.
  305. apply_filter_list(*backdrop_bitmap, backdrop_filter.filters);
  306. // FIXME: 3. If element B has any transforms (between B and the Backdrop Root), apply the inverse of those transforms to the contents of T’.
  307. // 4. Apply a clip to the contents of T’, using the border box of element B, including border-radius if specified. Note that the children of B are not considered for the sizing or location of this clip.
  308. // FIXME: 5. Draw all of element B, including its background, border, and any children elements, into T’.
  309. // FXIME: 6. If element B has any transforms, effects, or clips, apply those to T’.
  310. // 7. Composite the contents of T’ into element B’s parent, using source-over compositing.
  311. painter.blit(actual_region.location(), *backdrop_bitmap, backdrop_bitmap->rect());
  312. return CommandResult::Continue;
  313. }
  314. CommandResult PaintingCommandExecutorCPU::draw_rect(Gfx::IntRect const& rect, Color const& color, bool rough)
  315. {
  316. painter().draw_rect(rect, color, rough);
  317. return CommandResult::Continue;
  318. }
  319. CommandResult PaintingCommandExecutorCPU::paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size)
  320. {
  321. painter().fill_rect_with_radial_gradient(rect, radial_gradient_data.color_stops.list, center, size, radial_gradient_data.color_stops.repeat_length);
  322. return CommandResult::Continue;
  323. }
  324. CommandResult PaintingCommandExecutorCPU::paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position)
  325. {
  326. painter().fill_rect_with_conic_gradient(rect, conic_gradient_data.color_stops.list, position, conic_gradient_data.start_angle, conic_gradient_data.color_stops.repeat_length);
  327. return CommandResult::Continue;
  328. }
  329. CommandResult PaintingCommandExecutorCPU::draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const& color, int amplitude, int thickness)
  330. {
  331. painter().draw_triangle_wave(p1, p2, color, amplitude, thickness);
  332. return CommandResult::Continue;
  333. }
  334. CommandResult PaintingCommandExecutorCPU::sample_under_corners(BorderRadiusCornerClipper& corner_clipper)
  335. {
  336. corner_clipper.sample_under_corners(painter());
  337. return CommandResult::Continue;
  338. }
  339. CommandResult PaintingCommandExecutorCPU::blit_corner_clipping(BorderRadiusCornerClipper& corner_clipper)
  340. {
  341. corner_clipper.blit_corner_clipping(painter());
  342. return CommandResult::Continue;
  343. }
  344. bool PaintingCommandExecutorCPU::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
  345. {
  346. return !painter().clip_rect().intersects(rect.translated(painter().translation()));
  347. }
  348. }