BorderRadiusCornerClipper.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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(PaintContext& context, DevicePixelRect 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(context);
  14. auto top_right = border_radii.top_right.as_corner(context);
  15. auto bottom_right = border_radii.bottom_right.as_corner(context);
  16. auto bottom_left = border_radii.bottom_left.as_corner(context);
  17. DevicePixelSize corners_bitmap_size {
  18. max(
  19. max(
  20. top_left.horizontal_radius + top_right.horizontal_radius,
  21. top_left.horizontal_radius + bottom_right.horizontal_radius),
  22. max(
  23. bottom_left.horizontal_radius + top_right.horizontal_radius,
  24. bottom_left.horizontal_radius + bottom_right.horizontal_radius)),
  25. max(
  26. max(
  27. top_left.vertical_radius + bottom_left.vertical_radius,
  28. top_left.vertical_radius + bottom_right.vertical_radius),
  29. max(
  30. top_right.vertical_radius + bottom_left.vertical_radius,
  31. top_right.vertical_radius + bottom_right.vertical_radius))
  32. };
  33. RefPtr<Gfx::Bitmap> corner_bitmap;
  34. if (use_cached_bitmap == UseCachedBitmap::Yes) {
  35. corner_bitmap = get_cached_corner_bitmap(corners_bitmap_size);
  36. if (!corner_bitmap)
  37. return Error::from_errno(ENOMEM);
  38. } else {
  39. corner_bitmap = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, corners_bitmap_size.to_type<int>()));
  40. }
  41. CornerData corner_data {
  42. .corner_radii = {
  43. .top_left = top_left,
  44. .top_right = top_right,
  45. .bottom_right = bottom_right,
  46. .bottom_left = bottom_left },
  47. .page_locations = { .top_left = border_rect.top_left(), .top_right = border_rect.top_right().translated(-top_right.horizontal_radius, 0), .bottom_right = border_rect.bottom_right().translated(-bottom_right.horizontal_radius, -bottom_right.vertical_radius), .bottom_left = border_rect.bottom_left().translated(0, -bottom_left.vertical_radius) },
  48. .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 } },
  49. .corner_bitmap_size = corners_bitmap_size
  50. };
  51. return BorderRadiusCornerClipper { corner_data, corner_bitmap.release_nonnull(), corner_clip };
  52. }
  53. void BorderRadiusCornerClipper::sample_under_corners(Gfx::Painter& page_painter)
  54. {
  55. // Generate a mask for the corners:
  56. Gfx::Painter corner_painter { *m_corner_bitmap };
  57. Gfx::AntiAliasingPainter corner_aa_painter { corner_painter };
  58. Gfx::IntRect corner_rect { { 0, 0 }, m_data.corner_bitmap_size };
  59. corner_aa_painter.fill_rect_with_rounded_corners(corner_rect, Color::NamedColor::Black,
  60. m_data.corner_radii.top_left, m_data.corner_radii.top_right, m_data.corner_radii.bottom_right, m_data.corner_radii.bottom_left);
  61. auto copy_page_masked = [&](Gfx::IntRect const& mask_src, Gfx::IntPoint const& page_location) {
  62. for (int row = 0; row < mask_src.height(); ++row) {
  63. for (int col = 0; col < mask_src.width(); ++col) {
  64. auto corner_location = mask_src.location().translated(col, row);
  65. auto mask_pixel = m_corner_bitmap->get_pixel(corner_location);
  66. u8 mask_alpha = mask_pixel.alpha();
  67. if (m_corner_clip == CornerClip::Outside)
  68. mask_alpha = ~mask_pixel.alpha();
  69. auto final_pixel = Color();
  70. if (mask_alpha > 0) {
  71. auto page_pixel = page_painter.get_pixel(page_location.translated(col, row));
  72. if (page_pixel.has_value())
  73. final_pixel = page_pixel.value().with_alpha(mask_alpha);
  74. }
  75. m_corner_bitmap->set_pixel(corner_location, final_pixel);
  76. }
  77. }
  78. };
  79. // Copy the pixels under the corner mask (using the alpha of the mask):
  80. if (m_data.corner_radii.top_left)
  81. copy_page_masked(m_data.corner_radii.top_left.as_rect().translated(m_data.bitmap_locations.top_left.to_type<int>()), m_data.page_locations.top_left.to_type<int>());
  82. if (m_data.corner_radii.top_right)
  83. copy_page_masked(m_data.corner_radii.top_right.as_rect().translated(m_data.bitmap_locations.top_right.to_type<int>()), m_data.page_locations.top_right.to_type<int>());
  84. if (m_data.corner_radii.bottom_right)
  85. copy_page_masked(m_data.corner_radii.bottom_right.as_rect().translated(m_data.bitmap_locations.bottom_right.to_type<int>()), m_data.page_locations.bottom_right.to_type<int>());
  86. if (m_data.corner_radii.bottom_left)
  87. copy_page_masked(m_data.corner_radii.bottom_left.as_rect().translated(m_data.bitmap_locations.bottom_left.to_type<int>()), m_data.page_locations.bottom_left.to_type<int>());
  88. m_has_sampled = true;
  89. }
  90. void BorderRadiusCornerClipper::blit_corner_clipping(Gfx::Painter& painter)
  91. {
  92. VERIFY(m_has_sampled);
  93. // Restore the corners:
  94. if (m_data.corner_radii.top_left)
  95. painter.blit(m_data.page_locations.top_left.to_type<int>(), *m_corner_bitmap, m_data.corner_radii.top_left.as_rect().translated(m_data.bitmap_locations.top_left.to_type<int>()));
  96. if (m_data.corner_radii.top_right)
  97. painter.blit(m_data.page_locations.top_right.to_type<int>(), *m_corner_bitmap, m_data.corner_radii.top_right.as_rect().translated(m_data.bitmap_locations.top_right.to_type<int>()));
  98. if (m_data.corner_radii.bottom_right)
  99. painter.blit(m_data.page_locations.bottom_right.to_type<int>(), *m_corner_bitmap, m_data.corner_radii.bottom_right.as_rect().translated(m_data.bitmap_locations.bottom_right.to_type<int>()));
  100. if (m_data.corner_radii.bottom_left)
  101. painter.blit(m_data.page_locations.bottom_left.to_type<int>(), *m_corner_bitmap, m_data.corner_radii.bottom_left.as_rect().translated(m_data.bitmap_locations.bottom_left.to_type<int>()));
  102. }
  103. }