BorderPainting.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Painter.h>
  7. #include <LibWeb/Painting/BorderPainting.h>
  8. #include <LibWeb/Painting/PaintContext.h>
  9. namespace Web::Painting {
  10. void paint_border(PaintContext& context, BorderEdge edge, const Gfx::FloatRect& rect, const CSS::ComputedValues& style)
  11. {
  12. const auto& border_data = [&] {
  13. switch (edge) {
  14. case BorderEdge::Top:
  15. return style.border_top();
  16. case BorderEdge::Right:
  17. return style.border_right();
  18. case BorderEdge::Bottom:
  19. return style.border_bottom();
  20. default: // BorderEdge::Left:
  21. return style.border_left();
  22. }
  23. }();
  24. float width = border_data.width;
  25. if (width <= 0)
  26. return;
  27. auto color = border_data.color;
  28. auto border_style = border_data.line_style;
  29. int int_width = max((int)width, 1);
  30. struct Points {
  31. Gfx::FloatPoint p1;
  32. Gfx::FloatPoint p2;
  33. };
  34. auto points_for_edge = [](BorderEdge edge, const Gfx::FloatRect& rect) -> Points {
  35. switch (edge) {
  36. case BorderEdge::Top:
  37. return { rect.top_left(), rect.top_right() };
  38. case BorderEdge::Right:
  39. return { rect.top_right(), rect.bottom_right() };
  40. case BorderEdge::Bottom:
  41. return { rect.bottom_left(), rect.bottom_right() };
  42. default: // Edge::Left
  43. return { rect.top_left(), rect.bottom_left() };
  44. }
  45. };
  46. auto [p1, p2] = points_for_edge(edge, rect);
  47. if (border_style == CSS::LineStyle::Inset) {
  48. auto top_left_color = Color::from_rgb(0x5a5a5a);
  49. auto bottom_right_color = Color::from_rgb(0x888888);
  50. color = (edge == BorderEdge::Left || edge == BorderEdge::Top) ? top_left_color : bottom_right_color;
  51. } else if (border_style == CSS::LineStyle::Outset) {
  52. auto top_left_color = Color::from_rgb(0x888888);
  53. auto bottom_right_color = Color::from_rgb(0x5a5a5a);
  54. color = (edge == BorderEdge::Left || edge == BorderEdge::Top) ? top_left_color : bottom_right_color;
  55. }
  56. auto gfx_line_style = Gfx::Painter::LineStyle::Solid;
  57. if (border_style == CSS::LineStyle::Dotted)
  58. gfx_line_style = Gfx::Painter::LineStyle::Dotted;
  59. if (border_style == CSS::LineStyle::Dashed)
  60. gfx_line_style = Gfx::Painter::LineStyle::Dashed;
  61. if (gfx_line_style != Gfx::Painter::LineStyle::Solid) {
  62. switch (edge) {
  63. case BorderEdge::Top:
  64. p1.translate_by(int_width / 2, int_width / 2);
  65. p2.translate_by(-int_width / 2, int_width / 2);
  66. break;
  67. case BorderEdge::Right:
  68. p1.translate_by(-int_width / 2, int_width / 2);
  69. p2.translate_by(-int_width / 2, -int_width / 2);
  70. break;
  71. case BorderEdge::Bottom:
  72. p1.translate_by(int_width / 2, -int_width / 2);
  73. p2.translate_by(-int_width / 2, -int_width / 2);
  74. break;
  75. case BorderEdge::Left:
  76. p1.translate_by(int_width / 2, int_width / 2);
  77. p2.translate_by(int_width / 2, -int_width / 2);
  78. break;
  79. }
  80. context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, int_width, gfx_line_style);
  81. return;
  82. }
  83. auto draw_line = [&](auto& p1, auto& p2) {
  84. context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, 1, gfx_line_style);
  85. };
  86. float p1_step = 0;
  87. float p2_step = 0;
  88. bool has_top_left_radius = !style.border_top_left_radius().is_undefined();
  89. bool has_top_right_radius = !style.border_top_right_radius().is_undefined();
  90. bool has_bottom_left_radius = !style.border_bottom_left_radius().is_undefined();
  91. bool has_bottom_right_radius = !style.border_bottom_right_radius().is_undefined();
  92. switch (edge) {
  93. case BorderEdge::Top:
  94. p1_step = has_top_left_radius ? 0 : style.border_left().width / (float)int_width;
  95. p2_step = has_top_right_radius ? 0 : style.border_right().width / (float)int_width;
  96. for (int i = 0; i < int_width; ++i) {
  97. draw_line(p1, p2);
  98. p1.translate_by(p1_step, 1);
  99. p2.translate_by(-p2_step, 1);
  100. }
  101. break;
  102. case BorderEdge::Right:
  103. p1_step = has_top_right_radius ? 0 : style.border_top().width / (float)int_width;
  104. p2_step = has_bottom_right_radius ? 0 : style.border_bottom().width / (float)int_width;
  105. for (int i = int_width - 1; i >= 0; --i) {
  106. draw_line(p1, p2);
  107. p1.translate_by(-1, p1_step);
  108. p2.translate_by(-1, -p2_step);
  109. }
  110. break;
  111. case BorderEdge::Bottom:
  112. p1_step = has_bottom_left_radius ? 0 : style.border_left().width / (float)int_width;
  113. p2_step = has_bottom_right_radius ? 0 : style.border_right().width / (float)int_width;
  114. for (int i = int_width - 1; i >= 0; --i) {
  115. draw_line(p1, p2);
  116. p1.translate_by(p1_step, -1);
  117. p2.translate_by(-p2_step, -1);
  118. }
  119. break;
  120. case BorderEdge::Left:
  121. p1_step = has_top_left_radius ? 0 : style.border_top().width / (float)int_width;
  122. p2_step = has_bottom_left_radius ? 0 : style.border_bottom().width / (float)int_width;
  123. for (int i = 0; i < int_width; ++i) {
  124. draw_line(p1, p2);
  125. p1.translate_by(1, p1_step);
  126. p2.translate_by(1, -p2_step);
  127. }
  128. break;
  129. }
  130. }
  131. }