From 9fd12239923fb444716d5892dafe848bd71ec55f Mon Sep 17 00:00:00 2001 From: Lucien Fiorini Date: Wed, 18 Dec 2024 11:34:25 +0100 Subject: [PATCH] LibWeb+LibGfx: Refactor CSS filters into LibGfx CSS filters work similarly to canvas filters, so it makes sense to have Gfx::Filter that can be used by both libraries in an analogous way as Gfx::Color. --- Libraries/LibGfx/CMakeLists.txt | 1 + Libraries/LibGfx/Filter.h | 43 ++++ Libraries/LibGfx/PainterSkia.cpp | 164 +++++++-------- Libraries/LibGfx/PathSkia.cpp | 12 +- Libraries/LibGfx/SkiaUtils.cpp | 143 +++++++++++++ Libraries/LibGfx/SkiaUtils.h | 65 +++++- Libraries/LibWeb/CSS/ComputedValues.h | 45 +---- Libraries/LibWeb/CSS/Parser/Parser.cpp | 2 +- .../StyleValues/FilterValueListStyleValue.cpp | 14 +- .../StyleValues/FilterValueListStyleValue.h | 11 +- Libraries/LibWeb/Layout/Node.cpp | 20 +- Libraries/LibWeb/Painting/Command.h | 4 +- .../LibWeb/Painting/DisplayListPlayerSkia.cpp | 188 +----------------- .../LibWeb/Painting/DisplayListRecorder.cpp | 6 +- .../LibWeb/Painting/DisplayListRecorder.h | 4 +- Libraries/LibWeb/Painting/PaintableBox.cpp | 2 +- Libraries/LibWeb/Painting/SVGSVGPaintable.cpp | 4 +- Libraries/LibWeb/Painting/StackingContext.cpp | 4 +- 18 files changed, 365 insertions(+), 367 deletions(-) create mode 100644 Libraries/LibGfx/Filter.h create mode 100644 Libraries/LibGfx/SkiaUtils.cpp diff --git a/Libraries/LibGfx/CMakeLists.txt b/Libraries/LibGfx/CMakeLists.txt index d099b079bc7..b8feb18e36f 100644 --- a/Libraries/LibGfx/CMakeLists.txt +++ b/Libraries/LibGfx/CMakeLists.txt @@ -41,6 +41,7 @@ set(SOURCES ImageFormats/WebPWriterLossless.cpp ImageFormats/AVIFLoader.cpp ImmutableBitmap.cpp + SkiaUtils.cpp PaintingSurface.cpp Palette.cpp Path.cpp diff --git a/Libraries/LibGfx/Filter.h b/Libraries/LibGfx/Filter.h new file mode 100644 index 00000000000..c59037ac61e --- /dev/null +++ b/Libraries/LibGfx/Filter.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Lucien Fiorini + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Gfx { + +struct BlurFilter { + float radius; +}; + +struct DropShadowFilter { + float offset_x; + float offset_y; + float radius; + Gfx::Color color; +}; + +struct HueRotateFilter { + float angle_degrees; +}; + +struct ColorFilter { + enum class Type { + Brightness, + Contrast, + Grayscale, + Invert, + Opacity, + Saturate, + Sepia + } type; + float amount; +}; + +using Filter = Variant; +} diff --git a/Libraries/LibGfx/PainterSkia.cpp b/Libraries/LibGfx/PainterSkia.cpp index b9dece64b21..67c05266eea 100644 --- a/Libraries/LibGfx/PainterSkia.cpp +++ b/Libraries/LibGfx/PainterSkia.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -34,30 +35,75 @@ struct PainterSkia::Impl { } }; -static constexpr SkRect to_skia_rect(auto const& rect) +static void apply_paint_style(SkPaint& paint, Gfx::PaintStyle const& style) { - return SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); -} + if (is(style)) { + auto const& solid_color = static_cast(style); + auto color = solid_color.sample_color(Gfx::IntPoint(0, 0)); -static constexpr SkColor to_skia_color(Gfx::Color const& color) -{ - return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue()); -} + paint.setColor(to_skia_color(color)); + } else if (is(style)) { + auto const& linear_gradient = static_cast(style); + auto const& color_stops = linear_gradient.color_stops(); -static SkPath to_skia_path(Gfx::Path const& path) -{ - return static_cast(path.impl()).sk_path(); -} + Vector colors; + colors.ensure_capacity(color_stops.size()); + Vector positions; + positions.ensure_capacity(color_stops.size()); + for (auto const& color_stop : color_stops) { + colors.append(to_skia_color(color_stop.color)); + positions.append(color_stop.position); + } -static SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule) -{ - switch (winding_rule) { - case Gfx::WindingRule::Nonzero: - return SkPathFillType::kWinding; - case Gfx::WindingRule::EvenOdd: - return SkPathFillType::kEvenOdd; + Array points; + points[0] = to_skia_point(linear_gradient.start_point()); + points[1] = to_skia_point(linear_gradient.end_point()); + + SkMatrix matrix; + auto shader = SkGradientShader::MakeLinear(points.data(), colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, 0, &matrix); + paint.setShader(shader); + } else if (is(style)) { + auto const& radial_gradient = static_cast(style); + auto const& color_stops = radial_gradient.color_stops(); + + Vector colors; + colors.ensure_capacity(color_stops.size()); + Vector positions; + positions.ensure_capacity(color_stops.size()); + for (auto const& color_stop : color_stops) { + colors.append(to_skia_color(color_stop.color)); + positions.append(color_stop.position); + } + + auto start_center = radial_gradient.start_center(); + auto end_center = radial_gradient.end_center(); + auto start_radius = radial_gradient.start_radius(); + auto end_radius = radial_gradient.end_radius(); + + auto start_sk_point = to_skia_point(start_center); + auto end_sk_point = to_skia_point(end_center); + + SkMatrix matrix; + auto shader = SkGradientShader::MakeTwoPointConical(start_sk_point, start_radius, end_sk_point, end_radius, colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, 0, &matrix); + paint.setShader(shader); } - VERIFY_NOT_REACHED(); +} + +static void apply_filters(SkPaint& paint, ReadonlySpan filters) +{ + for (auto const& filter : filters) { + paint.setImageFilter(to_skia_image_filter(filter)); + } +} + +static SkPaint to_skia_paint(Gfx::PaintStyle const& style, ReadonlySpan filters) +{ + SkPaint paint; + + apply_paint_style(paint, style); + apply_filters(paint, filters); + + return paint; } PainterSkia::PainterSkia(NonnullRefPtr painting_surface) @@ -82,21 +128,6 @@ void PainterSkia::fill_rect(Gfx::FloatRect const& rect, Color color) impl().canvas()->drawRect(to_skia_rect(rect), paint); } -static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode) -{ - switch (scaling_mode) { - case Gfx::ScalingMode::NearestNeighbor: - return SkSamplingOptions(SkFilterMode::kNearest); - case Gfx::ScalingMode::BilinearBlend: - case Gfx::ScalingMode::SmoothPixels: - return SkSamplingOptions(SkFilterMode::kLinear); - case Gfx::ScalingMode::BoxSampling: - return SkSamplingOptions(SkCubicResampler::Mitchell()); - default: - VERIFY_NOT_REACHED(); - } -} - void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, float global_alpha) { SkPaint paint; @@ -121,67 +152,6 @@ void PainterSkia::set_transform(Gfx::AffineTransform const& transform) impl().canvas()->setMatrix(matrix); } -static SkPoint to_skia_point(auto const& point) -{ - return SkPoint::Make(point.x(), point.y()); -} - -static SkPaint to_skia_paint(Gfx::PaintStyle const& style) -{ - if (is(style)) { - auto const& linear_gradient = static_cast(style); - auto const& color_stops = linear_gradient.color_stops(); - - SkPaint paint; - Vector colors; - colors.ensure_capacity(color_stops.size()); - Vector positions; - positions.ensure_capacity(color_stops.size()); - for (auto const& color_stop : color_stops) { - colors.append(to_skia_color(color_stop.color)); - positions.append(color_stop.position); - } - - Array points; - points[0] = to_skia_point(linear_gradient.start_point()); - points[1] = to_skia_point(linear_gradient.end_point()); - - SkMatrix matrix; - auto shader = SkGradientShader::MakeLinear(points.data(), colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, 0, &matrix); - paint.setShader(shader); - return paint; - } - - if (is(style)) { - auto const& radial_gradient = static_cast(style); - auto const& color_stops = radial_gradient.color_stops(); - - SkPaint paint; - Vector colors; - colors.ensure_capacity(color_stops.size()); - Vector positions; - positions.ensure_capacity(color_stops.size()); - for (auto const& color_stop : color_stops) { - colors.append(to_skia_color(color_stop.color)); - positions.append(color_stop.position); - } - - auto start_center = radial_gradient.start_center(); - auto end_center = radial_gradient.end_center(); - auto start_radius = radial_gradient.start_radius(); - auto end_radius = radial_gradient.end_radius(); - - auto start_sk_point = to_skia_point(start_center); - auto end_sk_point = to_skia_point(end_center); - - SkMatrix matrix; - auto shader = SkGradientShader::MakeTwoPointConical(start_sk_point, start_radius, end_sk_point, end_radius, colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, 0, &matrix); - paint.setShader(shader); - return paint; - } - return {}; -} - void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness) { // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. @@ -220,7 +190,7 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& pain return; auto sk_path = to_skia_path(path); - auto paint = to_skia_paint(paint_style); + auto paint = to_skia_paint(paint_style, {}); paint.setAntiAlias(true); paint.setAlphaf(global_alpha); paint.setStyle(SkPaint::Style::kStroke_Style); @@ -253,7 +223,7 @@ void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_ { auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); - auto paint = to_skia_paint(paint_style); + auto paint = to_skia_paint(paint_style, {}); paint.setAntiAlias(true); paint.setAlphaf(global_alpha); impl().canvas()->drawPath(sk_path, paint); diff --git a/Libraries/LibGfx/PathSkia.cpp b/Libraries/LibGfx/PathSkia.cpp index a4dc0864e37..57bdb330b6e 100644 --- a/Libraries/LibGfx/PathSkia.cpp +++ b/Libraries/LibGfx/PathSkia.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -205,17 +206,6 @@ Gfx::FloatRect PathImplSkia::bounding_box() const return { bounds.fLeft, bounds.fTop, bounds.fRight - bounds.fLeft, bounds.fBottom - bounds.fTop }; } -static SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule) -{ - switch (winding_rule) { - case Gfx::WindingRule::Nonzero: - return SkPathFillType::kWinding; - case Gfx::WindingRule::EvenOdd: - return SkPathFillType::kEvenOdd; - } - VERIFY_NOT_REACHED(); -} - bool PathImplSkia::contains(FloatPoint point, Gfx::WindingRule winding_rule) const { SkPath temp_path = *m_path; diff --git a/Libraries/LibGfx/SkiaUtils.cpp b/Libraries/LibGfx/SkiaUtils.cpp new file mode 100644 index 00000000000..1e952120fd6 --- /dev/null +++ b/Libraries/LibGfx/SkiaUtils.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024, Lucien Fiorini + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +SkPath to_skia_path(Path const& path) +{ + return static_cast(path.impl()).sk_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); + }); +} + +} diff --git a/Libraries/LibGfx/SkiaUtils.h b/Libraries/LibGfx/SkiaUtils.h index f4fc02f4fb1..656bc02ab55 100644 --- a/Libraries/LibGfx/SkiaUtils.h +++ b/Libraries/LibGfx/SkiaUtils.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Pavel Shliak + * Copyright (c) 2024, Lucien Fiorini * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,11 +9,19 @@ #include #include +#include +#include +#include +#include +#include #include +#include +#include +#include namespace Gfx { -static SkColorType to_skia_color_type(Gfx::BitmapFormat format) +constexpr SkColorType to_skia_color_type(Gfx::BitmapFormat format) { switch (format) { case Gfx::BitmapFormat::Invalid: @@ -28,4 +37,58 @@ static SkColorType to_skia_color_type(Gfx::BitmapFormat format) VERIFY_NOT_REACHED(); } +constexpr SkRect to_skia_rect(auto const& rect) +{ + return SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); +} + +constexpr SkColor to_skia_color(Gfx::Color const& color) +{ + return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue()); +} + +constexpr SkColor4f to_skia_color4f(Color const& color) +{ + return { + .fR = color.red() / 255.0f, + .fG = color.green() / 255.0f, + .fB = color.blue() / 255.0f, + .fA = color.alpha() / 255.0f, + }; +} + +constexpr SkPoint to_skia_point(auto const& point) +{ + return SkPoint::Make(point.x(), point.y()); +} + +constexpr SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule) +{ + switch (winding_rule) { + case Gfx::WindingRule::Nonzero: + return SkPathFillType::kWinding; + case Gfx::WindingRule::EvenOdd: + return SkPathFillType::kEvenOdd; + } + VERIFY_NOT_REACHED(); +} + +constexpr SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode) +{ + switch (scaling_mode) { + case Gfx::ScalingMode::NearestNeighbor: + return SkSamplingOptions(SkFilterMode::kNearest); + case Gfx::ScalingMode::BilinearBlend: + case Gfx::ScalingMode::SmoothPixels: + return SkSamplingOptions(SkFilterMode::kLinear); + case Gfx::ScalingMode::BoxSampling: + return SkSamplingOptions(SkCubicResampler::Mitchell()); + default: + VERIFY_NOT_REACHED(); + } +} + +SkPath to_skia_path(Path const& path); +sk_sp to_skia_image_filter(Gfx::Filter const& filter); + } diff --git a/Libraries/LibWeb/CSS/ComputedValues.h b/Libraries/LibWeb/CSS/ComputedValues.h index be5324bf5f5..31c19521476 100644 --- a/Libraries/LibWeb/CSS/ComputedValues.h +++ b/Libraries/LibWeb/CSS/ComputedValues.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -57,34 +58,6 @@ struct QuotesData { Vector> strings {}; }; -struct ResolvedFilter { - struct Blur { - float radius; - }; - - struct DropShadow { - double offset_x; - double offset_y; - double radius; - Gfx::Color color; - }; - - struct HueRotate { - float angle_degrees; - }; - - struct Color { - FilterOperation::Color::Type type; - float amount; - }; - - using FilterFunction = Variant; - - bool is_none() const { return filters.size() == 0; } - - Vector filters; -}; - struct ObjectPosition { PositionEdge edge_x { PositionEdge::Left }; CSS::LengthPercentage offset_x { Percentage(50) }; @@ -122,8 +95,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 CSS::ResolvedFilter backdrop_filter() { return ResolvedFilter { .filters = {} }; } - static CSS::ResolvedFilter filter() { return ResolvedFilter { .filters = {} }; } + static Vector backdrop_filter() { return {}; } + static Vector filter() { return {}; } static Color background_color() { return Color::Transparent; } static CSS::ListStyleType list_style_type() { return CSS::ListStyleType::Disc; } static CSS::ListStylePosition list_style_position() { return CSS::ListStylePosition::Outside; } @@ -416,8 +389,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; } - CSS::ResolvedFilter const& backdrop_filter() const { return m_noninherited.backdrop_filter; } - CSS::ResolvedFilter const& filter() const { return m_noninherited.filter; } + Vector const& backdrop_filter() const { return m_noninherited.backdrop_filter; } + Vector 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; } @@ -636,8 +609,8 @@ protected: CSS::LengthBox inset { InitialValues::inset() }; CSS::LengthBox margin { InitialValues::margin() }; CSS::LengthBox padding { InitialValues::padding() }; - CSS::ResolvedFilter backdrop_filter { InitialValues::backdrop_filter() }; - CSS::ResolvedFilter filter { InitialValues::filter() }; + Vector backdrop_filter { InitialValues::backdrop_filter() }; + Vector filter { InitialValues::filter() }; BorderData border_left; BorderData border_top; BorderData border_right; @@ -793,8 +766,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(CSS::ResolvedFilter backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); } - void set_filter(CSS::ResolvedFilter filter) { m_noninherited.filter = move(filter); } + 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_border_bottom_left_radius(CSS::BorderRadiusData value) { m_noninherited.has_noninitial_border_radii = true; diff --git a/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Libraries/LibWeb/CSS/Parser/Parser.cpp index 9fd9f8b31d2..6a430aa3312 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -5425,7 +5425,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 303c32263c5..0d9b65b7487 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp @@ -92,19 +92,19 @@ String FilterValueListStyleValue::to_string(SerializationMode) const builder.appendff("{}(", [&] { switch (color.operation) { - case FilterOperation::Color::Type::Brightness: + case Gfx::ColorFilter::Type::Brightness: return "brightness"sv; - case FilterOperation::Color::Type::Contrast: + case Gfx::ColorFilter::Type::Contrast: return "contrast"sv; - case FilterOperation::Color::Type::Grayscale: + case Gfx::ColorFilter::Type::Grayscale: return "grayscale"sv; - case FilterOperation::Color::Type::Invert: + case Gfx::ColorFilter::Type::Invert: return "invert"sv; - case FilterOperation::Color::Type::Opacity: + case Gfx::ColorFilter::Type::Opacity: return "opacity"sv; - case FilterOperation::Color::Type::Saturate: + case Gfx::ColorFilter::Type::Saturate: return "saturate"sv; - case FilterOperation::Color::Type::Sepia: + case Gfx::ColorFilter::Type::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 ac450408833..1be7e71ddc3 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -44,15 +45,7 @@ struct HueRotate { }; struct Color { - enum class Type { - Brightness, - Contrast, - Grayscale, - Invert, - Opacity, - Saturate, - Sepia - } operation; + Gfx::ColorFilter::Type operation; Optional amount {}; float resolved_amount() const; bool operator==(Color const&) const = default; diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index e5c2c30e214..dc88a791ccd 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -204,7 +204,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_none() || !computed_values().filter().is_none()) + if (!computed_values().backdrop_filter().is_empty() || !computed_values().filter().is_empty()) return true; // Element with any of the following properties with value other than none: @@ -541,31 +541,31 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& 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) -> CSS::ResolvedFilter { - CSS::ResolvedFilter resolved_filter; + auto resolve_filter = [this](CSS::Filter const& computed_filter) -> Vector { + Vector resolved_filter; for (auto const& filter : computed_filter.filters()) { filter.visit( [&](CSS::FilterOperation::Blur const& blur) { - resolved_filter.filters.append(CSS::ResolvedFilter::Blur { + resolved_filter.append(Gfx::BlurFilter { .radius = blur.resolved_radius(*this) }); }, [&](CSS::FilterOperation::DropShadow const& drop_shadow) { auto context = CSS::Length::ResolutionContext::for_layout_node(*this); // 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.filters.append(CSS::ResolvedFilter::DropShadow { - .offset_x = drop_shadow.offset_x.resolved(context).to_px(*this).to_double(), - .offset_y = drop_shadow.offset_y.resolved(context).to_px(*this).to_double(), - .radius = drop_shadow.radius.has_value() ? drop_shadow.radius->resolved(context).to_px(*this).to_double() : 0.0, + resolved_filter.append(Gfx::DropShadowFilter { + .offset_x = static_cast(drop_shadow.offset_x.resolved(context).to_px(*this).to_double()), + .offset_y = static_cast(drop_shadow.offset_y.resolved(context).to_px(*this).to_double()), + .radius = static_cast(drop_shadow.radius.has_value() ? drop_shadow.radius->resolved(context).to_px(*this).to_double() : 0.0), .color = drop_shadow.color.has_value() ? *drop_shadow.color : this->computed_values().color() }); }, [&](CSS::FilterOperation::Color const& color_operation) { - resolved_filter.filters.append(CSS::ResolvedFilter::Color { + resolved_filter.append(Gfx::ColorFilter { .type = color_operation.operation, .amount = color_operation.resolved_amount() }); }, [&](CSS::FilterOperation::HueRotate const& hue_rotate) { - resolved_filter.filters.append(CSS::ResolvedFilter::HueRotate { .angle_degrees = hue_rotate.angle_degrees(*this) }); + resolved_filter.append(Gfx::HueRotateFilter { .angle_degrees = hue_rotate.angle_degrees(*this) }); }); } return resolved_filter; diff --git a/Libraries/LibWeb/Painting/Command.h b/Libraries/LibWeb/Painting/Command.h index 0569cfb0e53..b8db2edaa50 100644 --- a/Libraries/LibWeb/Painting/Command.h +++ b/Libraries/LibWeb/Painting/Command.h @@ -300,7 +300,7 @@ struct DrawLine { struct ApplyBackdropFilter { Gfx::IntRect backdrop_region; BorderRadiiData border_radii_data; - CSS::ResolvedFilter backdrop_filter; + Vector backdrop_filter; [[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; } @@ -408,7 +408,7 @@ struct ApplyOpacity { }; struct ApplyFilters { - CSS::ResolvedFilter filter; + Vector filter; }; struct ApplyTransform { diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index 97cf904225c..7044e86b9e5 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -64,169 +65,6 @@ DisplayListPlayerSkia::~DisplayListPlayerSkia() } } -static SkPoint to_skia_point(auto const& point) -{ - return SkPoint::Make(point.x(), point.y()); -} - -static SkRect to_skia_rect(auto const& rect) -{ - return SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); -} - -static SkColor to_skia_color(Gfx::Color const& color) -{ - return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue()); -} - -static SkColor4f to_skia_color4f(Gfx::Color const& color) -{ - return { - .fR = color.red() / 255.0f, - .fG = color.green() / 255.0f, - .fB = color.blue() / 255.0f, - .fA = color.alpha() / 255.0f, - }; -} - -static sk_sp to_skia_image_filter(CSS::ResolvedFilter::FilterFunction const& function) -{ - // See: https://drafts.fxtf.org/filter-effects-1/#supported-filter-functions - return function.visit( - [&](CSS::ResolvedFilter::Blur const& blur_filter) { - return SkImageFilters::Blur(blur_filter.radius, blur_filter.radius, nullptr); - }, - [&](CSS::ResolvedFilter::Color const& color) { - auto amount = color.amount; - - // Matrices are taken from https://drafts.fxtf.org/filter-effects-1/#FilterPrimitiveRepresentation - sk_sp color_filter; - switch (color.type) { - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - break; - } - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - break; - } - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes); - break; - } - case CSS::FilterOperation::Color::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 - }; - color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo); - break; - } - default: - VERIFY_NOT_REACHED(); - } - - return SkImageFilters::ColorFilter(color_filter, nullptr); - }, - [&](CSS::ResolvedFilter::HueRotate const& hue_rotate) { - float radians = AK::to_radians(hue_rotate.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 SkImageFilters::ColorFilter(color_filter, nullptr); - }, - [&](CSS::ResolvedFilter::DropShadow const& command) { - auto shadow_color = to_skia_color(command.color); - return SkImageFilters::DropShadow(command.offset_x, command.offset_y, command.radius, command.radius, shadow_color, nullptr); - }); -} - -static SkPath to_skia_path(Gfx::Path const& path) -{ - return static_cast(path.impl()).sk_path(); -} - -static SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule) -{ - switch (winding_rule) { - case Gfx::WindingRule::Nonzero: - return SkPathFillType::kWinding; - case Gfx::WindingRule::EvenOdd: - return SkPathFillType::kEvenOdd; - } - VERIFY_NOT_REACHED(); -} - static SkRRect to_skia_rrect(auto const& rect, CornerRadii const& corner_radii) { SkRRect rrect; @@ -254,21 +92,6 @@ static SkMatrix to_skia_matrix(Gfx::AffineTransform const& affine_transform) return matrix; } -static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode) -{ - switch (scaling_mode) { - case Gfx::ScalingMode::NearestNeighbor: - case Gfx::ScalingMode::SmoothPixels: - return SkSamplingOptions(SkFilterMode::kNearest); - case Gfx::ScalingMode::BilinearBlend: - return SkSamplingOptions(SkFilterMode::kLinear); - case Gfx::ScalingMode::BoxSampling: - return SkSamplingOptions(SkCubicResampler::Mitchell()); - default: - VERIFY_NOT_REACHED(); - } -} - Gfx::PaintingSurface& DisplayListPlayerSkia::surface() const { return *m_surface; @@ -916,8 +739,8 @@ void DisplayListPlayerSkia::apply_backdrop_filter(ApplyBackdropFilter const& com canvas.clipRect(rect); ScopeGuard guard = [&] { canvas.restore(); }; - for (auto const& filter_function : command.backdrop_filter.filters) { - auto image_filter = to_skia_image_filter(filter_function); + for (auto const& filter : command.backdrop_filter) { + auto image_filter = to_skia_image_filter(filter); canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, nullptr, image_filter.get(), 0)); canvas.restore(); } @@ -1091,7 +914,7 @@ void DisplayListPlayerSkia::apply_opacity(ApplyOpacity const& command) void DisplayListPlayerSkia::apply_filters(ApplyFilters const& command) { - if (command.filter.is_none()) { + if (command.filter.is_empty()) { return; } sk_sp image_filter; @@ -1103,7 +926,7 @@ void DisplayListPlayerSkia::apply_filters(ApplyFilters const& command) }; // Apply filters in order - for (auto filter : command.filter.filters) { + for (auto filter : command.filter) { append_filter(to_skia_image_filter(filter)); } @@ -1111,7 +934,6 @@ void DisplayListPlayerSkia::apply_filters(ApplyFilters const& command) paint.setImageFilter(image_filter); auto& canvas = surface().canvas(); canvas.saveLayer(nullptr, &paint); - return; } void DisplayListPlayerSkia::apply_transform(ApplyTransform const& command) diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index a02b88ecc71..0f862dda0cb 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -310,7 +310,7 @@ void DisplayListRecorder::pop_stacking_context() append(PopStackingContext {}); } -void DisplayListRecorder::apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, CSS::ResolvedFilter const& backdrop_filter) +void DisplayListRecorder::apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Vector const& backdrop_filter) { if (backdrop_region.is_empty()) return; @@ -407,9 +407,9 @@ void DisplayListRecorder::apply_opacity(float opacity) append(ApplyOpacity { .opacity = opacity }); } -void DisplayListRecorder::apply_filters(CSS::ResolvedFilter filter) +void DisplayListRecorder::apply_filters(Vector filter) { - append(ApplyFilters { .filter = filter }); + append(ApplyFilters { .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 45b1f0b5479..50c7df5c1e9 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -133,7 +133,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, CSS::ResolvedFilter const& backdrop_filter); + void apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Vector const& backdrop_filter); void paint_outer_box_shadow_params(PaintBoxShadowParams params); void paint_inner_box_shadow_params(PaintBoxShadowParams params); @@ -148,7 +148,7 @@ public: void paint_scrollbar(int scroll_frame_id, Gfx::IntRect, CSSPixelFraction scroll_size, bool vertical); void apply_opacity(float opacity); - void apply_filters(CSS::ResolvedFilter filter); + void apply_filters(Vector 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 f79d1f41d07..308a5bbf0c4 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -470,7 +470,7 @@ 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_none()) { + if (backdrop_filter.is_empty()) { return; } diff --git a/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp b/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp index b1fa7dec5bb..23e6201483d 100644 --- a/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp +++ b/Libraries/LibWeb/Painting/SVGSVGPaintable.cpp @@ -67,7 +67,7 @@ void SVGSVGPaintable::paint_svg_box(PaintContext& context, PaintableBox const& s context.display_list_recorder().apply_opacity(computed_values.opacity()); } - if (!filter.is_none()) { + if (!filter.is_empty()) { context.display_list_recorder().apply_filters(filter); } @@ -95,7 +95,7 @@ void SVGSVGPaintable::paint_svg_box(PaintContext& context, PaintableBox const& s paint_descendants(context, svg_box, phase); - if (!filter.is_none()) { + if (!filter.is_empty()) { context.display_list_recorder().restore(); } diff --git a/Libraries/LibWeb/Painting/StackingContext.cpp b/Libraries/LibWeb/Painting/StackingContext.cpp index c602a5dded9..901c2883b9b 100644 --- a/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Libraries/LibWeb/Painting/StackingContext.cpp @@ -329,7 +329,7 @@ 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_none()) { + if (!filter.is_empty()) { context.display_list_recorder().apply_filters(paintable_box().computed_values().filter()); } @@ -354,7 +354,7 @@ void StackingContext::paint(PaintContext& context) const paint_internal(context); - if (!filter.is_none()) { + if (!filter.is_empty()) { context.display_list_recorder().restore(); }