/* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Tobias Christiansen * Copyright (c) 2021-2023, Sam Atkins * Copyright (c) 2022-2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace Web::CSS { class AbstractImageStyleValue : public CSSStyleValue { public: using CSSStyleValue::CSSStyleValue; virtual Optional natural_width() const { return {}; } virtual Optional natural_height() const { return {}; } virtual Optional natural_aspect_ratio() const { auto width = natural_width(); auto height = natural_height(); if (width.has_value() && height.has_value()) return *width / *height; return {}; } virtual void load_any_resources(DOM::Document&) { } virtual void resolve_for_size(Layout::NodeWithStyle const&, CSSPixelSize) const { } virtual bool is_paintable() const = 0; virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering) const = 0; virtual Optional color_if_single_pixel_bitmap() const { return {}; } }; // And now, some gradient related things. Maybe these should live somewhere else. enum class GradientRepeating { Yes, No }; enum class GradientSpace : u8 { sRGB, sRGBLinear, DisplayP3, A98RGB, ProPhotoRGB, Rec2020, Lab, OKLab, XYZD50, XYZD65, HSL, HWB, LCH, OKLCH, }; enum class HueMethod : u8 { Shorter, Longer, Increasing, Decreasing, }; struct InterpolationMethod { GradientSpace color_space; HueMethod hue_method = HueMethod::Shorter; String to_string() const { StringBuilder builder; switch (color_space) { case GradientSpace::OKLab: builder.append("in oklab"sv); break; case GradientSpace::sRGB: builder.append("in srgb"sv); break; case GradientSpace::sRGBLinear: builder.append("in srgb-linear"sv); break; case GradientSpace::DisplayP3: builder.append("in display-p3"sv); break; case GradientSpace::A98RGB: builder.append("in a98-rgb"sv); break; case GradientSpace::ProPhotoRGB: builder.append("in prophoto-rgb"sv); break; case GradientSpace::Rec2020: builder.append("in rec2020"sv); break; case GradientSpace::Lab: builder.append("in lab"sv); break; case GradientSpace::XYZD50: builder.append("in xyz-d50"sv); break; case GradientSpace::XYZD65: builder.append("in xyz-d65"sv); break; case GradientSpace::HSL: builder.append("in hsl"sv); break; case GradientSpace::HWB: builder.append("in hwb"sv); break; case GradientSpace::LCH: builder.append("in lch"sv); break; case GradientSpace::OKLCH: builder.append("in oklch"sv); break; } switch (hue_method) { case HueMethod::Shorter: // "shorter" is the default value and isn't serialized break; case HueMethod::Longer: builder.append(" longer hue"sv); break; case HueMethod::Increasing: builder.append(" increasing hue"sv); break; case HueMethod::Decreasing: builder.append(" decreasing hue"sv); break; } return MUST(builder.to_string()); } static GradientSpace default_color_space(ColorSyntax color_syntax) { if (color_syntax == ColorSyntax::Legacy) return GradientSpace::sRGB; return GradientSpace::OKLab; } bool operator==(InterpolationMethod const&) const = default; }; template struct ColorStopListElement { using PositionType = TPosition; struct ColorHint { TPosition value; inline bool operator==(ColorHint const&) const = default; }; Optional transition_hint; struct ColorStop { RefPtr color; Optional position; Optional second_position = {}; inline bool operator==(ColorStop const&) const = default; } color_stop; inline bool operator==(ColorStopListElement const&) const = default; }; using LinearColorStopListElement = ColorStopListElement; using AngularColorStopListElement = ColorStopListElement; static void serialize_color_stop_list(StringBuilder& builder, auto const& color_stop_list, CSSStyleValue::SerializationMode mode) { bool first = true; for (auto const& element : color_stop_list) { if (!first) builder.append(", "sv); if (element.transition_hint.has_value()) builder.appendff("{}, "sv, element.transition_hint->value.to_string()); builder.append(element.color_stop.color->to_string(mode)); for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) { if (position->has_value()) builder.appendff(" {}"sv, (*position)->to_string()); } first = false; } } }