BorderRadiusCornerClipper.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /*
  2. * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Bitmap.h>
  7. #include <LibGfx/Painter.h>
  8. #include <LibWeb/Painting/BorderRadiusCornerClipper.h>
  9. namespace Web::Painting {
  10. ErrorOr<BorderRadiusCornerClipper> BorderRadiusCornerClipper::create(Gfx::IntRect const& border_rect, BorderRadiiData const& border_radii, CornerClip corner_clip, UseCachedBitmap use_cached_bitmap)
  11. {
  12. VERIFY(border_radii.has_any_radius());
  13. auto top_left = border_radii.top_left.as_corner();
  14. auto top_right = border_radii.top_right.as_corner();
  15. auto bottom_right = border_radii.bottom_right.as_corner();
  16. auto bottom_left = border_radii.bottom_left.as_corner();
  17. Gfx::IntSize corners_bitmap_size {
  18. max(
  19. top_left.horizontal_radius + top_right.horizontal_radius,
  20. bottom_left.horizontal_radius + bottom_right.horizontal_radius),
  21. max(
  22. top_left.vertical_radius + bottom_left.vertical_radius,
  23. top_right.vertical_radius + bottom_right.vertical_radius)
  24. };
  25. RefPtr<Gfx::Bitmap> corner_bitmap;
  26. if (use_cached_bitmap == UseCachedBitmap::Yes) {
  27. corner_bitmap = get_cached_corner_bitmap(corners_bitmap_size);
  28. if (!corner_bitmap)
  29. return Error::from_errno(ENOMEM);
  30. } else {
  31. corner_bitmap = TRY(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, corners_bitmap_size));
  32. }
  33. CornerData corner_data {
  34. .corner_radii = {
  35. .top_left = top_left,
  36. .top_right = top_right,
  37. .bottom_right = bottom_right,
  38. .bottom_left = bottom_left },
  39. .page_locations = { .top_left = border_rect.top_left(), .top_right = border_rect.top_right().translated(-top_right.horizontal_radius + 1, 0), .bottom_right = border_rect.bottom_right().translated(-bottom_right.horizontal_radius + 1, -bottom_right.vertical_radius + 1), .bottom_left = border_rect.bottom_left().translated(0, -bottom_left.vertical_radius + 1) },
  40. .bitmap_locations = { .top_left = { 0, 0 }, .top_right = { corners_bitmap_size.width() - top_right.horizontal_radius, 0 }, .bottom_right = { corners_bitmap_size.width() - bottom_right.horizontal_radius, corners_bitmap_size.height() - bottom_right.vertical_radius }, .bottom_left = { 0, corners_bitmap_size.height() - bottom_left.vertical_radius } },
  41. .corner_bitmap_size = corners_bitmap_size
  42. };
  43. return BorderRadiusCornerClipper { corner_data, corner_bitmap.release_nonnull(), corner_clip };
  44. }
  45. void BorderRadiusCornerClipper::sample_under_corners(Gfx::Painter& page_painter)
  46. {
  47. // Generate a mask for the corners:
  48. Gfx::Painter corner_painter { *m_corner_bitmap };
  49. Gfx::AntiAliasingPainter corner_aa_painter { corner_painter };
  50. Gfx::IntRect corner_rect { { 0, 0 }, m_data.corner_bitmap_size };
  51. corner_aa_painter.fill_rect_with_rounded_corners(corner_rect, Color::NamedColor::Black,
  52. m_data.corner_radii.top_left, m_data.corner_radii.top_right, m_data.corner_radii.bottom_right, m_data.corner_radii.bottom_left);
  53. auto copy_page_masked = [&](auto const& mask_src, auto const& page_location) {
  54. for (int row = 0; row < mask_src.height(); ++row) {
  55. for (int col = 0; col < mask_src.width(); ++col) {
  56. auto corner_location = mask_src.location().translated(col, row);
  57. auto mask_pixel = m_corner_bitmap->get_pixel(corner_location);
  58. u8 mask_alpha = mask_pixel.alpha();
  59. if (m_corner_clip == CornerClip::Outside)
  60. mask_alpha = ~mask_pixel.alpha();
  61. auto final_pixel = Color();
  62. if (mask_alpha > 0) {
  63. auto page_pixel = page_painter.get_pixel(page_location.translated(col, row));
  64. if (page_pixel.has_value())
  65. final_pixel = page_pixel.value().with_alpha(mask_alpha);
  66. }
  67. m_corner_bitmap->set_pixel(corner_location, final_pixel);
  68. }
  69. }
  70. };
  71. // Copy the pixels under the corner mask (using the alpha of the mask):
  72. if (m_data.corner_radii.top_left)
  73. copy_page_masked(m_data.corner_radii.top_left.as_rect().translated(m_data.bitmap_locations.top_left), m_data.page_locations.top_left);
  74. if (m_data.corner_radii.top_right)
  75. copy_page_masked(m_data.corner_radii.top_right.as_rect().translated(m_data.bitmap_locations.top_right), m_data.page_locations.top_right);
  76. if (m_data.corner_radii.bottom_right)
  77. copy_page_masked(m_data.corner_radii.bottom_right.as_rect().translated(m_data.bitmap_locations.bottom_right), m_data.page_locations.bottom_right);
  78. if (m_data.corner_radii.bottom_left)
  79. copy_page_masked(m_data.corner_radii.bottom_left.as_rect().translated(m_data.bitmap_locations.bottom_left), m_data.page_locations.bottom_left);
  80. m_has_sampled = true;
  81. }
  82. void BorderRadiusCornerClipper::blit_corner_clipping(Gfx::Painter& painter)
  83. {
  84. VERIFY(m_has_sampled);
  85. // Restore the corners:
  86. if (m_data.corner_radii.top_left)
  87. painter.blit(m_data.page_locations.top_left, *m_corner_bitmap, m_data.corner_radii.top_left.as_rect().translated(m_data.bitmap_locations.top_left));
  88. if (m_data.corner_radii.top_right)
  89. painter.blit(m_data.page_locations.top_right, *m_corner_bitmap, m_data.corner_radii.top_right.as_rect().translated(m_data.bitmap_locations.top_right));
  90. if (m_data.corner_radii.bottom_right)
  91. painter.blit(m_data.page_locations.bottom_right, *m_corner_bitmap, m_data.corner_radii.bottom_right.as_rect().translated(m_data.bitmap_locations.bottom_right));
  92. if (m_data.corner_radii.bottom_left)
  93. painter.blit(m_data.page_locations.bottom_left, *m_corner_bitmap, m_data.corner_radii.bottom_left.as_rect().translated(m_data.bitmap_locations.bottom_left));
  94. }
  95. }