Skip to content

Commit

Permalink
Implement background-blend-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
vukanovics committed Jan 6, 2025
1 parent 8214371 commit ad5eac6
Show file tree
Hide file tree
Showing 23 changed files with 268 additions and 30 deletions.
30 changes: 30 additions & 0 deletions Libraries/LibGfx/BlendMode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2025, Stefan Vukanović <[email protected]>
*
* 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
};

}
41 changes: 41 additions & 0 deletions Libraries/LibGfx/SkiaUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@

#include <AK/Assertions.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/BlendMode.h>
#include <LibGfx/Filter.h>
#include <LibGfx/PathSkia.h>
#include <LibGfx/ScalingMode.h>
#include <LibGfx/WindingRule.h>
#include <core/SkBlendMode.h>
#include <core/SkColor.h>
#include <core/SkColorType.h>
#include <core/SkImageFilter.h>
Expand Down Expand Up @@ -92,3 +94,42 @@ SkPath to_skia_path(Path const& path);
sk_sp<SkImageFilter> 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();
}
42 changes: 42 additions & 0 deletions Libraries/LibWeb/CSS/ComputedValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <AK/Optional.h>
#include <LibGfx/BlendMode.h>
#include <LibGfx/Filter.h>
#include <LibGfx/FontCascadeList.h>
#include <LibGfx/ScalingMode.h>
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions Libraries/LibWeb/CSS/Enums.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 16 additions & 1 deletion Libraries/LibWeb/CSS/Keywords.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
1 change: 1 addition & 0 deletions Libraries/LibWeb/CSS/Parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8413,6 +8413,7 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> 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:
Expand Down
9 changes: 9 additions & 0 deletions Libraries/LibWeb/CSS/Properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Gfx::Color> color_if_single_pixel_bitmap() const { return {}; }
};
Expand Down
4 changes: 2 additions & 2 deletions Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>();
auto position = context.rounded_device_point(m_resolved->position).to_type<int>();
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
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ Optional<CSSPixelFraction> 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<int>()); b != nullptr) {
auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>());
context.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode);
context.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, to_gfx_blend_mode(blend_mode));
}
}

Expand Down
3 changes: 2 additions & 1 deletion Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#pragma once

#include <LibGC/Root.h>
#include <LibGfx/BlendMode.h>
#include <LibJS/Heap/Cell.h>
#include <LibURL/URL.h>
#include <LibWeb/CSS/Enums.h>
Expand Down Expand Up @@ -40,7 +41,7 @@ class ImageStyleValue final
Optional<CSSPixelFraction> 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<Gfx::Color> color_if_single_pixel_bitmap() const override;
Gfx::ImmutableBitmap const* current_frame_bitmap(DevicePixelRect const& dest_rect) const;
Expand Down
4 changes: 2 additions & 2 deletions Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(), m_resolved->data);
context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type<int>(), m_resolved->data, to_gfx_blend_mode(blend_mode));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating)
Expand Down
4 changes: 2 additions & 2 deletions Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>();
auto size = context.rounded_device_size(m_resolved->gradient_size).to_type<int>();
context.display_list_recorder().fill_rect_with_radial_gradient(dest_rect.to_type<int>(), m_resolved->data, center, size);
context.display_list_recorder().fill_rect_with_radial_gradient(dest_rect.to_type<int>(), m_resolved->data, center, size, to_gfx_blend_mode(blend_mode));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading

0 comments on commit ad5eac6

Please sign in to comment.