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:
parent
417f4edc46
commit
0fcb574041
Notes:
github-actions[bot]
2025-06-01 21:23:19 +00:00
Author: https://github.com/ananas-dev
Commit: 0fcb574041
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4692
Reviewed-by: https://github.com/kalenikaliaksandr ✅
Reviewed-by: https://github.com/konradekk
26 changed files with 412 additions and 256 deletions
|
@ -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
224
Libraries/LibGfx/Filter.cpp
Normal 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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +1,51 @@
|
|||
/*
|
||||
* 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;
|
||||
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<BlurFilter, DropShadowFilter, HueRotateFilter, ColorFilter>;
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
29
Libraries/LibGfx/FilterImpl.h
Normal file
29
Libraries/LibGfx/FilterImpl.h
Normal 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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue