1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +09:00

LibGfx+LibWeb: Turn Gfx::Filter into a SkImageFilter wrapper

This commit is contained in:
Lucien Fiorini 2025-05-11 12:44:54 +02:00 committed by Alexander Kalenik
parent 417f4edc46
commit 0fcb574041
Notes: github-actions[bot] 2025-06-01 21:23:19 +00:00
26 changed files with 412 additions and 256 deletions

View file

@ -9,6 +9,7 @@ set(SOURCES
Color.cpp
ColorSpace.cpp
Cursor.cpp
Filter.cpp
FontCascadeList.cpp
Font/Font.cpp
Font/FontData.cpp

224
Libraries/LibGfx/Filter.cpp Normal file
View file

@ -0,0 +1,224 @@
/*
* Copyright (c) 2025, Lucien Fiorini <lucienfiorini@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Filter.h>
#include <LibGfx/FilterImpl.h>
#include <LibGfx/SkiaUtils.h>
#include <core/SkBlendMode.h>
#include <core/SkColorFilter.h>
#include <effects/SkColorMatrix.h>
#include <effects/SkImageFilters.h>
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<FilterImpl>&& 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<Filter const&> input)
{
sk_sp<SkImageFilter> 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<u8>(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<Filter const&> input)
{
sk_sp<SkImageFilter> 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<Filter const&> input)
{
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
sk_sp<SkColorFilter> 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<Filter const&> input)
{
sk_sp<SkImageFilter> 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<Filter const&> input)
{
sk_sp<SkImageFilter> 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<Filter const&> input)
{
sk_sp<SkImageFilter> 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)));
}
}

View file

@ -1,33 +1,18 @@
/*
* Copyright (c) 2024, Lucien Fiorini <lucienfiorini@gmail.com>
* Copyright (c) 2024-2025, Lucien Fiorini <lucienfiorini@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Variant.h>
#include <AK/NonnullOwnPtr.h>
#include <LibGfx/Color.h>
#include <LibGfx/CompositingAndBlendingOperator.h>
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 {
enum class ColorFilterType {
Brightness,
Contrast,
Grayscale,
@ -35,10 +20,32 @@ struct ColorFilter {
Opacity,
Saturate,
Sepia
} type;
float amount;
};
using Filter = Variant<BlurFilter, DropShadowFilter, HueRotateFilter, ColorFilter>;
struct FilterImpl;
class Filter {
public:
Filter(Filter const&);
Filter& operator=(Filter const&);
~Filter();
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<Filter const&> input = {});
static Filter blur(float radius, Optional<Filter const&> input = {});
static Filter color(ColorFilterType type, float amount, Optional<Filter const&> input = {});
static Filter color_matrix(float matrix[20], Optional<Filter const&> input = {});
static Filter saturate(float value, Optional<Filter const&> input = {});
static Filter hue_rotate(float angle_degrees, Optional<Filter const&> input = {});
FilterImpl const& impl() const;
private:
Filter(NonnullOwnPtr<FilterImpl>&&);
NonnullOwnPtr<FilterImpl> m_impl;
};
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024-2025, Lucien Fiorini <lucienfiorini@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <core/SkColorFilter.h>
#include <effects/SkImageFilters.h>
namespace Gfx {
struct FilterImpl {
sk_sp<SkImageFilter> filter;
static NonnullOwnPtr<FilterImpl> create(sk_sp<SkImageFilter> filter)
{
return adopt_own(*new FilterImpl(move(filter)));
}
NonnullOwnPtr<FilterImpl> clone() const
{
return adopt_own(*new FilterImpl(filter));
}
};
}

View file

@ -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<Gfx::Filter> 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<Gfx::Filter> 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<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector<float> const&, float dash_offset) = 0;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector<float> 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<Gfx::Filter>, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) = 0;
virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) = 0;
virtual void set_transform(Gfx::AffineTransform const&) = 0;

View file

@ -17,10 +17,8 @@
#include <LibGfx/SkiaUtils.h>
#include <AK/TypeCasts.h>
#include <core/SkBlender.h>
#include <core/SkCanvas.h>
#include <core/SkPath.h>
#include <core/SkPathEffect.h>
#include <effects/SkBlurMaskFilter.h>
#include <effects/SkDashPathEffect.h>
#include <effects/SkGradientShader.h>
@ -99,19 +97,19 @@ static void apply_paint_style(SkPaint& paint, Gfx::PaintStyle const& style)
}
}
static void apply_filters(SkPaint& paint, ReadonlySpan<Gfx::Filter> filters)
static void apply_filter(SkPaint& paint, Gfx::Filter const& filter)
{
for (auto const& filter : filters) {
paint.setImageFilter(to_skia_image_filter(filter));
}
}
static SkPaint to_skia_paint(Gfx::PaintStyle const& style, ReadonlySpan<Gfx::Filter> filters)
static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Optional<Gfx::Filter const&> 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<Gfx::Filter> 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<Gfx::Filter> 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<u8>(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<Gfx::Filter> 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<Gfx::Filter> 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<Gfx::Filter> 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<float> const& dash_array, float dash_offset)
void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Optional<Gfx::Filter> 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<float> 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<Gfx::Filter> 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<Gfx::Filter> 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);

View file

@ -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<Gfx::Filter>, 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<Gfx::Filter>, 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<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, ReadonlySpan<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector<float> const&, float dash_offset) override;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override;
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector<float> 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<Gfx::Filter>, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule) override;
virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, 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;

View file

@ -6,12 +6,11 @@
#include <AK/Assertions.h>
#include <LibGfx/Filter.h>
#include <LibGfx/FilterImpl.h>
#include <LibGfx/SkiaUtils.h>
#include <core/SkBlender.h>
#include <core/SkColorFilter.h>
#include <core/SkImageFilter.h>
#include <core/SkString.h>
#include <effects/SkImageFilters.h>
#include <effects/SkRuntimeEffect.h>
namespace Gfx {
@ -23,124 +22,7 @@ SkPath to_skia_path(Path const& path)
sk_sp<SkImageFilter> 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<SkColorFilter> 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<SkBlender> to_skia_blender(Gfx::CompositingAndBlendingOperator compositing_and_blending_operator)

View file

@ -20,6 +20,7 @@
#include <core/SkImageFilter.h>
#include <core/SkPaint.h>
#include <core/SkPath.h>
#include <core/SkPathEffect.h>
#include <core/SkSamplingOptions.h>
namespace Gfx {

View file

@ -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<Gfx::Filter> backdrop_filter() { return {}; }
static Vector<Gfx::Filter> filter() { return {}; }
static Optional<Gfx::Filter> backdrop_filter() { return {}; }
static Optional<Gfx::Filter> 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<Gfx::Filter> const& backdrop_filter() const { return m_noninherited.backdrop_filter; }
Vector<Gfx::Filter> const& filter() const { return m_noninherited.filter; }
Optional<Gfx::Filter> const& backdrop_filter() const { return m_noninherited.backdrop_filter; }
Optional<Gfx::Filter> const& filter() const { return m_noninherited.filter; }
Vector<ShadowData> 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<Gfx::Filter> backdrop_filter { InitialValues::backdrop_filter() };
Vector<Gfx::Filter> filter { InitialValues::filter() };
Optional<Gfx::Filter> backdrop_filter { InitialValues::backdrop_filter() };
Optional<Gfx::Filter> 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<Gfx::Filter> backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); }
void set_filter(Vector<Gfx::Filter> filter) { m_noninherited.filter = move(filter); }
void set_backdrop_filter(Optional<Gfx::Filter> backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); }
void set_filter(Optional<Gfx::Filter> filter) { m_noninherited.filter = move(filter); }
void set_border_bottom_left_radius(CSS::BorderRadiusData value)
{
m_noninherited.has_noninitial_border_radii = true;

View file

@ -4545,7 +4545,7 @@ RefPtr<CSSStyleValue const> Parser::parse_filter_value_list_value(TokenStream<Co
auto filter_token_to_operation = [&](auto filter) {
VERIFY(to_underlying(filter) < to_underlying(FilterToken::Blur));
return static_cast<Gfx::ColorFilter::Type>(filter);
return static_cast<Gfx::ColorFilterType>(filter);
};
auto parse_filter_function_name = [&](auto name) -> Optional<FilterToken> {

View file

@ -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();

View file

@ -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;

View file

@ -87,8 +87,8 @@ public:
float shadow_offset_y { 0.0f };
float shadow_blur { 0.0f };
Gfx::Color shadow_color { Gfx::Color::Transparent };
Vector<Gfx::Filter> filters;
Optional<String> filters_string;
Optional<Gfx::Filter> filter;
Optional<String> filter_string;
float line_width { 1 };
Bindings::CanvasLineCap line_cap { Bindings::CanvasLineCap::Butt };
Bindings::CanvasLineJoin line_join { Bindings::CanvasLineJoin::Miter };

View file

@ -228,7 +228,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
}
if (auto* painter = this->painter()) {
painter->draw_bitmap(destination_rect, *bitmap, source_rect.to_rounded<int>(), 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<int>(), 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<float>(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.

View file

@ -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<Gfx::Filter> {
Vector<Gfx::Filter> resolved_filter;
auto resolve_filter = [this](CSS::Filter const& computed_filter) -> Optional<Gfx::Filter> {
Optional<Gfx::Filter> 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;

View file

@ -308,7 +308,7 @@ struct DrawLine {
struct ApplyBackdropFilter {
Gfx::IntRect backdrop_region;
BorderRadiiData border_radii_data;
Vector<Gfx::Filter> backdrop_filter;
Optional<Gfx::Filter> 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<Gfx::Filter> filter;
struct ApplyFilter {
Gfx::Filter filter;
};
struct ApplyTransform {
@ -483,7 +483,7 @@ using Command = Variant<
PaintScrollBar,
ApplyOpacity,
ApplyCompositeAndBlendingOperator,
ApplyFilters,
ApplyFilter,
ApplyTransform,
ApplyMaskBitmap>;

View file

@ -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();

View file

@ -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;

View file

@ -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<SkImageFilter> 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<SkImageFilter> image_filter = to_skia_image_filter(command.filter);
SkPaint paint;
paint.setImageFilter(image_filter);

View file

@ -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;

View file

@ -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<Gfx::Filter> 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<Gfx::Filter> 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)

View file

@ -134,7 +134,7 @@ public:
void add_rounded_rect_clip(CornerRadii corner_radii, Gfx::IntRect border_rect, CornerClip corner_clip);
void add_mask(RefPtr<DisplayList> display_list, Gfx::IntRect rect);
void apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, Vector<Gfx::Filter> 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<Gfx::Filter> 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);

View file

@ -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<int>(), border_radii_data, backdrop_filter);
context.display_list_recorder().apply_backdrop_filter(backdrop_region.to_type<int>(), border_radii_data, backdrop_filter.value());
}
void PaintableBox::paint_background(PaintContext& context) const

View file

@ -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();
}

View file

@ -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();
}