ShadowPainting.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/NumericLimits.h>
  9. #include <LibGfx/DisjointRectSet.h>
  10. #include <LibGfx/Filters/StackBlurFilter.h>
  11. #include <LibGfx/Font/Font.h>
  12. #include <LibGfx/Painter.h>
  13. #include <LibWeb/Layout/LineBoxFragment.h>
  14. #include <LibWeb/Layout/Node.h>
  15. #include <LibWeb/Painting/BorderPainting.h>
  16. #include <LibWeb/Painting/BorderRadiusCornerClipper.h>
  17. #include <LibWeb/Painting/PaintBoxShadowParams.h>
  18. #include <LibWeb/Painting/PaintContext.h>
  19. #include <LibWeb/Painting/PaintableBox.h>
  20. #include <LibWeb/Painting/ShadowPainting.h>
  21. namespace Web::Painting {
  22. void paint_inner_box_shadow(Gfx::Painter& painter, PaintBoxShadowParams params)
  23. {
  24. auto device_content_rect = params.device_content_rect;
  25. int offset_x = params.offset_x;
  26. int offset_y = params.offset_y;
  27. int blur_radius = params.blur_radius;
  28. int spread_distance = params.spread_distance;
  29. auto shadows_bitmap_rect = device_content_rect.inflated(
  30. blur_radius + offset_y,
  31. blur_radius + abs(offset_x),
  32. blur_radius + abs(offset_y),
  33. blur_radius + offset_x);
  34. auto shadows_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, shadows_bitmap_rect.size());
  35. if (shadows_bitmap.is_error()) {
  36. dbgln("Unable to allocate temporary bitmap {} for box-shadow rendering: {}", device_content_rect, shadows_bitmap.error());
  37. return;
  38. }
  39. auto shadow_bitmap = shadows_bitmap.release_value();
  40. Gfx::Painter shadow_painter { *shadow_bitmap };
  41. Gfx::AntiAliasingPainter shadow_aa_painter { shadow_painter };
  42. auto device_content_rect_int = device_content_rect;
  43. auto origin_device_content_rect = device_content_rect_int.translated(-device_content_rect_int.x(), -device_content_rect_int.y());
  44. auto outer_shadow_rect = origin_device_content_rect.translated({ offset_x + blur_radius, offset_y + blur_radius });
  45. auto spread_distance_value = spread_distance;
  46. auto inner_shadow_rect = outer_shadow_rect.inflated(-spread_distance_value, -spread_distance_value, -spread_distance_value, -spread_distance_value);
  47. outer_shadow_rect.inflate(
  48. blur_radius + offset_y,
  49. blur_radius + abs(offset_x),
  50. blur_radius + abs(offset_y),
  51. blur_radius + offset_x);
  52. auto top_left_corner = params.corner_radii.top_left;
  53. auto top_right_corner = params.corner_radii.top_right;
  54. auto bottom_right_corner = params.corner_radii.bottom_right;
  55. auto bottom_left_corner = params.corner_radii.bottom_left;
  56. shadow_painter.fill_rect(outer_shadow_rect, params.color.with_alpha(0xff));
  57. if (params.corner_radii.has_any_radius()) {
  58. shadow_aa_painter.fill_rect_with_rounded_corners(inner_shadow_rect, params.color.with_alpha(0xff),
  59. top_left_corner, top_right_corner, bottom_right_corner, bottom_left_corner,
  60. Gfx::AntiAliasingPainter::BlendMode::AlphaSubtract);
  61. } else {
  62. shadow_painter.clear_rect(inner_shadow_rect, Color::Transparent);
  63. }
  64. Gfx::StackBlurFilter filter(*shadow_bitmap);
  65. filter.process_rgba(blur_radius, params.color);
  66. Gfx::PainterStateSaver save { painter };
  67. painter.add_clip_rect(device_content_rect_int);
  68. painter.blit({ device_content_rect_int.left() - blur_radius, device_content_rect_int.top() - blur_radius },
  69. *shadow_bitmap, shadow_bitmap->rect(), params.color.alpha() / 255.);
  70. }
  71. struct OuterBoxShadowMetrics {
  72. Gfx::IntRect shadow_bitmap_rect;
  73. Gfx::IntRect non_blurred_shadow_rect;
  74. Gfx::IntRect inner_bounding_rect;
  75. int blurred_edge_thickness;
  76. int double_radius;
  77. int blur_radius;
  78. Gfx::IntRect top_left_corner_rect;
  79. Gfx::IntRect top_right_corner_rect;
  80. Gfx::IntRect bottom_right_corner_rect;
  81. Gfx::IntRect bottom_left_corner_rect;
  82. Gfx::IntPoint top_left_corner_blit_pos;
  83. Gfx::IntPoint top_right_corner_blit_pos;
  84. Gfx::IntPoint bottom_right_corner_blit_pos;
  85. Gfx::IntPoint bottom_left_corner_blit_pos;
  86. Gfx::IntSize top_left_corner_size;
  87. Gfx::IntSize top_right_corner_size;
  88. Gfx::IntSize bottom_right_corner_size;
  89. Gfx::IntSize bottom_left_corner_size;
  90. int left_start;
  91. int top_start;
  92. int right_start;
  93. int bottom_start;
  94. Gfx::IntRect left_edge_rect;
  95. Gfx::IntRect right_edge_rect;
  96. Gfx::IntRect top_edge_rect;
  97. Gfx::IntRect bottom_edge_rect;
  98. CornerRadius top_left_shadow_corner;
  99. CornerRadius top_right_shadow_corner;
  100. CornerRadius bottom_right_shadow_corner;
  101. CornerRadius bottom_left_shadow_corner;
  102. };
  103. static OuterBoxShadowMetrics get_outer_box_shadow_configuration(PaintBoxShadowParams params)
  104. {
  105. auto device_content_rect = params.device_content_rect;
  106. auto top_left_corner = params.corner_radii.top_left;
  107. auto top_right_corner = params.corner_radii.top_right;
  108. auto bottom_right_corner = params.corner_radii.bottom_right;
  109. auto bottom_left_corner = params.corner_radii.bottom_left;
  110. auto offset_x = params.offset_x;
  111. auto offset_y = params.offset_y;
  112. auto blur_radius = params.blur_radius;
  113. auto spread_distance = params.spread_distance;
  114. // Our blur cannot handle radii over 255 so there's no point trying (255 is silly big anyway)
  115. blur_radius = clamp(blur_radius, 0, 255);
  116. auto top_left_shadow_corner = top_left_corner;
  117. auto top_right_shadow_corner = top_right_corner;
  118. auto bottom_right_shadow_corner = bottom_right_corner;
  119. auto bottom_left_shadow_corner = bottom_left_corner;
  120. auto spread_corner = [&](auto& corner) {
  121. if (corner) {
  122. corner.horizontal_radius += spread_distance;
  123. corner.vertical_radius += spread_distance;
  124. }
  125. };
  126. spread_corner(top_left_shadow_corner);
  127. spread_corner(top_right_shadow_corner);
  128. spread_corner(bottom_right_shadow_corner);
  129. spread_corner(bottom_left_shadow_corner);
  130. auto expansion = spread_distance - (blur_radius * 2);
  131. Gfx::IntRect inner_bounding_rect = {
  132. device_content_rect.x() + offset_x - expansion,
  133. device_content_rect.y() + offset_y - expansion,
  134. device_content_rect.width() + 2 * expansion,
  135. device_content_rect.height() + 2 * expansion
  136. };
  137. // Calculating and blurring the box-shadow full size is expensive, and wasteful - aside from the corners,
  138. // all vertical strips of the shadow are identical, and the same goes for horizontal ones.
  139. // So instead, we generate a shadow bitmap that is just large enough to include the corners and 1px of
  140. // non-corner, and then we repeatedly blit sections of it. This is similar to a NinePatch on Android.
  141. auto double_radius = blur_radius * 2;
  142. auto blurred_edge_thickness = blur_radius * 4;
  143. auto default_corner_size = Gfx::IntSize { double_radius, double_radius };
  144. auto top_left_corner_size = top_left_shadow_corner ? top_left_shadow_corner.as_rect().size() : default_corner_size;
  145. auto top_right_corner_size = top_right_shadow_corner ? top_right_shadow_corner.as_rect().size() : default_corner_size;
  146. auto bottom_left_corner_size = bottom_left_shadow_corner ? bottom_left_shadow_corner.as_rect().size() : default_corner_size;
  147. auto bottom_right_corner_size = bottom_right_shadow_corner ? bottom_right_shadow_corner.as_rect().size() : default_corner_size;
  148. auto non_blurred_shadow_rect = device_content_rect.inflated(spread_distance, spread_distance, spread_distance, spread_distance);
  149. auto max_edge_width = non_blurred_shadow_rect.width() / 2;
  150. auto max_edge_height = non_blurred_shadow_rect.height() / 2;
  151. auto extra_edge_width = non_blurred_shadow_rect.width() % 2;
  152. auto extra_edge_height = non_blurred_shadow_rect.height() % 2;
  153. auto clip_corner_size = [&](auto& size, auto const& corner, int x_bonus = 0, int y_bonus = 0) {
  154. auto max_x = max_edge_width + x_bonus;
  155. auto max_y = max_edge_height + y_bonus;
  156. auto min_x = max(corner.horizontal_radius, min(double_radius, max_x));
  157. auto min_y = max(corner.vertical_radius, min(double_radius, max_y));
  158. if (min_x <= max_x)
  159. size.set_width(clamp(size.width(), min_x, max_x));
  160. if (min_y <= max_y)
  161. size.set_height(clamp(size.height(), min_y, max_y));
  162. };
  163. clip_corner_size(top_left_corner_size, top_left_corner, extra_edge_width, extra_edge_height);
  164. clip_corner_size(top_right_corner_size, top_right_corner, 0, extra_edge_height);
  165. clip_corner_size(bottom_left_corner_size, bottom_left_corner, extra_edge_width);
  166. clip_corner_size(bottom_right_corner_size, bottom_right_corner);
  167. auto shadow_bitmap_rect = Gfx::IntRect {
  168. 0, 0,
  169. max(max(
  170. top_left_corner_size.width() + top_right_corner_size.width(),
  171. bottom_left_corner_size.width() + bottom_right_corner_size.width()),
  172. max(top_left_corner_size.width() + bottom_right_corner_size.width(),
  173. bottom_left_corner_size.width() + top_right_corner_size.width()))
  174. + 1 + blurred_edge_thickness,
  175. max(max(
  176. top_left_corner_size.height() + bottom_left_corner_size.height(),
  177. top_right_corner_size.height() + bottom_right_corner_size.height()),
  178. max(top_left_corner_size.height() + bottom_right_corner_size.height(),
  179. bottom_left_corner_size.height() + top_right_corner_size.height()))
  180. + 1 + blurred_edge_thickness
  181. };
  182. auto top_left_corner_rect = Gfx::IntRect {
  183. 0, 0,
  184. top_left_corner_size.width() + double_radius,
  185. top_left_corner_size.height() + double_radius
  186. };
  187. auto top_right_corner_rect = Gfx::IntRect {
  188. shadow_bitmap_rect.width() - (top_right_corner_size.width() + double_radius), 0,
  189. top_right_corner_size.width() + double_radius,
  190. top_right_corner_size.height() + double_radius
  191. };
  192. auto bottom_right_corner_rect = Gfx::IntRect {
  193. shadow_bitmap_rect.width() - (bottom_right_corner_size.width() + double_radius),
  194. shadow_bitmap_rect.height() - (bottom_right_corner_size.height() + double_radius),
  195. bottom_right_corner_size.width() + double_radius,
  196. bottom_right_corner_size.height() + double_radius
  197. };
  198. auto bottom_left_corner_rect = Gfx::IntRect {
  199. 0, shadow_bitmap_rect.height() - (bottom_left_corner_size.height() + double_radius),
  200. bottom_left_corner_size.width() + double_radius,
  201. bottom_left_corner_size.height() + double_radius
  202. };
  203. auto horizontal_edge_width = min(max_edge_height, double_radius) + double_radius;
  204. auto vertical_edge_width = min(max_edge_width, double_radius) + double_radius;
  205. auto horizontal_top_edge_width = min(max_edge_height + extra_edge_height, double_radius) + double_radius;
  206. auto vertical_left_edge_width = min(max_edge_width + extra_edge_width, double_radius) + double_radius;
  207. Gfx::IntRect left_edge_rect { 0, top_left_corner_rect.height(), vertical_left_edge_width, 1 };
  208. Gfx::IntRect right_edge_rect { shadow_bitmap_rect.width() - vertical_edge_width, top_right_corner_rect.height(), vertical_edge_width, 1 };
  209. Gfx::IntRect top_edge_rect { top_left_corner_rect.width(), 0, 1, horizontal_top_edge_width };
  210. Gfx::IntRect bottom_edge_rect { bottom_left_corner_rect.width(), shadow_bitmap_rect.height() - horizontal_edge_width, 1, horizontal_edge_width };
  211. auto left_start = inner_bounding_rect.left() - blurred_edge_thickness;
  212. auto right_start = inner_bounding_rect.left() + inner_bounding_rect.width() + (blurred_edge_thickness - vertical_edge_width);
  213. auto top_start = inner_bounding_rect.top() - blurred_edge_thickness;
  214. auto bottom_start = inner_bounding_rect.top() + inner_bounding_rect.height() + (blurred_edge_thickness - horizontal_edge_width);
  215. auto top_left_corner_blit_pos = inner_bounding_rect.top_left().translated(-blurred_edge_thickness, -blurred_edge_thickness);
  216. auto top_right_corner_blit_pos = inner_bounding_rect.top_right().translated(-top_right_corner_size.width() + double_radius, -blurred_edge_thickness);
  217. auto bottom_left_corner_blit_pos = inner_bounding_rect.bottom_left().translated(-blurred_edge_thickness, -bottom_left_corner_size.height() + double_radius);
  218. auto bottom_right_corner_blit_pos = inner_bounding_rect.bottom_right().translated(-bottom_right_corner_size.width() + double_radius, -bottom_right_corner_size.height() + double_radius);
  219. return OuterBoxShadowMetrics {
  220. .shadow_bitmap_rect = shadow_bitmap_rect,
  221. .non_blurred_shadow_rect = non_blurred_shadow_rect,
  222. .inner_bounding_rect = inner_bounding_rect,
  223. .blurred_edge_thickness = blurred_edge_thickness,
  224. .double_radius = double_radius,
  225. .blur_radius = blur_radius,
  226. .top_left_corner_rect = top_left_corner_rect,
  227. .top_right_corner_rect = top_right_corner_rect,
  228. .bottom_right_corner_rect = bottom_right_corner_rect,
  229. .bottom_left_corner_rect = bottom_left_corner_rect,
  230. .top_left_corner_blit_pos = top_left_corner_blit_pos,
  231. .top_right_corner_blit_pos = top_right_corner_blit_pos,
  232. .bottom_right_corner_blit_pos = bottom_right_corner_blit_pos,
  233. .bottom_left_corner_blit_pos = bottom_left_corner_blit_pos,
  234. .top_left_corner_size = top_left_corner_size,
  235. .top_right_corner_size = top_right_corner_size,
  236. .bottom_right_corner_size = bottom_right_corner_size,
  237. .bottom_left_corner_size = bottom_left_corner_size,
  238. .left_start = left_start,
  239. .top_start = top_start,
  240. .right_start = right_start,
  241. .bottom_start = bottom_start,
  242. .left_edge_rect = left_edge_rect,
  243. .right_edge_rect = right_edge_rect,
  244. .top_edge_rect = top_edge_rect,
  245. .bottom_edge_rect = bottom_edge_rect,
  246. .top_left_shadow_corner = top_left_shadow_corner,
  247. .top_right_shadow_corner = top_right_shadow_corner,
  248. .bottom_right_shadow_corner = bottom_right_shadow_corner,
  249. .bottom_left_shadow_corner = bottom_left_shadow_corner,
  250. };
  251. }
  252. Gfx::IntRect get_outer_box_shadow_bounding_rect(PaintBoxShadowParams params)
  253. {
  254. auto shadow_config = get_outer_box_shadow_configuration(params);
  255. auto const& top_left_corner_blit_pos = shadow_config.top_left_corner_blit_pos;
  256. auto const& top_right_corner_blit_pos = shadow_config.top_right_corner_blit_pos;
  257. auto const& bottom_left_corner_blit_pos = shadow_config.bottom_left_corner_blit_pos;
  258. auto const& top_right_corner_rect = shadow_config.top_right_corner_rect;
  259. auto const& bottom_left_corner_rect = shadow_config.bottom_left_corner_rect;
  260. return Gfx::IntRect {
  261. top_left_corner_blit_pos,
  262. { top_right_corner_blit_pos.x() - top_left_corner_blit_pos.x() + top_right_corner_rect.width(),
  263. bottom_left_corner_blit_pos.y() - top_left_corner_blit_pos.y() + bottom_left_corner_rect.height() }
  264. };
  265. }
  266. void paint_outer_box_shadow(Gfx::Painter& painter, PaintBoxShadowParams params)
  267. {
  268. auto const& device_content_rect = params.device_content_rect;
  269. auto const& top_left_corner = params.corner_radii.top_left;
  270. auto const& top_right_corner = params.corner_radii.top_right;
  271. auto const& bottom_right_corner = params.corner_radii.bottom_right;
  272. auto const& bottom_left_corner = params.corner_radii.bottom_left;
  273. auto offset_x = params.offset_x;
  274. auto offset_y = params.offset_y;
  275. auto shadow_config = get_outer_box_shadow_configuration(params);
  276. auto const& shadow_bitmap_rect = shadow_config.shadow_bitmap_rect;
  277. auto const& non_blurred_shadow_rect = shadow_config.non_blurred_shadow_rect;
  278. auto const& inner_bounding_rect = shadow_config.inner_bounding_rect;
  279. auto const& blurred_edge_thickness = shadow_config.blurred_edge_thickness;
  280. auto const& double_radius = shadow_config.double_radius;
  281. auto const& blur_radius = shadow_config.blur_radius;
  282. auto const& top_left_corner_rect = shadow_config.top_left_corner_rect;
  283. auto const& top_right_corner_rect = shadow_config.top_right_corner_rect;
  284. auto const& bottom_right_corner_rect = shadow_config.bottom_right_corner_rect;
  285. auto const& bottom_left_corner_rect = shadow_config.bottom_left_corner_rect;
  286. auto const& top_left_corner_blit_pos = shadow_config.top_left_corner_blit_pos;
  287. auto const& top_right_corner_blit_pos = shadow_config.top_right_corner_blit_pos;
  288. auto const& bottom_right_corner_blit_pos = shadow_config.bottom_right_corner_blit_pos;
  289. auto const& bottom_left_corner_blit_pos = shadow_config.bottom_left_corner_blit_pos;
  290. auto const& top_left_corner_size = shadow_config.top_left_corner_size;
  291. auto const& top_right_corner_size = shadow_config.top_right_corner_size;
  292. auto const& bottom_right_corner_size = shadow_config.bottom_right_corner_size;
  293. auto const& bottom_left_corner_size = shadow_config.bottom_left_corner_size;
  294. auto const& left_start = shadow_config.left_start;
  295. auto const& top_start = shadow_config.top_start;
  296. auto const& right_start = shadow_config.right_start;
  297. auto const& bottom_start = shadow_config.bottom_start;
  298. auto const& left_edge_rect = shadow_config.left_edge_rect;
  299. auto const& right_edge_rect = shadow_config.right_edge_rect;
  300. auto const& top_edge_rect = shadow_config.top_edge_rect;
  301. auto const& bottom_edge_rect = shadow_config.bottom_edge_rect;
  302. auto const& top_left_shadow_corner = shadow_config.top_left_shadow_corner;
  303. auto const& top_right_shadow_corner = shadow_config.top_right_shadow_corner;
  304. auto const& bottom_right_shadow_corner = shadow_config.bottom_right_shadow_corner;
  305. auto const& bottom_left_shadow_corner = shadow_config.bottom_left_shadow_corner;
  306. auto fill_rect_masked = [](auto& painter, auto fill_rect, auto mask_rect, auto color) {
  307. Gfx::DisjointRectSet<int> rect_set;
  308. rect_set.add(fill_rect);
  309. auto shattered = rect_set.shatter(mask_rect);
  310. for (auto& rect : shattered.rects())
  311. painter.fill_rect(rect, color);
  312. };
  313. // If there's no blurring, nor rounded corners, we can save a lot of effort.
  314. if (blur_radius == 0 && !params.corner_radii.has_any_radius()) {
  315. fill_rect_masked(painter, non_blurred_shadow_rect.translated(offset_x, offset_y), device_content_rect, params.color);
  316. return;
  317. }
  318. auto paint_shadow_infill = [&] {
  319. if (!params.corner_radii.has_any_radius())
  320. return painter.fill_rect(inner_bounding_rect, params.color);
  321. auto top_left_inner_width = top_left_corner_rect.width() - blurred_edge_thickness;
  322. auto top_left_inner_height = top_left_corner_rect.height() - blurred_edge_thickness;
  323. auto top_right_inner_width = top_right_corner_rect.width() - blurred_edge_thickness;
  324. auto top_right_inner_height = top_right_corner_rect.height() - blurred_edge_thickness;
  325. auto bottom_right_inner_width = bottom_right_corner_rect.width() - blurred_edge_thickness;
  326. auto bottom_right_inner_height = bottom_right_corner_rect.height() - blurred_edge_thickness;
  327. auto bottom_left_inner_width = bottom_left_corner_rect.width() - blurred_edge_thickness;
  328. auto bottom_left_inner_height = bottom_left_corner_rect.height() - blurred_edge_thickness;
  329. Gfx::IntRect top_rect {
  330. inner_bounding_rect.x() + top_left_inner_width,
  331. inner_bounding_rect.y(),
  332. inner_bounding_rect.width() - top_left_inner_width - top_right_inner_width,
  333. top_left_inner_height
  334. };
  335. Gfx::IntRect right_rect {
  336. inner_bounding_rect.x() + inner_bounding_rect.width() - top_right_inner_width,
  337. inner_bounding_rect.y() + top_right_inner_height,
  338. top_right_inner_width,
  339. inner_bounding_rect.height() - top_right_inner_height - bottom_right_inner_height
  340. };
  341. Gfx::IntRect bottom_rect {
  342. inner_bounding_rect.x() + bottom_left_inner_width,
  343. inner_bounding_rect.y() + inner_bounding_rect.height() - bottom_right_inner_height,
  344. inner_bounding_rect.width() - bottom_left_inner_width - bottom_right_inner_width,
  345. bottom_right_inner_height
  346. };
  347. Gfx::IntRect left_rect {
  348. inner_bounding_rect.x(),
  349. inner_bounding_rect.y() + top_left_inner_height,
  350. bottom_left_inner_width,
  351. inner_bounding_rect.height() - top_left_inner_height - bottom_left_inner_height
  352. };
  353. Gfx::IntRect inner = {
  354. left_rect.x() + left_rect.width(),
  355. left_rect.y(),
  356. inner_bounding_rect.width() - left_rect.width() - right_rect.width(),
  357. inner_bounding_rect.height() - top_rect.height() - bottom_rect.height()
  358. };
  359. painter.fill_rect(top_rect, params.color);
  360. painter.fill_rect(right_rect, params.color);
  361. painter.fill_rect(bottom_rect, params.color);
  362. painter.fill_rect(left_rect, params.color);
  363. painter.fill_rect(inner, params.color);
  364. };
  365. auto shadows_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, shadow_bitmap_rect.size());
  366. if (shadows_bitmap.is_error()) {
  367. dbgln("Unable to allocate temporary bitmap {} for box-shadow rendering: {}", shadow_bitmap_rect, shadows_bitmap.error());
  368. return;
  369. }
  370. auto shadow_bitmap = shadows_bitmap.release_value();
  371. Gfx::Painter corner_painter { *shadow_bitmap };
  372. Gfx::AntiAliasingPainter aa_corner_painter { corner_painter };
  373. aa_corner_painter.fill_rect_with_rounded_corners(
  374. shadow_bitmap_rect.shrunken(double_radius, double_radius, double_radius, double_radius),
  375. params.color, top_left_shadow_corner, top_right_shadow_corner, bottom_right_shadow_corner, bottom_left_shadow_corner);
  376. Gfx::StackBlurFilter filter(*shadow_bitmap);
  377. filter.process_rgba(blur_radius, params.color);
  378. auto paint_shadow = [&](Gfx::IntRect clip_rect) {
  379. Gfx::PainterStateSaver save { painter };
  380. painter.add_clip_rect(clip_rect);
  381. paint_shadow_infill();
  382. // Corners
  383. painter.blit(top_left_corner_blit_pos, shadow_bitmap, top_left_corner_rect);
  384. painter.blit(top_right_corner_blit_pos, shadow_bitmap, top_right_corner_rect);
  385. painter.blit(bottom_left_corner_blit_pos, shadow_bitmap, bottom_left_corner_rect);
  386. painter.blit(bottom_right_corner_blit_pos, shadow_bitmap, bottom_right_corner_rect);
  387. // Horizontal edges
  388. for (auto x = inner_bounding_rect.left() + (bottom_left_corner_size.width() - double_radius); x < inner_bounding_rect.right() - (bottom_right_corner_size.width() - double_radius); ++x)
  389. painter.blit({ x, bottom_start }, shadow_bitmap, bottom_edge_rect);
  390. for (auto x = inner_bounding_rect.left() + (top_left_corner_size.width() - double_radius); x < inner_bounding_rect.right() - (top_right_corner_size.width() - double_radius); ++x)
  391. painter.blit({ x, top_start }, shadow_bitmap, top_edge_rect);
  392. // Vertical edges
  393. for (auto y = inner_bounding_rect.top() + (top_right_corner_size.height() - double_radius); y < inner_bounding_rect.bottom() - (bottom_right_corner_size.height() - double_radius); ++y)
  394. painter.blit({ right_start, y }, shadow_bitmap, right_edge_rect);
  395. for (auto y = inner_bounding_rect.top() + (top_left_corner_size.height() - double_radius); y < inner_bounding_rect.bottom() - (bottom_left_corner_size.height() - double_radius); ++y)
  396. painter.blit({ left_start, y }, shadow_bitmap, left_edge_rect);
  397. };
  398. // FIXME: Painter only lets us define a clip-rect which discards drawing outside of it, whereas here we want
  399. // a rect which discards drawing inside it. So, we run the draw operations 4 to 8 times with clip-rects
  400. // covering each side of the content_rect exactly once.
  401. // If we were painting a shadow without a border radius we'd want to clip everything inside the box below.
  402. // If painting a shadow with rounded corners (but still rectangular) we want to clip everything inside
  403. // the box except the corners. This gives us an upper bound of 8 shadow paints now :^(.
  404. // (However, this does not seem to be the costly part in profiling).
  405. //
  406. // ┌───┬────────┬───┐
  407. // │ │xxxxxxxx│ │
  408. // ├───┼────────┼───┤
  409. // │xxx│xxxxxxxx│xxx│
  410. // │xxx│xxxxxxxx│xxx│
  411. // │xxx│xxxxxxxx│xxx│
  412. // │xxx│xxxxxxxx│xxx│
  413. // │xxx│xxxxxxxx│xxx│
  414. // ├───┼────────┼───┤
  415. // │ │ xxxxxx │ │
  416. // └───┴────────┴───┘
  417. // How many times would you like to paint the shadow sir?
  418. // Yes.
  419. // FIXME: Could reduce the shadow paints from 8 to 4 for shadows with all corner radii 50%.
  420. // FIXME: We use this since we want the clip rect to include everything after a certain x or y.
  421. // Note: Using painter.target().width() or height() does not work, when the painter is a small
  422. // translated bitmap rather than full screen, as the clip rect may not intersect.
  423. constexpr auto really_large_number = NumericLimits<int>::max() / 2;
  424. // Everything above content_rect, including sides
  425. paint_shadow({ 0, 0, really_large_number, device_content_rect.top() });
  426. // Everything below content_rect, including sides
  427. paint_shadow({ 0, device_content_rect.bottom(), really_large_number, really_large_number });
  428. // Everything directly to the left of content_rect
  429. paint_shadow({ 0, device_content_rect.top(), device_content_rect.left(), device_content_rect.height() });
  430. // Everything directly to the right of content_rect
  431. paint_shadow({ device_content_rect.right(), device_content_rect.top(), really_large_number, device_content_rect.height() });
  432. if (top_left_corner) {
  433. // Inside the top left corner (the part outside the border radius)
  434. auto top_left = top_left_corner.as_rect().translated(device_content_rect.top_left());
  435. paint_shadow(top_left);
  436. }
  437. if (top_right_corner) {
  438. // Inside the top right corner (the part outside the border radius)
  439. auto top_right = top_right_corner.as_rect().translated(device_content_rect.top_right().translated(-top_right_corner.horizontal_radius, 0));
  440. paint_shadow(top_right);
  441. }
  442. if (bottom_right_corner) {
  443. // Inside the bottom right corner (the part outside the border radius)
  444. auto bottom_right = bottom_right_corner.as_rect().translated(device_content_rect.bottom_right().translated(-bottom_right_corner.horizontal_radius, -bottom_right_corner.vertical_radius));
  445. paint_shadow(bottom_right);
  446. }
  447. if (bottom_left_corner) {
  448. // Inside the bottom left corner (the part outside the border radius)
  449. auto bottom_left = bottom_left_corner.as_rect().translated(device_content_rect.bottom_left().translated(0, -bottom_left_corner.vertical_radius));
  450. paint_shadow(bottom_left);
  451. }
  452. }
  453. void paint_box_shadow(PaintContext& context,
  454. CSSPixelRect const& bordered_content_rect,
  455. CSSPixelRect const& borderless_content_rect,
  456. BordersData const& borders_data,
  457. BorderRadiiData const& border_radii,
  458. Vector<ShadowData> const& box_shadow_layers)
  459. {
  460. // Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse
  461. for (auto& box_shadow_data : box_shadow_layers.in_reverse()) {
  462. auto offset_x = context.rounded_device_pixels(box_shadow_data.offset_x);
  463. auto offset_y = context.rounded_device_pixels(box_shadow_data.offset_y);
  464. auto blur_radius = context.rounded_device_pixels(box_shadow_data.blur_radius);
  465. auto spread_distance = context.rounded_device_pixels(box_shadow_data.spread_distance);
  466. DevicePixelRect device_content_rect;
  467. if (box_shadow_data.placement == ShadowPlacement::Inner) {
  468. device_content_rect = context.rounded_device_rect(borderless_content_rect);
  469. } else {
  470. device_content_rect = context.rounded_device_rect(bordered_content_rect);
  471. }
  472. auto params = PaintBoxShadowParams {
  473. .color = box_shadow_data.color,
  474. .placement = box_shadow_data.placement,
  475. .corner_radii = CornerRadii {
  476. .top_left = border_radii.top_left.as_corner(context),
  477. .top_right = border_radii.top_right.as_corner(context),
  478. .bottom_right = border_radii.bottom_right.as_corner(context),
  479. .bottom_left = border_radii.bottom_left.as_corner(context) },
  480. .offset_x = offset_x.value(),
  481. .offset_y = offset_y.value(),
  482. .blur_radius = blur_radius.value(),
  483. .spread_distance = spread_distance.value(),
  484. .device_content_rect = device_content_rect.to_type<int>(),
  485. };
  486. if (box_shadow_data.placement == ShadowPlacement::Inner) {
  487. auto shrinked_border_radii = border_radii;
  488. shrinked_border_radii.shrink(borders_data.top.width, borders_data.right.width, borders_data.bottom.width, borders_data.left.width);
  489. ScopedCornerRadiusClip corner_clipper { context, device_content_rect, shrinked_border_radii, CornerClip::Outside };
  490. context.recording_painter().paint_inner_box_shadow_params(params);
  491. } else {
  492. ScopedCornerRadiusClip corner_clipper { context, device_content_rect, border_radii, CornerClip::Inside };
  493. context.recording_painter().paint_outer_box_shadow_params(params);
  494. }
  495. }
  496. }
  497. void paint_text_shadow(PaintContext& context, PaintableFragment const& fragment, Vector<ShadowData> const& shadow_layers)
  498. {
  499. if (shadow_layers.is_empty() || fragment.glyph_run().is_empty())
  500. return;
  501. auto fragment_width = context.enclosing_device_pixels(fragment.width()).value();
  502. auto fragment_height = context.enclosing_device_pixels(fragment.height()).value();
  503. auto draw_rect = context.enclosing_device_rect(fragment.absolute_rect()).to_type<int>();
  504. auto fragment_baseline = context.rounded_device_pixels(fragment.baseline()).value();
  505. Vector<Gfx::DrawGlyphOrEmoji> scaled_glyph_run;
  506. scaled_glyph_run.ensure_capacity(fragment.glyph_run().glyphs().size());
  507. for (auto glyph : fragment.glyph_run().glyphs()) {
  508. glyph.visit([&](auto& glyph) {
  509. glyph.font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(context.device_pixels_per_css_pixel()));
  510. glyph.position = glyph.position.scaled(context.device_pixels_per_css_pixel());
  511. });
  512. scaled_glyph_run.append(move(glyph));
  513. }
  514. // Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse
  515. for (auto& layer : shadow_layers.in_reverse()) {
  516. int offset_x = context.rounded_device_pixels(layer.offset_x).value();
  517. int offset_y = context.rounded_device_pixels(layer.offset_y).value();
  518. int blur_radius = context.rounded_device_pixels(layer.blur_radius).value();
  519. // Space around the painted text to allow it to blur.
  520. // FIXME: Include spread in this once we use that.
  521. int margin = blur_radius * 2;
  522. Gfx::IntRect text_rect {
  523. margin, margin,
  524. fragment_width, fragment_height
  525. };
  526. Gfx::IntRect bounding_rect {
  527. 0, 0,
  528. text_rect.width() + margin + margin,
  529. text_rect.height() + margin + margin
  530. };
  531. Gfx::IntPoint draw_location {
  532. draw_rect.x() + offset_x - margin,
  533. draw_rect.y() + offset_y - margin
  534. };
  535. context.recording_painter().paint_text_shadow(blur_radius, bounding_rect, text_rect, scaled_glyph_run, layer.color, fragment_baseline, draw_location);
  536. }
  537. }
  538. }