LinearGradientStyleValue.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  4. * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  5. * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include "LinearGradientStyleValue.h"
  10. #include <LibWeb/CSS/Serialize.h>
  11. namespace Web::CSS {
  12. // FIXME: Temporary until AbstractImageStyleValue.h exists. (And the Serialize.h include above.)
  13. static ErrorOr<void> serialize_color_stop_list(StringBuilder& builder, auto const& color_stop_list)
  14. {
  15. bool first = true;
  16. for (auto const& element : color_stop_list) {
  17. if (!first)
  18. TRY(builder.try_append(", "sv));
  19. if (element.transition_hint.has_value())
  20. TRY(builder.try_appendff("{}, "sv, TRY(element.transition_hint->value.to_string())));
  21. TRY(serialize_a_srgb_value(builder, element.color_stop.color));
  22. for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) {
  23. if (position->has_value())
  24. TRY(builder.try_appendff(" {}"sv, TRY((*position)->to_string())));
  25. }
  26. first = false;
  27. }
  28. return {};
  29. }
  30. ErrorOr<String> LinearGradientStyleValue::to_string() const
  31. {
  32. StringBuilder builder;
  33. auto side_or_corner_to_string = [](SideOrCorner value) {
  34. switch (value) {
  35. case SideOrCorner::Top:
  36. return "top"sv;
  37. case SideOrCorner::Bottom:
  38. return "bottom"sv;
  39. case SideOrCorner::Left:
  40. return "left"sv;
  41. case SideOrCorner::Right:
  42. return "right"sv;
  43. case SideOrCorner::TopLeft:
  44. return "top left"sv;
  45. case SideOrCorner::TopRight:
  46. return "top right"sv;
  47. case SideOrCorner::BottomLeft:
  48. return "bottom left"sv;
  49. case SideOrCorner::BottomRight:
  50. return "bottom right"sv;
  51. default:
  52. VERIFY_NOT_REACHED();
  53. }
  54. };
  55. if (m_properties.gradient_type == GradientType::WebKit)
  56. TRY(builder.try_append("-webkit-"sv));
  57. if (is_repeating())
  58. TRY(builder.try_append("repeating-"sv));
  59. TRY(builder.try_append("linear-gradient("sv));
  60. TRY(m_properties.direction.visit(
  61. [&](SideOrCorner side_or_corner) -> ErrorOr<void> {
  62. return builder.try_appendff("{}{}, "sv, m_properties.gradient_type == GradientType::Standard ? "to "sv : ""sv, side_or_corner_to_string(side_or_corner));
  63. },
  64. [&](Angle const& angle) -> ErrorOr<void> {
  65. return builder.try_appendff("{}, "sv, TRY(angle.to_string()));
  66. }));
  67. TRY(serialize_color_stop_list(builder, m_properties.color_stop_list));
  68. TRY(builder.try_append(")"sv));
  69. return builder.to_string();
  70. }
  71. bool LinearGradientStyleValue::equals(StyleValue const& other_) const
  72. {
  73. if (type() != other_.type())
  74. return false;
  75. auto& other = other_.as_linear_gradient();
  76. return m_properties == other.m_properties;
  77. }
  78. float LinearGradientStyleValue::angle_degrees(CSSPixelSize gradient_size) const
  79. {
  80. auto corner_angle_degrees = [&] {
  81. return static_cast<float>(atan2(gradient_size.height().value(), gradient_size.width().value())) * 180 / AK::Pi<float>;
  82. };
  83. return m_properties.direction.visit(
  84. [&](SideOrCorner side_or_corner) {
  85. auto angle = [&] {
  86. switch (side_or_corner) {
  87. case SideOrCorner::Top:
  88. return 0.0f;
  89. case SideOrCorner::Bottom:
  90. return 180.0f;
  91. case SideOrCorner::Left:
  92. return 270.0f;
  93. case SideOrCorner::Right:
  94. return 90.0f;
  95. case SideOrCorner::TopRight:
  96. return corner_angle_degrees();
  97. case SideOrCorner::BottomLeft:
  98. return corner_angle_degrees() + 180.0f;
  99. case SideOrCorner::TopLeft:
  100. return -corner_angle_degrees();
  101. case SideOrCorner::BottomRight:
  102. return -(corner_angle_degrees() + 180.0f);
  103. default:
  104. VERIFY_NOT_REACHED();
  105. }
  106. }();
  107. // Note: For unknowable reasons the angles are opposite on the -webkit- version
  108. if (m_properties.gradient_type == GradientType::WebKit)
  109. return angle + 180.0f;
  110. return angle;
  111. },
  112. [&](Angle const& angle) {
  113. return angle.to_degrees();
  114. });
  115. }
  116. void LinearGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize size) const
  117. {
  118. if (m_resolved.has_value() && m_resolved->size == size)
  119. return;
  120. m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size };
  121. }
  122. void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const
  123. {
  124. VERIFY(m_resolved.has_value());
  125. Painting::paint_linear_gradient(context, dest_rect, m_resolved->data);
  126. }
  127. }