From 0fcb5740413df355a4667ba9826bd3e2e2ac9782 Mon Sep 17 00:00:00 2001 From: Lucien Fiorini Date: Sun, 11 May 2025 12:44:54 +0200 Subject: [PATCH] LibGfx+LibWeb: Turn Gfx::Filter into a SkImageFilter wrapper --- Libraries/LibGfx/CMakeLists.txt | 1 + Libraries/LibGfx/Filter.cpp | 224 ++++++++++++++++++ Libraries/LibGfx/Filter.h | 59 +++-- Libraries/LibGfx/FilterImpl.h | 29 +++ Libraries/LibGfx/Painter.h | 8 +- Libraries/LibGfx/PainterSkia.cpp | 33 +-- Libraries/LibGfx/PainterSkia.h | 8 +- Libraries/LibGfx/SkiaUtils.cpp | 122 +--------- Libraries/LibGfx/SkiaUtils.h | 1 + Libraries/LibWeb/CSS/ComputedValues.h | 16 +- .../LibWeb/CSS/Parser/PropertyParsing.cpp | 2 +- .../StyleValues/FilterValueListStyleValue.cpp | 14 +- .../StyleValues/FilterValueListStyleValue.h | 2 +- Libraries/LibWeb/HTML/Canvas/CanvasState.h | 4 +- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 44 ++-- Libraries/LibWeb/Layout/Node.cpp | 39 +-- Libraries/LibWeb/Painting/Command.h | 8 +- Libraries/LibWeb/Painting/DisplayList.cpp | 2 +- Libraries/LibWeb/Painting/DisplayList.h | 2 +- .../LibWeb/Painting/DisplayListPlayerSkia.cpp | 22 +- .../LibWeb/Painting/DisplayListPlayerSkia.h | 2 +- .../LibWeb/Painting/DisplayListRecorder.cpp | 6 +- .../LibWeb/Painting/DisplayListRecorder.h | 4 +- Libraries/LibWeb/Painting/PaintableBox.cpp | 4 +- Libraries/LibWeb/Painting/SVGSVGPaintable.cpp | 6 +- Libraries/LibWeb/Painting/StackingContext.cpp | 6 +- 26 files changed, 412 insertions(+), 256 deletions(-) create mode 100644 Libraries/LibGfx/Filter.cpp create mode 100644 Libraries/LibGfx/FilterImpl.h diff --git a/Libraries/LibGfx/CMakeLists.txt b/Libraries/LibGfx/CMakeLists.txt index 4281ec57079..e116e137d7c 100644 --- a/Libraries/LibGfx/CMakeLists.txt +++ b/Libraries/LibGfx/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES Color.cpp ColorSpace.cpp Cursor.cpp + Filter.cpp FontCascadeList.cpp Font/Font.cpp Font/FontData.cpp diff --git a/Libraries/LibGfx/Filter.cpp b/Libraries/LibGfx/Filter.cpp new file mode 100644 index 00000000000..a052fa8b2ad --- /dev/null +++ b/Libraries/LibGfx/Filter.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2025, Lucien Fiorini + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +using Impl = FilterImpl; + +Filter::Filter(Filter const& other) + : m_impl(other.m_impl->clone()) +{ +} + +Filter& Filter::operator=(Filter const& other) +{ + if (this != &other) { + m_impl = other.m_impl->clone(); + } + return *this; +} + +Filter::~Filter() = default; + +Filter::Filter(NonnullOwnPtr&& impl) + : m_impl(impl->clone()) +{ +} + +FilterImpl const& Filter::impl() const +{ + return *m_impl; +} + +Filter Filter::compose(Filter const& outer, Filter const& inner) +{ + auto inner_skia = inner.m_impl->filter; + auto outer_skia = outer.m_impl->filter; + + auto filter = SkImageFilters::Compose(outer_skia, inner_skia); + return Filter(Impl::create(filter)); +} + +Filter Filter::blend(Filter const& background, Filter const& foreground, Gfx::CompositingAndBlendingOperator mode) +{ + auto filter = SkImageFilters::Blend(to_skia_blender(mode), background.m_impl->filter, foreground.m_impl->filter); + return Filter(Impl::create(filter)); +} + +Filter Filter::blur(float radius, Optional input) +{ + sk_sp input_skia = input.has_value() ? input->m_impl->filter : nullptr; + + auto filter = SkImageFilters::Blur(radius, radius, input_skia); + return Filter(Impl::create(filter)); +} + +Filter Filter::flood(Gfx::Color color, float opacity) +{ + auto color_skia = to_skia_color(color); + color_skia = SkColorSetA(color_skia, static_cast(opacity * 255)); + + return Filter(Impl::create(SkImageFilters::Shader(SkShaders::Color(color_skia)))); +} + +Filter Filter::drop_shadow(float offset_x, float offset_y, float radius, Gfx::Color color, + Optional input) +{ + sk_sp input_skia = input.has_value() ? input->m_impl->filter : nullptr; + auto shadow_color = to_skia_color(color); + + auto filter = SkImageFilters::DropShadow(offset_x, offset_y, radius, radius, shadow_color, input_skia); + return Filter(Impl::create(filter)); +} + +Filter Filter::color(ColorFilterType type, float amount, Optional input) +{ + sk_sp input_skia = input.has_value() ? input->m_impl->filter : nullptr; + + sk_sp color_filter; + + // Matrices are taken from https://drafts.fxtf.org/filter-effects-1/#FilterPrimitiveRepresentation + switch (type) { + case ColorFilterType::Grayscale: { + float matrix[20] = { + 0.2126f + 0.7874f * (1 - amount), 0.7152f - 0.7152f * (1 - amount), + 0.0722f - 0.0722f * (1 - amount), 0, 0, + 0.2126f - 0.2126f * (1 - amount), 0.7152f + 0.2848f * (1 - amount), + 0.0722f - 0.0722f * (1 - amount), 0, 0, + 0.2126f - 0.2126f * (1 - amount), 0.7152f - 0.7152f * (1 - amount), + 0.0722f + 0.9278f * (1 - amount), 0, 0, + 0, 0, 0, 1, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); + break; + } + case Gfx::ColorFilterType::Brightness: { + float matrix[20] = { + amount, 0, 0, 0, 0, + 0, amount, 0, 0, 0, + 0, 0, amount, 0, 0, + 0, 0, 0, 1, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); + break; + } + case Gfx::ColorFilterType::Contrast: { + float intercept = -(0.5f * amount) + 0.5f; + float matrix[20] = { + amount, 0, 0, 0, intercept, + 0, amount, 0, 0, intercept, + 0, 0, amount, 0, intercept, + 0, 0, 0, 1, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); + break; + } + case Gfx::ColorFilterType::Invert: { + float matrix[20] = { + 1 - 2 * amount, 0, 0, 0, amount, + 0, 1 - 2 * amount, 0, 0, amount, + 0, 0, 1 - 2 * amount, 0, amount, + 0, 0, 0, 1, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); + break; + } + case Gfx::ColorFilterType::Opacity: { + float matrix[20] = { + 1, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 0, amount, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); + break; + } + case Gfx::ColorFilterType::Sepia: { + float matrix[20] = { + 0.393f + 0.607f * (1 - amount), 0.769f - 0.769f * (1 - amount), 0.189f - 0.189f * (1 - amount), 0, + 0, + 0.349f - 0.349f * (1 - amount), 0.686f + 0.314f * (1 - amount), 0.168f - 0.168f * (1 - amount), 0, + 0, + 0.272f - 0.272f * (1 - amount), 0.534f - 0.534f * (1 - amount), 0.131f + 0.869f * (1 - amount), 0, + 0, + 0, 0, 0, 1, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); + break; + } + case Gfx::ColorFilterType::Saturate: { + float matrix[20] = { + 0.213f + 0.787f * amount, 0.715f - 0.715f * amount, 0.072f - 0.072f * amount, 0, 0, + 0.213f - 0.213f * amount, 0.715f + 0.285f * amount, 0.072f - 0.072f * amount, 0, 0, + 0.213f - 0.213f * amount, 0.715f - 0.715f * amount, 0.072f + 0.928f * amount, 0, 0, + 0, 0, 0, 1, 0 + }; + color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); + break; + } + default: + VERIFY_NOT_REACHED(); + } + + return Filter(Impl::create(SkImageFilters::ColorFilter(color_filter, input_skia))); +} + +Filter Filter::color_matrix(float matrix[20], Optional input) +{ + sk_sp input_skia = input.has_value() ? input->m_impl->filter : nullptr; + + return Filter(Impl::create(SkImageFilters::ColorFilter(SkColorFilters::Matrix(matrix), input_skia))); +} + +Filter Filter::saturate(float value, Optional input) +{ + sk_sp input_skia = input.has_value() ? input->m_impl->filter : nullptr; + + SkColorMatrix matrix; + matrix.setSaturation(value); + + return Filter(Impl::create(SkImageFilters::ColorFilter(SkColorFilters::Matrix(matrix), input_skia))); +} + +Filter Filter::hue_rotate(float angle_degrees, Optional input) +{ + sk_sp input_skia = input.has_value() ? input->m_impl->filter : nullptr; + + float radians = AK::to_radians(angle_degrees); + + auto cosA = cos(radians); + auto sinA = sin(radians); + + auto a00 = 0.213f + cosA * 0.787f - sinA * 0.213f; + auto a01 = 0.715f - cosA * 0.715f - sinA * 0.715f; + auto a02 = 0.072f - cosA * 0.072f + sinA * 0.928f; + auto a10 = 0.213f - cosA * 0.213f + sinA * 0.143f; + auto a11 = 0.715f + cosA * 0.285f + sinA * 0.140f; + auto a12 = 0.072f - cosA * 0.072f - sinA * 0.283f; + auto a20 = 0.213f - cosA * 0.213f - sinA * 0.787f; + auto a21 = 0.715f - cosA * 0.715f + sinA * 0.715f; + auto a22 = 0.072f + cosA * 0.928f + sinA * 0.072f; + + float matrix[20] = { + a00, a01, a02, 0, 0, + a10, a11, a12, 0, 0, + a20, a21, a22, 0, 0, + 0, 0, 0, 1, 0 + }; + + auto color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); + return Filter(Impl::create(SkImageFilters::ColorFilter(color_filter, input_skia))); +} + +} diff --git a/Libraries/LibGfx/Filter.h b/Libraries/LibGfx/Filter.h index 67ff839f258..6395f9d3da7 100644 --- a/Libraries/LibGfx/Filter.h +++ b/Libraries/LibGfx/Filter.h @@ -1,44 +1,51 @@ /* - * Copyright (c) 2024, Lucien Fiorini + * Copyright (c) 2024-2025, Lucien Fiorini * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include +#include #include +#include namespace Gfx { -struct BlurFilter { - float radius; +enum class ColorFilterType { + Brightness, + Contrast, + Grayscale, + Invert, + Opacity, + Saturate, + Sepia }; -struct DropShadowFilter { - float offset_x; - float offset_y; - float radius; - Gfx::Color color; -}; +struct FilterImpl; -struct HueRotateFilter { - float angle_degrees; -}; +class Filter { +public: + Filter(Filter const&); + Filter& operator=(Filter const&); -struct ColorFilter { - enum class Type { - Brightness, - Contrast, - Grayscale, - Invert, - Opacity, - Saturate, - Sepia - } type; - float amount; -}; + ~Filter(); -using Filter = Variant; + static Filter compose(Filter const& outer, Filter const& inner); + static Filter blend(Filter const& background, Filter const& foreground, CompositingAndBlendingOperator mode); + static Filter flood(Gfx::Color color, float opacity); + static Filter drop_shadow(float offset_x, float offset_y, float radius, Gfx::Color color, Optional input = {}); + static Filter blur(float radius, Optional input = {}); + static Filter color(ColorFilterType type, float amount, Optional input = {}); + static Filter color_matrix(float matrix[20], Optional input = {}); + static Filter saturate(float value, Optional input = {}); + static Filter hue_rotate(float angle_degrees, Optional input = {}); + + FilterImpl const& impl() const; + +private: + Filter(NonnullOwnPtr&&); + NonnullOwnPtr m_impl; +}; } diff --git a/Libraries/LibGfx/FilterImpl.h b/Libraries/LibGfx/FilterImpl.h new file mode 100644 index 00000000000..325fb504c83 --- /dev/null +++ b/Libraries/LibGfx/FilterImpl.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024-2025, Lucien Fiorini + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Gfx { + +struct FilterImpl { + sk_sp filter; + + static NonnullOwnPtr create(sk_sp filter) + { + return adopt_own(*new FilterImpl(move(filter))); + } + + NonnullOwnPtr clone() const + { + return adopt_own(*new FilterImpl(filter)); + } +}; + +} diff --git a/Libraries/LibGfx/Painter.h b/Libraries/LibGfx/Painter.h index e4b30fbf64f..ea1aede9cd0 100644 --- a/Libraries/LibGfx/Painter.h +++ b/Libraries/LibGfx/Painter.h @@ -26,16 +26,16 @@ public: virtual void clear_rect(Gfx::FloatRect const&, Gfx::Color) = 0; virtual void fill_rect(Gfx::FloatRect const&, Gfx::Color) = 0; - virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, ReadonlySpan filters, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0; + virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, Optional filters, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0; virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) = 0; virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0; - virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0; - virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector const&, float dash_offset) = 0; + virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0; + virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector const&, float dash_offset) = 0; virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) = 0; virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0; - virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) = 0; + virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) = 0; virtual void set_transform(Gfx::AffineTransform const&) = 0; diff --git a/Libraries/LibGfx/PainterSkia.cpp b/Libraries/LibGfx/PainterSkia.cpp index fda2a0cdbde..8f62d0a964a 100644 --- a/Libraries/LibGfx/PainterSkia.cpp +++ b/Libraries/LibGfx/PainterSkia.cpp @@ -17,10 +17,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -99,19 +97,19 @@ static void apply_paint_style(SkPaint& paint, Gfx::PaintStyle const& style) } } -static void apply_filters(SkPaint& paint, ReadonlySpan filters) +static void apply_filter(SkPaint& paint, Gfx::Filter const& filter) { - for (auto const& filter : filters) { - paint.setImageFilter(to_skia_image_filter(filter)); - } + paint.setImageFilter(to_skia_image_filter(filter)); } -static SkPaint to_skia_paint(Gfx::PaintStyle const& style, ReadonlySpan filters) +static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Optional filter) { SkPaint paint; apply_paint_style(paint, style); - apply_filters(paint, filters); + + if (filter.has_value()) + apply_filter(paint, move(filter.value())); return paint; } @@ -142,10 +140,13 @@ void PainterSkia::fill_rect(Gfx::FloatRect const& rect, Color color) }); } -void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, ReadonlySpan filters, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) +void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, Optional filter, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) { SkPaint paint; - apply_filters(paint, filters); + + if (filter.has_value()) + apply_filter(paint, filter.value()); + paint.setAlpha(static_cast(global_alpha * 255)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); @@ -208,14 +209,14 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thi }); } -void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, ReadonlySpan filters, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) +void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Optional filter, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) { // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. if (thickness <= 0) return; auto sk_path = to_skia_path(path); - auto paint = to_skia_paint(paint_style, filters); + auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); @@ -227,14 +228,14 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& pain }); } -void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, ReadonlySpan filters, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const& cap_style, Gfx::Path::JoinStyle const& join_style, float miter_limit, Vector const& dash_array, float dash_offset) +void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Optional filter, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const& cap_style, Gfx::Path::JoinStyle const& join_style, float miter_limit, Vector const& dash_array, float dash_offset) { // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. if (thickness <= 0) return; auto sk_path = to_skia_path(path); - auto paint = to_skia_paint(paint_style, filters); + auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); @@ -276,11 +277,11 @@ void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::Windin }); } -void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, ReadonlySpan filters, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule winding_rule) +void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Optional filter, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule winding_rule) { auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); - auto paint = to_skia_paint(paint_style, filters); + auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); diff --git a/Libraries/LibGfx/PainterSkia.h b/Libraries/LibGfx/PainterSkia.h index 0f78388cce2..8f8291641f9 100644 --- a/Libraries/LibGfx/PainterSkia.h +++ b/Libraries/LibGfx/PainterSkia.h @@ -21,14 +21,14 @@ public: virtual void clear_rect(Gfx::FloatRect const&, Color) override; virtual void fill_rect(Gfx::FloatRect const&, Color) override; - virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, ReadonlySpan, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override; + virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, Optional, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override; virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) override; virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override; - virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override; - virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector const&, float dash_offset) override; + virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override; + virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector const&, float dash_offset) override; virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) override; virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override; - virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) override; + virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) override; virtual void set_transform(Gfx::AffineTransform const&) override; virtual void save() override; virtual void restore() override; diff --git a/Libraries/LibGfx/SkiaUtils.cpp b/Libraries/LibGfx/SkiaUtils.cpp index 9228e3e6b9b..7bcc7178e71 100644 --- a/Libraries/LibGfx/SkiaUtils.cpp +++ b/Libraries/LibGfx/SkiaUtils.cpp @@ -6,12 +6,11 @@ #include #include +#include #include #include -#include #include #include -#include #include namespace Gfx { @@ -23,124 +22,7 @@ SkPath to_skia_path(Path const& path) sk_sp to_skia_image_filter(Gfx::Filter const& filter) { - // See: https://drafts.fxtf.org/filter-effects-1/#supported-filter-functions - return filter.visit( - [&](Gfx::BlurFilter blur_filter) { - return SkImageFilters::Blur(blur_filter.radius, blur_filter.radius, nullptr); - }, - [&](Gfx::ColorFilter color_filter) { - sk_sp skia_color_filter; - float amount = color_filter.amount; - - // Matrices are taken from https://drafts.fxtf.org/filter-effects-1/#FilterPrimitiveRepresentation - switch (color_filter.type) { - case ColorFilter::Type::Grayscale: { - float matrix[20] = { - 0.2126f + 0.7874f * (1 - amount), 0.7152f - 0.7152f * (1 - amount), 0.0722f - 0.0722f * (1 - amount), 0, 0, - 0.2126f - 0.2126f * (1 - amount), 0.7152f + 0.2848f * (1 - amount), 0.0722f - 0.0722f * (1 - amount), 0, 0, - 0.2126f - 0.2126f * (1 - amount), 0.7152f - 0.7152f * (1 - amount), 0.0722f + 0.9278f * (1 - amount), 0, 0, - 0, 0, 0, 1, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case Gfx::ColorFilter::Type::Brightness: { - float matrix[20] = { - amount, 0, 0, 0, 0, - 0, amount, 0, 0, 0, - 0, 0, amount, 0, 0, - 0, 0, 0, 1, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - break; - } - case Gfx::ColorFilter::Type::Contrast: { - float intercept = -(0.5f * amount) + 0.5f; - float matrix[20] = { - amount, 0, 0, 0, intercept, - 0, amount, 0, 0, intercept, - 0, 0, amount, 0, intercept, - 0, 0, 0, 1, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - break; - } - case Gfx::ColorFilter::Type::Invert: { - float matrix[20] = { - 1 - 2 * amount, 0, 0, 0, amount, - 0, 1 - 2 * amount, 0, 0, amount, - 0, 0, 1 - 2 * amount, 0, amount, - 0, 0, 0, 1, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case Gfx::ColorFilter::Type::Opacity: { - float matrix[20] = { - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, amount, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case Gfx::ColorFilter::Type::Sepia: { - float matrix[20] = { - 0.393f + 0.607f * (1 - amount), 0.769f - 0.769f * (1 - amount), 0.189f - 0.189f * (1 - amount), 0, 0, - 0.349f - 0.349f * (1 - amount), 0.686f + 0.314f * (1 - amount), 0.168f - 0.168f * (1 - amount), 0, 0, - 0.272f - 0.272f * (1 - amount), 0.534f - 0.534f * (1 - amount), 0.131f + 0.869f * (1 - amount), 0, 0, - 0, 0, 0, 1, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case Gfx::ColorFilter::Type::Saturate: { - float matrix[20] = { - 0.213f + 0.787f * amount, 0.715f - 0.715f * amount, 0.072f - 0.072f * amount, 0, 0, - 0.213f - 0.213f * amount, 0.715f + 0.285f * amount, 0.072f - 0.072f * amount, 0, 0, - 0.213f - 0.213f * amount, 0.715f - 0.715f * amount, 0.072f + 0.928f * amount, 0, 0, - 0, 0, 0, 1, 0 - }; - skia_color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - break; - } - default: - VERIFY_NOT_REACHED(); - } - - return SkImageFilters::ColorFilter(skia_color_filter, nullptr); - }, - [&](Gfx::HueRotateFilter hue_rotate_filter) { - float radians = AK::to_radians(hue_rotate_filter.angle_degrees); - - auto cosA = cos(radians); - auto sinA = sin(radians); - - auto a00 = 0.213f + cosA * 0.787f - sinA * 0.213f; - auto a01 = 0.715f - cosA * 0.715f - sinA * 0.715f; - auto a02 = 0.072f - cosA * 0.072f + sinA * 0.928f; - auto a10 = 0.213f - cosA * 0.213f + sinA * 0.143f; - auto a11 = 0.715f + cosA * 0.285f + sinA * 0.140f; - auto a12 = 0.072f - cosA * 0.072f - sinA * 0.283f; - auto a20 = 0.213f - cosA * 0.213f - sinA * 0.787f; - auto a21 = 0.715f - cosA * 0.715f + sinA * 0.715f; - auto a22 = 0.072f + cosA * 0.928f + sinA * 0.072f; - - float matrix[20] = { - a00, a01, a02, 0, 0, - a10, a11, a12, 0, 0, - a20, a21, a22, 0, 0, - 0, 0, 0, 1, 0 - }; - - auto filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - return SkImageFilters::ColorFilter(filter, nullptr); - }, - [&](Gfx::DropShadowFilter drop_shadow_filter) { - auto shadow_color = to_skia_color(drop_shadow_filter.color); - return SkImageFilters::DropShadow(drop_shadow_filter.offset_x, drop_shadow_filter.offset_y, drop_shadow_filter.radius, drop_shadow_filter.radius, shadow_color, nullptr); - }); + return filter.impl().filter; } sk_sp to_skia_blender(Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) diff --git a/Libraries/LibGfx/SkiaUtils.h b/Libraries/LibGfx/SkiaUtils.h index fdf124cbbdf..811f5a8000a 100644 --- a/Libraries/LibGfx/SkiaUtils.h +++ b/Libraries/LibGfx/SkiaUtils.h @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace Gfx { diff --git a/Libraries/LibWeb/CSS/ComputedValues.h b/Libraries/LibWeb/CSS/ComputedValues.h index 5b6b0c89d55..0ed1c1dd33d 100644 --- a/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Libraries/LibWeb/CSS/ComputedValues.h @@ -118,8 +118,8 @@ public: static CSS::Display display() { return CSS::Display { CSS::DisplayOutside::Inline, CSS::DisplayInside::Flow }; } static Color color() { return Color::Black; } static Color stop_color() { return Color::Black; } - static Vector backdrop_filter() { return {}; } - static Vector filter() { return {}; } + static Optional backdrop_filter() { return {}; } + static Optional filter() { return {}; } static Color background_color() { return Color::Transparent; } static CSS::ListStyleType list_style_type() { return CSS::CounterStyleNameKeyword::Disc; } static CSS::ListStylePosition list_style_position() { return CSS::ListStylePosition::Outside; } @@ -451,8 +451,8 @@ public: CSS::JustifyContent justify_content() const { return m_noninherited.justify_content; } CSS::JustifySelf justify_self() const { return m_noninherited.justify_self; } CSS::JustifyItems justify_items() const { return m_noninherited.justify_items; } - Vector const& backdrop_filter() const { return m_noninherited.backdrop_filter; } - Vector const& filter() const { return m_noninherited.filter; } + Optional const& backdrop_filter() const { return m_noninherited.backdrop_filter; } + Optional const& filter() const { return m_noninherited.filter; } Vector const& box_shadow() const { return m_noninherited.box_shadow; } CSS::BoxSizing box_sizing() const { return m_noninherited.box_sizing; } CSS::Size const& width() const { return m_noninherited.width; } @@ -680,8 +680,8 @@ protected: CSS::LengthBox inset { InitialValues::inset() }; CSS::LengthBox margin { InitialValues::margin() }; CSS::LengthBox padding { InitialValues::padding() }; - Vector backdrop_filter { InitialValues::backdrop_filter() }; - Vector filter { InitialValues::filter() }; + Optional backdrop_filter { InitialValues::backdrop_filter() }; + Optional filter { InitialValues::filter() }; BorderData border_left; BorderData border_top; BorderData border_right; @@ -849,8 +849,8 @@ public: void set_list_style_type(CSS::ListStyleType value) { m_inherited.list_style_type = value; } void set_list_style_position(CSS::ListStylePosition value) { m_inherited.list_style_position = value; } void set_display(CSS::Display value) { m_noninherited.display = value; } - void set_backdrop_filter(Vector backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); } - void set_filter(Vector filter) { m_noninherited.filter = move(filter); } + void set_backdrop_filter(Optional backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); } + void set_filter(Optional filter) { m_noninherited.filter = move(filter); } void set_border_bottom_left_radius(CSS::BorderRadiusData value) { m_noninherited.has_noninitial_border_radii = true; diff --git a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp index 5964206ce20..d98336b6c96 100644 --- a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp @@ -4545,7 +4545,7 @@ RefPtr Parser::parse_filter_value_list_value(TokenStream(filter); + return static_cast(filter); }; auto parse_filter_function_name = [&](auto name) -> Optional { diff --git a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp index 0698ab550fe..88ce91e367f 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp @@ -78,19 +78,19 @@ String FilterValueListStyleValue::to_string(SerializationMode) const builder.appendff("{}(", [&] { switch (color.operation) { - case Gfx::ColorFilter::Type::Brightness: + case Gfx::ColorFilterType::Brightness: return "brightness"sv; - case Gfx::ColorFilter::Type::Contrast: + case Gfx::ColorFilterType::Contrast: return "contrast"sv; - case Gfx::ColorFilter::Type::Grayscale: + case Gfx::ColorFilterType::Grayscale: return "grayscale"sv; - case Gfx::ColorFilter::Type::Invert: + case Gfx::ColorFilterType::Invert: return "invert"sv; - case Gfx::ColorFilter::Type::Opacity: + case Gfx::ColorFilterType::Opacity: return "opacity"sv; - case Gfx::ColorFilter::Type::Saturate: + case Gfx::ColorFilterType::Saturate: return "saturate"sv; - case Gfx::ColorFilter::Type::Sepia: + case Gfx::ColorFilterType::Sepia: return "sepia"sv; default: VERIFY_NOT_REACHED(); diff --git a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.h index 8985b27d9bb..559c1e56842 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.h @@ -45,7 +45,7 @@ struct HueRotate { }; struct Color { - Gfx::ColorFilter::Type operation; + Gfx::ColorFilterType operation; NumberPercentage amount { Number { Number::Type::Integer, 1.0 } }; float resolved_amount() const; bool operator==(Color const&) const = default; diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasState.h b/Libraries/LibWeb/HTML/Canvas/CanvasState.h index 3c5801a77ca..3f3de2e761e 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasState.h +++ b/Libraries/LibWeb/HTML/Canvas/CanvasState.h @@ -87,8 +87,8 @@ public: float shadow_offset_y { 0.0f }; float shadow_blur { 0.0f }; Gfx::Color shadow_color { Gfx::Color::Transparent }; - Vector filters; - Optional filters_string; + Optional filter; + Optional filter_string; float line_width { 1 }; Bindings::CanvasLineCap line_cap { Bindings::CanvasLineCap::Butt }; Bindings::CanvasLineJoin line_join { Bindings::CanvasLineJoin::Miter }; diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 6f17332a3e8..9f4bd10da9c 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -228,7 +228,7 @@ WebIDL::ExceptionOr CanvasRenderingContext2D::draw_image_internal(CanvasIm } if (auto* painter = this->painter()) { - painter->draw_bitmap(destination_rect, *bitmap, source_rect.to_rounded(), scaling_mode, drawing_state().filters, drawing_state().global_alpha, drawing_state().current_compositing_and_blending_operator); + painter->draw_bitmap(destination_rect, *bitmap, source_rect.to_rounded(), scaling_mode, drawing_state().filter, drawing_state().global_alpha, drawing_state().current_compositing_and_blending_operator); did_draw(destination_rect); } @@ -412,7 +412,7 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path) for (auto const& dash : state.dash_list) { dash_array.append(static_cast(dash)); } - painter->stroke_path(path, state.stroke_style.to_gfx_paint_style(), state.filters, state.line_width, state.global_alpha, state.current_compositing_and_blending_operator, line_cap, line_join, state.miter_limit, dash_array, state.line_dash_offset); + painter->stroke_path(path, state.stroke_style.to_gfx_paint_style(), state.filter, state.line_width, state.global_alpha, state.current_compositing_and_blending_operator, line_cap, line_join, state.miter_limit, dash_array, state.line_dash_offset); did_draw(path.bounding_box()); } @@ -448,7 +448,7 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::Winding auto path_to_fill = path; path_to_fill.close_all_subpaths(); auto& state = this->drawing_state(); - painter->fill_path(path_to_fill, state.fill_style.to_gfx_paint_style(), state.filters, state.global_alpha, state.current_compositing_and_blending_operator, winding_rule); + painter->fill_path(path_to_fill, state.fill_style.to_gfx_paint_style(), state.filter, state.global_alpha, state.current_compositing_and_blending_operator, winding_rule); did_draw(path_to_fill.bounding_box()); } @@ -563,7 +563,7 @@ void CanvasRenderingContext2D::put_image_data(ImageData& image_data, float x, fl Gfx::ImmutableBitmap::create(image_data.bitmap(), Gfx::AlphaType::Unpremultiplied), image_data.bitmap().rect(), Gfx::ScalingMode::NearestNeighbor, - drawing_state().filters, + drawing_state().filter, 1.0f, Gfx::CompositingAndBlendingOperator::SourceOver); did_draw(dst_rect); @@ -1040,21 +1040,21 @@ void CanvasRenderingContext2D::paint_shadow_for_stroke_internal(Gfx::Path const& String CanvasRenderingContext2D::filter() const { - if (!drawing_state().filters_string.has_value()) { + if (!drawing_state().filter_string.has_value()) { return String::from_utf8_without_validation("none"sv.bytes()); } - return drawing_state().filters_string.value(); + return drawing_state().filter_string.value(); } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-filter void CanvasRenderingContext2D::set_filter(String filter) { - drawing_state().filters.clear(); + drawing_state().filter.clear(); // 1. If the given value is "none", then set this's current filter to "none" and return. if (filter == "none"sv) { - drawing_state().filters_string.clear(); + drawing_state().filter_string.clear(); return; } @@ -1069,8 +1069,6 @@ void CanvasRenderingContext2D::set_filter(String filter) if (style_value && style_value->is_filter_value_list()) { auto filter_value_list = style_value->as_filter_value_list().filter_value_list(); - drawing_state().filters.grow_capacity(filter_value_list.size()); - // Note: The layout must be updated to make sure the canvas's layout node isn't null. canvas_element().document().update_layout(DOM::UpdateLayoutReason::CanvasRenderingContext2DSetFilter); auto layout_node = canvas_element().layout_node(); @@ -1081,15 +1079,27 @@ void CanvasRenderingContext2D::set_filter(String filter) item.visit( [&](CSS::FilterOperation::Blur const& blur_filter) { float radius = blur_filter.resolved_radius(*layout_node); - drawing_state().filters.append(Gfx::BlurFilter { radius }); + auto new_filter = Gfx::Filter::blur(radius); + + drawing_state().filter = drawing_state().filter.has_value() + ? Gfx::Filter::compose(new_filter, *drawing_state().filter) + : new_filter; }, [&](CSS::FilterOperation::Color const& color) { float amount = color.resolved_amount(); - drawing_state().filters.append(Gfx::ColorFilter { color.operation, amount }); + auto new_filter = Gfx::Filter::color(color.operation, amount); + + drawing_state().filter = drawing_state().filter.has_value() + ? Gfx::Filter::compose(new_filter, *drawing_state().filter) + : new_filter; }, [&](CSS::FilterOperation::HueRotate const& hue_rotate) { float angle = hue_rotate.angle_degrees(*layout_node); - drawing_state().filters.append(Gfx::HueRotateFilter { angle }); + auto new_filter = Gfx::Filter::hue_rotate(angle); + + drawing_state().filter = drawing_state().filter.has_value() + ? Gfx::Filter::compose(new_filter, *drawing_state().filter) + : new_filter; }, [&](CSS::FilterOperation::DropShadow const& drop_shadow) { auto resolution_context = CSS::Length::ResolutionContext::for_layout_node(*layout_node); @@ -1108,11 +1118,15 @@ void CanvasRenderingContext2D::set_filter(String filter) auto color = drop_shadow.color.value_or(Gfx::Color { 0, 0, 0, 255 }); - drawing_state().filters.append(Gfx::DropShadowFilter { offset_x, offset_y, radius, color }); + auto new_filter = Gfx::Filter::drop_shadow(offset_x, offset_y, radius, color); + + drawing_state().filter = drawing_state().filter.has_value() + ? Gfx::Filter::compose(new_filter, *drawing_state().filter) + : new_filter; }); } - drawing_state().filters_string = move(filter); + drawing_state().filter_string = move(filter); } // 3. If parsedValue is failure, then return. diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index 1e4f9e5a3e2..51c5c714444 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -220,7 +220,7 @@ bool Node::establishes_stacking_context() const // [CSS21] and a Containing Block for absolute and fixed position descendants, unless the // element it applies to is a document root element in the current browsing context. // Spec Note: This rule works in the same way as for the filter property. - if (!computed_values().backdrop_filter().is_empty() || !computed_values().filter().is_empty()) + if (computed_values().backdrop_filter().has_value() || computed_values().filter().has_value()) return true; // Element with any of the following properties with value other than none: @@ -578,13 +578,16 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style) computed_values.set_order(computed_style.order()); computed_values.set_clip(computed_style.clip()); - auto resolve_filter = [this](CSS::Filter const& computed_filter) -> Vector { - Vector resolved_filter; + auto resolve_filter = [this](CSS::Filter const& computed_filter) -> Optional { + Optional resolved_filter; for (auto const& filter : computed_filter.filters()) { filter.visit( [&](CSS::FilterOperation::Blur const& blur) { - resolved_filter.append(Gfx::BlurFilter { - .radius = blur.resolved_radius(*this) }); + auto new_filter = Gfx::Filter::blur(blur.resolved_radius(*this)); + + resolved_filter = resolved_filter.has_value() + ? Gfx::Filter::compose(new_filter, *resolved_filter) + : new_filter; }, [&](CSS::FilterOperation::DropShadow const& drop_shadow) { CSS::CalculationResolutionContext context { @@ -595,19 +598,27 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style) }; // The default value for omitted values is missing length values set to 0 // and the missing used color is taken from the color property. - resolved_filter.append(Gfx::DropShadowFilter { - .offset_x = to_px(drop_shadow.offset_x), - .offset_y = to_px(drop_shadow.offset_y), - .radius = drop_shadow.radius.has_value() ? to_px(*drop_shadow.radius) : 0.0f, - .color = drop_shadow.color.has_value() ? *drop_shadow.color : this->computed_values().color() }); + auto new_filter = Gfx::Filter::drop_shadow(to_px(drop_shadow.offset_x), + to_px(drop_shadow.offset_y), + drop_shadow.radius.has_value() ? to_px(*drop_shadow.radius) : 0.0f, drop_shadow.color.has_value() ? *drop_shadow.color : this->computed_values().color()); + + resolved_filter = resolved_filter.has_value() + ? Gfx::Filter::compose(new_filter, *resolved_filter) + : new_filter; }, [&](CSS::FilterOperation::Color const& color_operation) { - resolved_filter.append(Gfx::ColorFilter { - .type = color_operation.operation, - .amount = color_operation.resolved_amount() }); + auto new_filter = Gfx::Filter::color(color_operation.operation, color_operation.resolved_amount()); + + resolved_filter = resolved_filter.has_value() + ? Gfx::Filter::compose(new_filter, *resolved_filter) + : new_filter; }, [&](CSS::FilterOperation::HueRotate const& hue_rotate) { - resolved_filter.append(Gfx::HueRotateFilter { .angle_degrees = hue_rotate.angle_degrees(*this) }); + auto new_filter = Gfx::Filter::hue_rotate(hue_rotate.angle_degrees(*this)); + + resolved_filter = resolved_filter.has_value() + ? Gfx::Filter::compose(new_filter, *resolved_filter) + : new_filter; }); } return resolved_filter; diff --git a/Libraries/LibWeb/Painting/Command.h b/Libraries/LibWeb/Painting/Command.h index 2c4ee283e56..2fc4bcef464 100644 --- a/Libraries/LibWeb/Painting/Command.h +++ b/Libraries/LibWeb/Painting/Command.h @@ -308,7 +308,7 @@ struct DrawLine { struct ApplyBackdropFilter { Gfx::IntRect backdrop_region; BorderRadiiData border_radii_data; - Vector backdrop_filter; + Optional backdrop_filter; [[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; } @@ -422,8 +422,8 @@ struct ApplyCompositeAndBlendingOperator { Gfx::CompositingAndBlendingOperator compositing_and_blending_operator; }; -struct ApplyFilters { - Vector filter; +struct ApplyFilter { + Gfx::Filter filter; }; struct ApplyTransform { @@ -483,7 +483,7 @@ using Command = Variant< PaintScrollBar, ApplyOpacity, ApplyCompositeAndBlendingOperator, - ApplyFilters, + ApplyFilter, ApplyTransform, ApplyMaskBitmap>; diff --git a/Libraries/LibWeb/Painting/DisplayList.cpp b/Libraries/LibWeb/Painting/DisplayList.cpp index 67f538c4985..64f9294e60e 100644 --- a/Libraries/LibWeb/Painting/DisplayList.cpp +++ b/Libraries/LibWeb/Painting/DisplayList.cpp @@ -142,7 +142,7 @@ void DisplayListPlayer::execute_impl(DisplayList& display_list, ScrollStateSnaps else HANDLE_COMMAND(PaintNestedDisplayList, paint_nested_display_list) else HANDLE_COMMAND(ApplyOpacity, apply_opacity) else HANDLE_COMMAND(ApplyCompositeAndBlendingOperator, apply_composite_and_blending_operator) - else HANDLE_COMMAND(ApplyFilters, apply_filters) + else HANDLE_COMMAND(ApplyFilter, apply_filters) else HANDLE_COMMAND(ApplyTransform, apply_transform) else HANDLE_COMMAND(ApplyMaskBitmap, apply_mask_bitmap) else VERIFY_NOT_REACHED(); diff --git a/Libraries/LibWeb/Painting/DisplayList.h b/Libraries/LibWeb/Painting/DisplayList.h index f7af7846251..a85cca088d6 100644 --- a/Libraries/LibWeb/Painting/DisplayList.h +++ b/Libraries/LibWeb/Painting/DisplayList.h @@ -69,7 +69,7 @@ private: virtual void paint_scrollbar(PaintScrollBar const&) = 0; virtual void apply_opacity(ApplyOpacity const&) = 0; virtual void apply_composite_and_blending_operator(ApplyCompositeAndBlendingOperator const&) = 0; - virtual void apply_filters(ApplyFilters const&) = 0; + virtual void apply_filters(ApplyFilter const&) = 0; virtual void apply_transform(ApplyTransform const&) = 0; virtual void apply_mask_bitmap(ApplyMaskBitmap const&) = 0; virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0; diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index caaf96dbb70..a34e558893d 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -784,8 +784,8 @@ void DisplayListPlayerSkia::apply_backdrop_filter(ApplyBackdropFilter const& com canvas.clipRect(rect); ScopeGuard guard = [&] { canvas.restore(); }; - for (auto const& filter : command.backdrop_filter) { - auto image_filter = to_skia_image_filter(filter); + if (command.backdrop_filter.has_value()) { + auto image_filter = to_skia_image_filter(command.backdrop_filter.value()); canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, nullptr, image_filter.get(), 0)); canvas.restore(); } @@ -1036,23 +1036,9 @@ void DisplayListPlayerSkia::apply_composite_and_blending_operator(ApplyComposite canvas.saveLayer(nullptr, &paint); } -void DisplayListPlayerSkia::apply_filters(ApplyFilters const& command) +void DisplayListPlayerSkia::apply_filters(ApplyFilter const& command) { - if (command.filter.is_empty()) { - return; - } - sk_sp image_filter; - auto append_filter = [&image_filter](auto new_filter) { - if (image_filter) - image_filter = SkImageFilters::Compose(new_filter, image_filter); - else - image_filter = new_filter; - }; - - // Apply filters in order - for (auto filter : command.filter) { - append_filter(to_skia_image_filter(filter)); - } + sk_sp image_filter = to_skia_image_filter(command.filter); SkPaint paint; paint.setImageFilter(image_filter); diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h index b11642156f4..1db3786cf28 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h @@ -56,7 +56,7 @@ private: void paint_nested_display_list(PaintNestedDisplayList const&) override; void apply_opacity(ApplyOpacity const&) override; void apply_composite_and_blending_operator(ApplyCompositeAndBlendingOperator const&) override; - void apply_filters(ApplyFilters const&) override; + void apply_filters(ApplyFilter const&) override; void apply_transform(ApplyTransform const&) override; void apply_mask_bitmap(ApplyMaskBitmap const&) override; diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index 11b225d9d83..83581304de9 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -317,7 +317,7 @@ void DisplayListRecorder::pop_stacking_context() append(PopStackingContext {}); } -void DisplayListRecorder::apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Vector const& backdrop_filter) +void DisplayListRecorder::apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Gfx::Filter const& backdrop_filter) { if (backdrop_region.is_empty()) return; @@ -420,9 +420,9 @@ void DisplayListRecorder::apply_compositing_and_blending_operator(Gfx::Compositi append(ApplyCompositeAndBlendingOperator { .compositing_and_blending_operator = compositing_and_blending_operator }); } -void DisplayListRecorder::apply_filters(Vector filter) +void DisplayListRecorder::apply_filter(Gfx::Filter filter) { - append(ApplyFilters { .filter = move(filter) }); + append(ApplyFilter { .filter = move(filter) }); } void DisplayListRecorder::apply_transform(Gfx::FloatPoint origin, Gfx::FloatMatrix4x4 matrix) diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Libraries/LibWeb/Painting/DisplayListRecorder.h index f7c8c029e0d..19b3cf996ac 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -134,7 +134,7 @@ public: void add_rounded_rect_clip(CornerRadii corner_radii, Gfx::IntRect border_rect, CornerClip corner_clip); void add_mask(RefPtr display_list, Gfx::IntRect rect); - void apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Vector const& backdrop_filter); + void apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Gfx::Filter const& backdrop_filter); void paint_outer_box_shadow_params(PaintBoxShadowParams params); void paint_inner_box_shadow_params(PaintBoxShadowParams params); @@ -150,7 +150,7 @@ public: void apply_opacity(float opacity); void apply_compositing_and_blending_operator(Gfx::CompositingAndBlendingOperator compositing_and_blending_operator); - void apply_filters(Vector filter); + void apply_filter(Gfx::Filter filter); void apply_transform(Gfx::FloatPoint origin, Gfx::FloatMatrix4x4); void apply_mask_bitmap(Gfx::IntPoint origin, Gfx::ImmutableBitmap const&, Gfx::Bitmap::MaskKind); diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 593a255c99b..74645e9766d 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -572,14 +572,14 @@ void PaintableBox::paint_border(PaintContext& context) const void PaintableBox::paint_backdrop_filter(PaintContext& context) const { auto const& backdrop_filter = computed_values().backdrop_filter(); - if (backdrop_filter.is_empty()) { + if (!backdrop_filter.has_value()) { return; } auto backdrop_region = context.rounded_device_rect(absolute_border_box_rect()); auto border_radii_data = normalized_border_radii_data(); ScopedCornerRadiusClip corner_clipper { context, backdrop_region, border_radii_data }; - context.display_list_recorder().apply_backdrop_filter(backdrop_region.to_type(), border_radii_data, backdrop_filter); + context.display_list_recorder().apply_backdrop_filter(backdrop_region.to_type(), border_radii_data, backdrop_filter.value()); } void PaintableBox::paint_background(PaintContext& context) const diff --git a/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp b/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp index 084a9a6bb6e..61cf5806d6a 100644 --- a/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp +++ b/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp @@ -73,8 +73,8 @@ void SVGSVGPaintable::paint_svg_box(PaintContext& context, PaintableBox const& s context.display_list_recorder().apply_opacity(computed_values.opacity()); } - if (!filter.is_empty()) { - context.display_list_recorder().apply_filters(filter); + if (filter.has_value()) { + context.display_list_recorder().apply_filter(filter.value()); } if (compositing_and_blending_operator != Gfx::CompositingAndBlendingOperator::Normal) { @@ -114,7 +114,7 @@ void SVGSVGPaintable::paint_svg_box(PaintContext& context, PaintableBox const& s context.display_list_recorder().restore(); } - if (!filter.is_empty()) { + if (filter.has_value()) { context.display_list_recorder().restore(); } diff --git a/Libraries/LibWeb/Painting/StackingContext.cpp b/Libraries/LibWeb/Painting/StackingContext.cpp index e89c7085968..7e4e03039f4 100644 --- a/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Libraries/LibWeb/Painting/StackingContext.cpp @@ -348,8 +348,8 @@ void StackingContext::paint(PaintContext& context) const context.display_list_recorder().push_stacking_context(push_stacking_context_params); auto const& filter = computed_values.filter(); - if (!filter.is_empty()) { - context.display_list_recorder().apply_filters(paintable_box().computed_values().filter()); + if (filter.has_value()) { + context.display_list_recorder().apply_filter(paintable_box().computed_values().filter().value()); } if (auto mask_image = computed_values.mask_image()) { @@ -375,7 +375,7 @@ void StackingContext::paint(PaintContext& context) const paint_internal(context); context.display_list_recorder().pop_scroll_frame_id(); - if (!filter.is_empty()) { + if (filter.has_value()) { context.display_list_recorder().restore(); }