diff --git a/Libraries/LibGfx/BlendMode.h b/Libraries/LibGfx/BlendMode.h new file mode 100644 index 0000000000000..b47783b91e06d --- /dev/null +++ b/Libraries/LibGfx/BlendMode.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025, Stefan Vukanović + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace Gfx { + +enum class BlendMode { + Normal, + Darken, + Multiply, + ColorBurn, + Lighten, + Screen, + ColorDodge, + Overlay, + SoftLight, + HardLight, + Difference, + Exclusion, + Hue, + Saturation, + Color, + Luminosity +}; + +} diff --git a/Libraries/LibGfx/SkiaUtils.h b/Libraries/LibGfx/SkiaUtils.h index 656bc02ab554a..bb140a15560b2 100644 --- a/Libraries/LibGfx/SkiaUtils.h +++ b/Libraries/LibGfx/SkiaUtils.h @@ -9,10 +9,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -92,3 +94,42 @@ SkPath to_skia_path(Path const& path); sk_sp to_skia_image_filter(Gfx::Filter const& filter); } + +constexpr SkBlendMode to_skia_blend_mode(Gfx::BlendMode blend_mode) +{ + switch (blend_mode) { + case Gfx::BlendMode::Normal: + return SkBlendMode::kSrc; + case Gfx::BlendMode::Darken: + return SkBlendMode::kDarken; + case Gfx::BlendMode::Multiply: + return SkBlendMode::kMultiply; + case Gfx::BlendMode::ColorBurn: + return SkBlendMode::kColorBurn; + case Gfx::BlendMode::Lighten: + return SkBlendMode::kLighten; + case Gfx::BlendMode::Screen: + return SkBlendMode::kScreen; + case Gfx::BlendMode::ColorDodge: + return SkBlendMode::kColorDodge; + case Gfx::BlendMode::Overlay: + return SkBlendMode::kOverlay; + case Gfx::BlendMode::SoftLight: + return SkBlendMode::kSoftLight; + case Gfx::BlendMode::HardLight: + return SkBlendMode::kHardLight; + case Gfx::BlendMode::Difference: + return SkBlendMode::kDifference; + case Gfx::BlendMode::Exclusion: + return SkBlendMode::kExclusion; + case Gfx::BlendMode::Hue: + return SkBlendMode::kHue; + case Gfx::BlendMode::Saturation: + return SkBlendMode::kSaturation; + case Gfx::BlendMode::Color: + return SkBlendMode::kColor; + case Gfx::BlendMode::Luminosity: + return SkBlendMode::kLuminosity; + } + VERIFY_NOT_REACHED(); +} diff --git a/Libraries/LibWeb/CSS/ComputedValues.h b/Libraries/LibWeb/CSS/ComputedValues.h index 31c1952147638..2f192f0586c24 100644 --- a/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Libraries/LibWeb/CSS/ComputedValues.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -270,6 +271,7 @@ struct BackgroundLayerData { CSS::LengthPercentage size_y { CSS::Length::make_auto() }; CSS::Repeat repeat_x { CSS::Repeat::Repeat }; CSS::Repeat repeat_y { CSS::Repeat::Repeat }; + CSS::BlendMode blend_mode { CSS::BlendMode::Normal }; }; struct BorderData { @@ -336,6 +338,46 @@ inline Gfx::ScalingMode to_gfx_scaling_mode(CSS::ImageRendering css_value, Gfx:: VERIFY_NOT_REACHED(); } +// FIXME: Find a better place for this helper. +constexpr Gfx::BlendMode to_gfx_blend_mode(CSS::BlendMode blend_mode) +{ + switch (blend_mode) { + case BlendMode::Normal: + return Gfx::BlendMode::Normal; + case BlendMode::Darken: + return Gfx::BlendMode::Darken; + case BlendMode::Multiply: + return Gfx::BlendMode::Multiply; + case BlendMode::ColorBurn: + return Gfx::BlendMode::ColorBurn; + case BlendMode::Lighten: + return Gfx::BlendMode::Lighten; + case BlendMode::Screen: + return Gfx::BlendMode::Screen; + case BlendMode::ColorDodge: + return Gfx::BlendMode::ColorDodge; + case BlendMode::Overlay: + return Gfx::BlendMode::Overlay; + case BlendMode::SoftLight: + return Gfx::BlendMode::SoftLight; + case BlendMode::HardLight: + return Gfx::BlendMode::HardLight; + case BlendMode::Difference: + return Gfx::BlendMode::Difference; + case BlendMode::Exclusion: + return Gfx::BlendMode::Exclusion; + case BlendMode::Hue: + return Gfx::BlendMode::Hue; + case BlendMode::Saturation: + return Gfx::BlendMode::Saturation; + case BlendMode::Color: + return Gfx::BlendMode::Color; + case BlendMode::Luminosity: + return Gfx::BlendMode::Luminosity; + } + VERIFY_NOT_REACHED(); +} + class ComputedValues { AK_MAKE_NONCOPYABLE(ComputedValues); AK_MAKE_NONMOVABLE(ComputedValues); diff --git a/Libraries/LibWeb/CSS/Enums.json b/Libraries/LibWeb/CSS/Enums.json index 6bc9d84bed0f3..f042b8110cdde 100644 --- a/Libraries/LibWeb/CSS/Enums.json +++ b/Libraries/LibWeb/CSS/Enums.json @@ -79,6 +79,24 @@ "local", "scroll" ], + "blend-mode": [ + "normal", + "darken", + "multiply", + "color-burn", + "lighten", + "screen", + "color-dodge", + "overlay", + "soft-light", + "hard-light", + "difference", + "exclusion", + "hue", + "saturation", + "color", + "luminosity" + ], "background-box": [ "border-box", "content-box", diff --git a/Libraries/LibWeb/CSS/Keywords.json b/Libraries/LibWeb/CSS/Keywords.json index e95576afa7732..12e8917fd3beb 100644 --- a/Libraries/LibWeb/CSS/Keywords.json +++ b/Libraries/LibWeb/CSS/Keywords.json @@ -457,5 +457,20 @@ "xx-small", "xxx-large", "zoom-in", - "zoom-out" + "zoom-out", + "darken", + "multiply", + "color-burn", + "lighten", + "screen", + "color-dodge", + "overlay", + "soft-light", + "hard-light", + "difference", + "exclusion", + "hue", + "saturation", + "color", + "luminosity" ] diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index 4cb2deac739c9..a5cf8d25194a7 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -8413,6 +8413,7 @@ Parser::ParseErrorOr> Parser::parse_css_value(Prope if (auto parsed_value = parse_background_value(tokens); parsed_value && !tokens.has_next_token()) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + case PropertyID::BackgroundBlendMode: case PropertyID::BackgroundAttachment: case PropertyID::BackgroundClip: case PropertyID::BackgroundImage: diff --git a/Libraries/LibWeb/CSS/Properties.json b/Libraries/LibWeb/CSS/Properties.json index 59d2246300e74..846ea7126518a 100644 --- a/Libraries/LibWeb/CSS/Properties.json +++ b/Libraries/LibWeb/CSS/Properties.json @@ -324,6 +324,15 @@ "background-attachment" ] }, + "background-blend-mode": { + "affects-layout": false, + "animation-type": "none", + "inherited": false, + "initial": "normal", + "valid-types": [ + "blend-mode" + ] + }, "background-clip": { "affects-layout": false, "animation-type": "repeatable-list", diff --git a/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h index 801c041bdab81..646e99991be71 100644 --- a/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h @@ -36,7 +36,7 @@ class AbstractImageStyleValue : public CSSStyleValue { virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const { } virtual bool is_paintable() const = 0; - virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering) const = 0; + virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering, CSS::BlendMode blend_mode = CSS::BlendMode::Normal) const = 0; virtual Optional color_if_single_pixel_bitmap() const { return {}; } }; diff --git a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp index 387bcb720c8dc..2dc94707bf8ca 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp @@ -42,12 +42,12 @@ void ConicGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelM m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size }); } -void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const +void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, CSS::BlendMode blend_mode) const { VERIFY(m_resolved.has_value()); auto destination_rect = dest_rect.to_type(); auto position = context.rounded_device_point(m_resolved->position).to_type(); - context.display_list_recorder().fill_rect_with_conic_gradient(destination_rect, m_resolved->data, position); + context.display_list_recorder().fill_rect_with_conic_gradient(destination_rect, m_resolved->data, position, to_gfx_blend_mode(blend_mode)); } bool ConicGradientStyleValue::equals(CSSStyleValue const& other) const diff --git a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h index 6f755655b4474..54894da99faeb 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h @@ -25,7 +25,7 @@ class ConicGradientStyleValue final : public AbstractImageStyleValue { virtual String to_string(SerializationMode) const override; - void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override; + void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, CSS::BlendMode blend_mode = CSS::BlendMode::Normal) const override; virtual bool equals(CSSStyleValue const& other) const override; diff --git a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp index c093aac8a90cc..78482afc3c176 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp @@ -145,11 +145,11 @@ Optional ImageStyleValue::natural_aspect_ratio() const return {}; } -void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const +void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, CSS::BlendMode blend_mode) const { if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type()); b != nullptr) { auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type()); - context.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type(), *b, b->rect(), scaling_mode); + context.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type(), *b, b->rect(), scaling_mode, to_gfx_blend_mode(blend_mode)); } } diff --git a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h index 878c900c4025f..d529c12650126 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include #include @@ -40,7 +41,7 @@ class ImageStyleValue final Optional natural_aspect_ratio() const override; virtual bool is_paintable() const override; - void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override; + void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, CSS::BlendMode blend_mode = CSS::BlendMode::Normal) const override; virtual Optional color_if_single_pixel_bitmap() const override; Gfx::ImmutableBitmap const* current_frame_bitmap(DevicePixelRect const& dest_rect) const; diff --git a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp index 1f46e65c70e0f..8321b9ea5a30b 100644 --- a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp @@ -109,10 +109,10 @@ void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModel m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size }; } -void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const +void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, CSS::BlendMode blend_mode) const { VERIFY(m_resolved.has_value()); - context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type(), m_resolved->data); + context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type(), m_resolved->data, to_gfx_blend_mode(blend_mode)); } } diff --git a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h index 6e189555cce18..e094e905018d9 100644 --- a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h @@ -60,7 +60,7 @@ class LinearGradientStyleValue final : public AbstractImageStyleValue { void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; bool is_paintable() const override { return true; } - void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override; + void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, CSS::BlendMode blend_mode = CSS::BlendMode::Normal) const override; private: LinearGradientStyleValue(GradientDirection direction, Vector color_stop_list, GradientType type, GradientRepeating repeating) diff --git a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp index 1c80cef478563..786ab6ddbc1e8 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp @@ -222,12 +222,12 @@ bool RadialGradientStyleValue::equals(CSSStyleValue const& other) const return m_properties == other_gradient.m_properties; } -void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const +void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, CSS::BlendMode blend_mode) const { VERIFY(m_resolved.has_value()); auto center = context.rounded_device_point(m_resolved->center).to_type(); auto size = context.rounded_device_size(m_resolved->gradient_size).to_type(); - context.display_list_recorder().fill_rect_with_radial_gradient(dest_rect.to_type(), m_resolved->data, center, size); + context.display_list_recorder().fill_rect_with_radial_gradient(dest_rect.to_type(), m_resolved->data, center, size, to_gfx_blend_mode(blend_mode)); } } diff --git a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h index 86d6bbca553db..9c6f78e61a521 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h @@ -51,7 +51,7 @@ class RadialGradientStyleValue final : public AbstractImageStyleValue { virtual String to_string(SerializationMode) const override; - void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override; + void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, CSS::BlendMode blend_mode = CSS::BlendMode::Normal) const override; virtual bool equals(CSSStyleValue const& other) const override; diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index bb885a3d0e750..f61e30991f950 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -350,6 +350,7 @@ void NodeWithStyle::apply_style(const CSS::ComputedProperties& computed_style) auto const& y_positions = computed_style.property(CSS::PropertyID::BackgroundPositionY); auto const& repeats = computed_style.property(CSS::PropertyID::BackgroundRepeat); auto const& sizes = computed_style.property(CSS::PropertyID::BackgroundSize); + auto const& blend_modes = computed_style.property(CSS::PropertyID::BackgroundBlendMode); auto count_layers = [](auto const& maybe_style_value) -> size_t { if (maybe_style_value.is_value_list()) @@ -373,6 +374,7 @@ void NodeWithStyle::apply_style(const CSS::ComputedProperties& computed_style) layer_count = max(layer_count, count_layers(y_positions)); layer_count = max(layer_count, count_layers(repeats)); layer_count = max(layer_count, count_layers(sizes)); + layer_count = max(layer_count, count_layers(blend_modes)); Vector layers; layers.ensure_capacity(layer_count); @@ -463,6 +465,67 @@ void NodeWithStyle::apply_style(const CSS::ComputedProperties& computed_style) layer.repeat_y = repeat_value->as_background_repeat().repeat_y(); } + if (auto blend_mode_value = value_for_layer(blend_modes, layer_index); blend_mode_value && blend_mode_value->is_keyword()) { + auto const num_blend_modes = blend_modes.is_value_list() ? blend_modes.as_value_list().size() : 1; + + if (layer_index >= num_blend_modes) { + layer.blend_mode = CSS::BlendMode::Normal; + } else { + switch (blend_mode_value->to_keyword()) { + case CSS::Keyword::Normal: + layer.blend_mode = CSS::BlendMode::Normal; + break; + case CSS::Keyword::Darken: + layer.blend_mode = CSS::BlendMode::Darken; + break; + case CSS::Keyword::Multiply: + layer.blend_mode = CSS::BlendMode::Multiply; + break; + case CSS::Keyword::ColorBurn: + layer.blend_mode = CSS::BlendMode::ColorBurn; + break; + case CSS::Keyword::Lighten: + layer.blend_mode = CSS::BlendMode::Lighten; + break; + case CSS::Keyword::Screen: + layer.blend_mode = CSS::BlendMode::Screen; + break; + case CSS::Keyword::ColorDodge: + layer.blend_mode = CSS::BlendMode::ColorDodge; + break; + case CSS::Keyword::Overlay: + layer.blend_mode = CSS::BlendMode::Overlay; + break; + case CSS::Keyword::SoftLight: + layer.blend_mode = CSS::BlendMode::SoftLight; + break; + case CSS::Keyword::HardLight: + layer.blend_mode = CSS::BlendMode::HardLight; + break; + case CSS::Keyword::Difference: + layer.blend_mode = CSS::BlendMode::Difference; + break; + case CSS::Keyword::Exclusion: + layer.blend_mode = CSS::BlendMode::Exclusion; + break; + case CSS::Keyword::Hue: + layer.blend_mode = CSS::BlendMode::Hue; + break; + case CSS::Keyword::Saturation: + layer.blend_mode = CSS::BlendMode::Saturation; + break; + case CSS::Keyword::Color: + layer.blend_mode = CSS::BlendMode::Color; + break; + case CSS::Keyword::Luminosity: + layer.blend_mode = CSS::BlendMode::Luminosity; + break; + default: + layer.blend_mode = CSS::BlendMode::Normal; + } + } + } + layers.append(move(layer)); } diff --git a/Libraries/LibWeb/Painting/BackgroundPainting.cpp b/Libraries/LibWeb/Painting/BackgroundPainting.cpp index 8a8c456afc50b..92e0e2fe2f74b 100644 --- a/Libraries/LibWeb/Painting/BackgroundPainting.cpp +++ b/Libraries/LibWeb/Painting/BackgroundPainting.cpp @@ -6,6 +6,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -286,10 +287,10 @@ void paint_background(PaintContext& context, PaintableBox const& paintable_box, auto dest_rect = context.rounded_device_rect(image_rect); auto const* bitmap = static_cast(image).current_frame_bitmap(dest_rect); auto scaling_mode = to_gfx_scaling_mode(image_rendering, bitmap->rect(), dest_rect.to_type()); - context.display_list_recorder().draw_repeated_immutable_bitmap(dest_rect.to_type(), clip_rect.to_type(), *bitmap, scaling_mode, { .x = repeat_x, .y = repeat_y }); + context.display_list_recorder().draw_repeated_immutable_bitmap(dest_rect.to_type(), clip_rect.to_type(), *bitmap, scaling_mode, { .x = repeat_x, .y = repeat_y }, CSS::to_gfx_blend_mode(layer.blend_mode)); } else { for_each_image_device_rect([&](auto const& image_device_rect) { - image.paint(context, image_device_rect, image_rendering); + image.paint(context, image_device_rect, image_rendering, layer.blend_mode); }); } } @@ -407,7 +408,8 @@ ResolvedBackground resolve_background_layers(Vector co .background_positioning_area = background_positioning_area, .image_rect = image_rect, .repeat_x = layer.repeat_x, - .repeat_y = layer.repeat_y }); + .repeat_y = layer.repeat_y, + .blend_mode = layer.blend_mode }); } return ResolvedBackground { diff --git a/Libraries/LibWeb/Painting/BackgroundPainting.h b/Libraries/LibWeb/Painting/BackgroundPainting.h index 63e583205ce47..af411134b5acb 100644 --- a/Libraries/LibWeb/Painting/BackgroundPainting.h +++ b/Libraries/LibWeb/Painting/BackgroundPainting.h @@ -24,6 +24,7 @@ struct ResolvedBackgroundLayerData { CSSPixelRect image_rect; CSS::Repeat repeat_x; CSS::Repeat repeat_y; + CSS::BlendMode blend_mode; }; struct BackgroundBox { diff --git a/Libraries/LibWeb/Painting/Command.h b/Libraries/LibWeb/Painting/Command.h index 73b17a2b06d79..e4c4093bb9266 100644 --- a/Libraries/LibWeb/Painting/Command.h +++ b/Libraries/LibWeb/Painting/Command.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ struct DrawScaledImmutableBitmap { NonnullRefPtr bitmap; Gfx::IntRect src_rect; Gfx::ScalingMode scaling_mode; + Gfx::BlendMode blend_mode; [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } @@ -89,6 +91,7 @@ struct DrawRepeatedImmutableBitmap { Gfx::IntRect clip_rect; NonnullRefPtr bitmap; Gfx::ScalingMode scaling_mode; + Gfx::BlendMode blend_mode; Repeat repeat; void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } @@ -136,6 +139,7 @@ struct PopStackingContext { }; struct PaintLinearGradient { Gfx::IntRect gradient_rect; LinearGradientData linear_gradient_data; + Gfx::BlendMode blend_mode; [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; } @@ -325,6 +329,7 @@ struct PaintRadialGradient { RadialGradientData radial_gradient_data; Gfx::IntPoint center; Gfx::IntSize size; + Gfx::BlendMode blend_mode; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } @@ -335,6 +340,7 @@ struct PaintConicGradient { Gfx::IntRect rect; ConicGradientData conic_gradient_data; Gfx::IntPoint position; + Gfx::BlendMode blend_mode; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index b3eedde3d26ef..661f08defca42 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -161,6 +161,7 @@ void DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmutableBitm auto dst_rect = to_skia_rect(command.dst_rect); auto& canvas = surface().canvas(); SkPaint paint; + paint.setBlendMode(to_skia_blend_mode(command.blend_mode)); canvas.drawImageRect(command.bitmap->sk_image(), src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint); } @@ -178,6 +179,7 @@ void DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutable auto shader = command.bitmap->sk_image()->makeShader(tile_mode_x, tile_mode_y, sampling_options, matrix); SkPaint paint; + paint.setBlendMode(to_skia_blend_mode(command.blend_mode)); paint.setShader(shader); auto& canvas = surface().canvas(); canvas.drawPaint(paint); @@ -376,6 +378,7 @@ void DisplayListPlayerSkia::paint_linear_gradient(PaintLinearGradient const& com SkPaint paint; paint.setShader(shader); + paint.setBlendMode(to_skia_blend_mode(command.blend_mode)); surface().canvas().drawRect(to_skia_rect(rect), paint); } @@ -792,6 +795,7 @@ void DisplayListPlayerSkia::paint_radial_gradient(PaintRadialGradient const& com SkPaint paint; paint.setAntiAlias(true); paint.setShader(shader); + paint.setBlendMode(to_skia_blend_mode(command.blend_mode)); surface().canvas().drawRect(to_skia_rect(rect), paint); } @@ -831,6 +835,7 @@ void DisplayListPlayerSkia::paint_conic_gradient(PaintConicGradient const& comma SkPaint paint; paint.setAntiAlias(true); paint.setShader(shader); + paint.setBlendMode(to_skia_blend_mode(command.blend_mode)); surface().canvas().drawRect(to_skia_rect(rect), paint); } diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index e8bd4536000cb..601cc8b0f9421 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -140,24 +140,25 @@ void DisplayListRecorder::fill_ellipse(Gfx::IntRect const& a_rect, Color color) append(FillEllipse { a_rect, color }); } -void DisplayListRecorder::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data) +void DisplayListRecorder::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Gfx::BlendMode blend_mode) { if (gradient_rect.is_empty()) return; - append(PaintLinearGradient { gradient_rect, data }); + append(PaintLinearGradient { gradient_rect, data, blend_mode }); } -void DisplayListRecorder::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position) +void DisplayListRecorder::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Gfx::BlendMode blend_mode) { if (rect.is_empty()) return; append(PaintConicGradient { .rect = rect, .conic_gradient_data = data, - .position = position }); + .position = position, + .blend_mode = blend_mode }); } -void DisplayListRecorder::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size) +void DisplayListRecorder::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Gfx::BlendMode blend_mode) { if (rect.is_empty()) return; @@ -165,7 +166,8 @@ void DisplayListRecorder::fill_rect_with_radial_gradient(Gfx::IntRect const& rec .rect = rect, .radial_gradient_data = data, .center = center, - .size = size }); + .size = size, + .blend_mode = blend_mode }); } void DisplayListRecorder::draw_rect(Gfx::IntRect const& rect, Color color, bool rough) @@ -190,7 +192,7 @@ void DisplayListRecorder::draw_painting_surface(Gfx::IntRect const& dst_rect, No }); } -void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode) +void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, Gfx::BlendMode blend_mode) { if (dst_rect.is_empty()) return; @@ -199,16 +201,18 @@ void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_r .bitmap = bitmap, .src_rect = src_rect, .scaling_mode = scaling_mode, + .blend_mode = blend_mode, }); } -void DisplayListRecorder::draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat repeat) +void DisplayListRecorder::draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat repeat, Gfx::BlendMode blend_mode) { append(DrawRepeatedImmutableBitmap { .dst_rect = dst_rect, .clip_rect = clip_rect, .bitmap = move(bitmap), .scaling_mode = scaling_mode, + .blend_mode = blend_mode, .repeat = repeat, }); } diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Libraries/LibWeb/Painting/DisplayListRecorder.h index 9265743681947..2fcfc19ba817c 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -88,16 +88,16 @@ class DisplayListRecorder { void fill_ellipse(Gfx::IntRect const& a_rect, Color color); - void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data); - void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position); - void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size); + void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Gfx::BlendMode blend_mode = Gfx::BlendMode::Normal); + void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Gfx::BlendMode blend_mode = Gfx::BlendMode::Normal); + void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Gfx::BlendMode blend_mode = Gfx::BlendMode::Normal); void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false); void draw_painting_surface(Gfx::IntRect const& dst_rect, NonnullRefPtr, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); - void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); + void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor, Gfx::BlendMode blend_mode = Gfx::BlendMode::Normal); - void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat); + void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat, Gfx::BlendMode blend_mode = Gfx::BlendMode::Normal); void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::LineStyle style = Gfx::LineStyle::Solid, Color alternate_color = Color::Transparent);