LibGfx+LibWeb: Remove everything in LibGfx/Filters

These are no longer needed after switching to use Skia for web page
painting.
This commit is contained in:
Aliaksandr Kalenik 2024-07-21 18:00:49 +02:00 committed by Andreas Kling
parent ba56cb6e51
commit 44e23dce77
Notes: github-actions[bot] 2024-07-21 17:31:47 +00:00
29 changed files with 0 additions and 1471 deletions

View file

@ -6,10 +6,6 @@ set(SOURCES
Color.cpp
DeltaE.cpp
EdgeFlagPathRasterizer.cpp
Filters/ColorBlindnessFilter.cpp
Filters/FastBoxBlurFilter.cpp
Filters/LumaFilter.cpp
Filters/StackBlurFilter.cpp
FontCascadeList.cpp
Font/Emoji.cpp
Font/Font.cpp

View file

@ -1,23 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "GenericConvolutionFilter.h"
#include <AK/StringView.h>
namespace Gfx {
template<size_t N>
class BoxBlurFilter : public GenericConvolutionFilter<N> {
public:
BoxBlurFilter() = default;
virtual ~BoxBlurFilter() = default;
virtual StringView class_name() const override { return "BoxBlurFilter"sv; }
};
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
namespace Gfx {
class BrightnessFilter : public ColorFilter {
public:
using ColorFilter::ColorFilter;
virtual ~BrightnessFilter() = default;
virtual StringView class_name() const override { return "BrightnessFilter"sv; }
virtual bool amount_handled_in_filter() const override
{
return true;
}
protected:
Color convert_color(Color original) override
{
auto convert_channel = [&](u8 channel) {
return static_cast<u8>(clamp(round_to<int>(channel * m_amount), 0, 255));
};
return Gfx::Color {
convert_channel(original.red()),
convert_channel(original.green()),
convert_channel(original.blue()),
original.alpha()
};
}
};
}

View file

@ -1,84 +0,0 @@
/*
* Copyright (c) 2021, Antonio Di Stefano <tonio9681@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ColorBlindnessFilter.h"
namespace Gfx {
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_protanopia()
{
return make<ColorBlindnessFilter>(
.56, .44, .0,
.55, .45, .0,
.0, .24, .76);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_protanomaly()
{
return make<ColorBlindnessFilter>(
.82, .18, .0,
.33, .67, .0,
.0, .13, .87);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_deuteranopia()
{
return make<ColorBlindnessFilter>(
.63, .37, .0,
.7, .3, .0,
.0, .3, .7);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_deuteranomaly()
{
return make<ColorBlindnessFilter>(
.8, .2, .0,
.26, .74, .0,
.0, .15, .85);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_tritanopia()
{
return make<ColorBlindnessFilter>(
.95, .05, .0,
.0, .44, .56,
.0, .48, .52);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_tritanomaly()
{
return make<ColorBlindnessFilter>(
.97, .03, .0,
.0, .73, .27,
.0, .18, .82);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_achromatopsia()
{
return make<ColorBlindnessFilter>(
.3, .59, .11,
.3, .59, .11,
.3, .59, .11);
}
NonnullOwnPtr<ColorBlindnessFilter> ColorBlindnessFilter::create_achromatomaly()
{
return make<ColorBlindnessFilter>(
.62, .32, .06,
.16, .78, .06,
.16, .32, .52);
}
Color ColorBlindnessFilter::convert_color(Color original)
{
return Color(
(u8)(original.red() * m_red_in_red_band + original.green() * m_green_in_red_band + original.blue() * m_blue_in_red_band),
(u8)(original.red() * m_red_in_green_band + original.green() * m_green_in_green_band + original.blue() * m_blue_in_green_band),
(u8)(original.red() * m_red_in_blue_band + original.green() * m_green_in_blue_band + original.blue() * m_blue_in_blue_band),
original.alpha());
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright (c) 2021, Antonio Di Stefano <tonio9681@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "ColorFilter.h"
#include <AK/NonnullOwnPtr.h>
#include <AK/StringView.h>
namespace Gfx {
class ColorBlindnessFilter : public ColorFilter {
public:
ColorBlindnessFilter(
double red_in_red_band,
double green_in_red_band,
double blue_in_red_band,
double red_in_green_band,
double green_in_green_band,
double blue_in_green_band,
double red_in_blue_band,
double green_in_blue_band,
double blue_in_blue_band)
: m_red_in_red_band(red_in_red_band)
, m_green_in_red_band(green_in_red_band)
, m_blue_in_red_band(blue_in_red_band)
, m_red_in_green_band(red_in_green_band)
, m_green_in_green_band(green_in_green_band)
, m_blue_in_green_band(blue_in_green_band)
, m_red_in_blue_band(red_in_blue_band)
, m_green_in_blue_band(green_in_blue_band)
, m_blue_in_blue_band(blue_in_blue_band)
{
}
virtual ~ColorBlindnessFilter() = default;
virtual StringView class_name() const override { return "ColorBlindnessFilter"sv; }
static NonnullOwnPtr<ColorBlindnessFilter> create_protanopia();
static NonnullOwnPtr<ColorBlindnessFilter> create_protanomaly();
static NonnullOwnPtr<ColorBlindnessFilter> create_deuteranopia();
static NonnullOwnPtr<ColorBlindnessFilter> create_deuteranomaly();
static NonnullOwnPtr<ColorBlindnessFilter> create_tritanopia();
static NonnullOwnPtr<ColorBlindnessFilter> create_tritanomaly();
static NonnullOwnPtr<ColorBlindnessFilter> create_achromatopsia();
static NonnullOwnPtr<ColorBlindnessFilter> create_achromatomaly();
protected:
Color convert_color(Color original) override;
private:
double m_red_in_red_band;
double m_green_in_red_band;
double m_blue_in_red_band;
double m_red_in_green_band;
double m_green_in_green_band;
double m_blue_in_green_band;
double m_red_in_blue_band;
double m_green_in_blue_band;
double m_blue_in_blue_band;
};
}

View file

@ -1,53 +0,0 @@
/*
* Copyright (c) 2021, Antonio Di Stefano <tonio9681@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Filter.h"
namespace Gfx {
class ColorFilter : public Filter {
public:
ColorFilter(float amount = 1.0f)
: m_amount(amount)
{
}
virtual ~ColorFilter() = default;
virtual bool amount_handled_in_filter() const
{
return false;
}
virtual void apply(Bitmap& target_bitmap, IntRect const& target_rect, Bitmap const& source_bitmap, IntRect const& source_rect) override
{
VERIFY(source_rect.size() == target_rect.size());
VERIFY(target_bitmap.rect().contains(target_rect));
VERIFY(source_bitmap.rect().contains(source_rect));
for (auto y = 0; y < source_rect.height(); ++y) {
ssize_t source_y = y + source_rect.y();
ssize_t target_y = y + target_rect.y();
for (auto x = 0; x < source_rect.width(); ++x) {
ssize_t source_x = x + source_rect.x();
ssize_t target_x = x + target_rect.x();
auto source_pixel = source_bitmap.get_pixel(source_x, source_y);
auto target_color = convert_color(source_pixel);
target_bitmap.set_pixel(target_x, target_y, m_amount < 1.0f && !amount_handled_in_filter() ? source_pixel.mixed_with(target_color, m_amount) : target_color);
}
}
}
protected:
virtual Color convert_color(Color) = 0;
float m_amount { 1.0f };
};
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
namespace Gfx {
class ContrastFilter : public ColorFilter {
public:
using ColorFilter::ColorFilter;
virtual ~ContrastFilter() = default;
virtual StringView class_name() const override { return "ContrastFilter"sv; }
virtual bool amount_handled_in_filter() const override
{
return true;
}
protected:
Color convert_color(Color original) override
{
auto convert_channel = [&](u8 channel) {
return static_cast<u8>(clamp(round_to<int>(channel * m_amount + (-128 * m_amount) + 128), 0, 255));
};
return Gfx::Color {
convert_channel(original.red()),
convert_channel(original.green()),
convert_channel(original.blue()),
original.alpha()
};
}
};
}

View file

@ -1,182 +0,0 @@
/*
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#if defined(AK_COMPILER_GCC)
# pragma GCC optimize("O3")
#endif
#include <AK/Function.h>
#include <AK/Vector.h>
#include <LibGfx/Filters/FastBoxBlurFilter.h>
namespace Gfx {
ALWAYS_INLINE static constexpr u8 red_value(Color color)
{
return (color.alpha() == 0) ? 0xFF : color.red();
}
ALWAYS_INLINE static constexpr u8 green_value(Color color)
{
return (color.alpha() == 0) ? 0xFF : color.green();
}
ALWAYS_INLINE static constexpr u8 blue_value(Color color)
{
return (color.alpha() == 0) ? 0xFF : color.blue();
}
FastBoxBlurFilter::FastBoxBlurFilter(Bitmap& bitmap)
: m_bitmap(bitmap)
{
}
void FastBoxBlurFilter::apply_single_pass(size_t radius)
{
apply_single_pass(radius, radius);
}
template<typename GetPixelFunction, typename SetPixelFunction>
static void do_single_pass(int width, int height, size_t radius_x, size_t radius_y, GetPixelFunction get_pixel_function, SetPixelFunction set_pixel_function)
{
int div_x = 2 * radius_x + 1;
int div_y = 2 * radius_y + 1;
Vector<Color, 1024> intermediate;
intermediate.resize(width * height);
// First pass: vertical
for (int y = 0; y < height; ++y) {
size_t sum_red = 0;
size_t sum_green = 0;
size_t sum_blue = 0;
size_t sum_alpha = 0;
// Setup sliding window
for (int i = -(int)radius_x; i <= (int)radius_x; ++i) {
auto color_at_px = get_pixel_function(clamp(i, 0, width - 1), y);
sum_red += red_value(color_at_px);
sum_green += green_value(color_at_px);
sum_blue += blue_value(color_at_px);
sum_alpha += color_at_px.alpha();
}
// Slide horizontally
for (int x = 0; x < width; ++x) {
auto const index = y * width + x;
auto& current_intermediate = intermediate[index];
current_intermediate.set_red(sum_red / div_x);
current_intermediate.set_green(sum_green / div_x);
current_intermediate.set_blue(sum_blue / div_x);
current_intermediate.set_alpha(sum_alpha / div_x);
auto leftmost_x_coord = max(x - (int)radius_x, 0);
auto rightmost_x_coord = min(x + (int)radius_x + 1, width - 1);
auto leftmost_x_color = get_pixel_function(leftmost_x_coord, y);
auto rightmost_x_color = get_pixel_function(rightmost_x_coord, y);
sum_red -= red_value(leftmost_x_color);
sum_red += red_value(rightmost_x_color);
sum_green -= green_value(leftmost_x_color);
sum_green += green_value(rightmost_x_color);
sum_blue -= blue_value(leftmost_x_color);
sum_blue += blue_value(rightmost_x_color);
sum_alpha -= leftmost_x_color.alpha();
sum_alpha += rightmost_x_color.alpha();
}
}
// Second pass: horizontal
for (int x = 0; x < width; ++x) {
size_t sum_red = 0;
size_t sum_green = 0;
size_t sum_blue = 0;
size_t sum_alpha = 0;
// Setup sliding window
for (int i = -(int)radius_y; i <= (int)radius_y; ++i) {
int offset = clamp(i, 0, height - 1) * width + x;
auto& current_intermediate = intermediate[offset];
sum_red += current_intermediate.red();
sum_green += current_intermediate.green();
sum_blue += current_intermediate.blue();
sum_alpha += current_intermediate.alpha();
}
for (int y = 0; y < height; ++y) {
auto color = Color(
sum_red / div_y,
sum_green / div_y,
sum_blue / div_y,
sum_alpha / div_y);
set_pixel_function(x, y, color);
auto const bottommost_y_coord = min(y + (int)radius_y + 1, height - 1);
auto const bottom_index = x + bottommost_y_coord * width;
auto& bottom_intermediate = intermediate[bottom_index];
sum_red += bottom_intermediate.red();
sum_green += bottom_intermediate.green();
sum_blue += bottom_intermediate.blue();
sum_alpha += bottom_intermediate.alpha();
auto const topmost_y_coord = max(y - (int)radius_y, 0);
auto const top_index = x + topmost_y_coord * width;
auto& top_intermediate = intermediate[top_index];
sum_red -= top_intermediate.red();
sum_green -= top_intermediate.green();
sum_blue -= top_intermediate.blue();
sum_alpha -= top_intermediate.alpha();
}
}
}
// Based on the super fast blur algorithm by Quasimondo, explored here: https://stackoverflow.com/questions/21418892/understanding-super-fast-blur-algorithm
FLATTEN void FastBoxBlurFilter::apply_single_pass(size_t radius_x, size_t radius_y)
{
auto format = m_bitmap.format();
VERIFY(format == BitmapFormat::BGRA8888 || format == BitmapFormat::BGRx8888);
switch (format) {
case BitmapFormat::BGRx8888:
do_single_pass(
m_bitmap.width(), m_bitmap.height(), radius_x, radius_y,
[&](int x, int y) { return m_bitmap.get_pixel<StorageFormat::BGRx8888>(x, y); },
[&](int x, int y, Color color) { return m_bitmap.set_pixel<StorageFormat::BGRx8888>(x, y, color); });
break;
case BitmapFormat::BGRA8888:
do_single_pass(
m_bitmap.width(), m_bitmap.height(), radius_x, radius_y,
[&](int x, int y) { return m_bitmap.get_pixel<StorageFormat::BGRA8888>(x, y); },
[&](int x, int y, Color color) { return m_bitmap.set_pixel<StorageFormat::BGRA8888>(x, y, color); });
break;
default:
VERIFY_NOT_REACHED();
}
}
// Math from here: http://blog.ivank.net/fastest-gaussian-blur.html
void FastBoxBlurFilter::apply_three_passes(size_t radius)
{
if (!radius)
return;
constexpr size_t no_of_passes = 3;
double w_ideal = sqrt((12 * radius * radius / (double)no_of_passes) + 1);
int wl = floor(w_ideal);
if (wl % 2 == 0)
wl--;
int wu = wl - 2;
double m_ideal = (12 * radius * radius - no_of_passes * wl * wl - 4 * no_of_passes * wl - 3 * no_of_passes) / (double)(-4 * wl - 4);
int m = round(m_ideal);
for (size_t i = 0; i < no_of_passes; ++i) {
int weighted_radius = (int)i < m ? wl : wu;
if (weighted_radius < 2)
continue;
apply_single_pass((weighted_radius - 1) / 2);
}
}
}

View file

@ -1,26 +0,0 @@
/*
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Bitmap.h>
namespace Gfx {
class FastBoxBlurFilter {
public:
FastBoxBlurFilter(Bitmap&);
void apply_single_pass(size_t radius);
void apply_single_pass(size_t radius_x, size_t radius_y);
void apply_three_passes(size_t radius);
private:
Bitmap& m_bitmap;
};
}

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Rect.h>
namespace Gfx {
class Filter {
public:
class Parameters {
public:
virtual bool is_generic_convolution_filter() const { return false; }
virtual ~Parameters() = default;
};
virtual ~Filter() = default;
virtual StringView class_name() const = 0;
virtual void apply(Bitmap&, IntRect const&, Bitmap const&, IntRect const&, Parameters const&) {};
virtual void apply(Bitmap&, IntRect const&, Bitmap const&, IntRect const&) {};
protected:
Filter() = default;
};
}

View file

@ -1,154 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Filter.h"
#include <AK/StringView.h>
#include <LibGfx/Matrix.h>
#include <LibGfx/Matrix4x4.h>
namespace Gfx {
template<size_t N, typename T>
static constexpr void normalize(Matrix<N, T>& matrix)
{
auto sum = 0.0f;
for (size_t i = 0; i < matrix.Size; ++i) {
for (size_t j = 0; j < matrix.Size; ++j) {
sum += matrix.elements()[i][j];
}
}
for (size_t i = 0; i < matrix.Size; ++i) {
for (size_t j = 0; j < matrix.Size; ++j) {
matrix.elements()[i][j] /= sum;
}
}
}
template<size_t N>
class GenericConvolutionFilter : public Filter {
public:
class Parameters : public Filter::Parameters {
public:
Parameters(Gfx::Matrix<N, float> const& kernel, bool should_wrap = true)
: m_kernel(kernel)
, m_should_wrap(should_wrap)
{
}
Gfx::Matrix<N, float> const& kernel() const { return m_kernel; }
Gfx::Matrix<N, float>& kernel() { return m_kernel; }
bool should_wrap() const { return m_should_wrap; }
private:
virtual bool is_generic_convolution_filter() const override { return true; }
Gfx::Matrix<N, float> m_kernel;
bool m_should_wrap { false };
};
class ApplyCache {
template<size_t>
friend class GenericConvolutionFilter;
private:
RefPtr<Gfx::Bitmap> m_target;
};
GenericConvolutionFilter() = default;
virtual ~GenericConvolutionFilter() = default;
virtual StringView class_name() const override { return "GenericConvolutionFilter"sv; }
virtual void apply(Bitmap& target_bitmap, IntRect const& target_rect, Bitmap const& source_bitmap, IntRect const& source_rect, Filter::Parameters const& parameters) override
{
VERIFY(parameters.is_generic_convolution_filter());
auto& gcf_params = static_cast<GenericConvolutionFilter::Parameters const&>(parameters);
ApplyCache apply_cache;
apply_with_cache(target_bitmap, target_rect, source_bitmap, source_rect, gcf_params, apply_cache);
}
void apply_with_cache(Bitmap& target, IntRect target_rect, Bitmap const& source, IntRect const& source_rect, GenericConvolutionFilter::Parameters const& parameters, ApplyCache& apply_cache)
{
// The target area (where the filter is applied) must be entirely
// contained by the source area. source_rect should be describing
// the pixels that can be accessed to apply this filter, while
// target_rect should describe the area where to apply the filter on.
VERIFY(source_rect.contains(target_rect));
VERIFY(source.size().contains(target.size()));
VERIFY(target.rect().contains(target_rect));
VERIFY(source.rect().contains(source_rect));
// If source is different from target, it should still be describing
// essentially the same bitmap. But it allows us to modify target
// without a temporary bitmap. This is important if this filter
// is applied on multiple areas of the same bitmap, at which point
// we would need to be able to access unmodified pixels if the
// areas are (almost) adjacent.
int source_delta_x = target_rect.x() - source_rect.x();
int source_delta_y = target_rect.y() - source_rect.y();
if (&target == &source && (!apply_cache.m_target || !apply_cache.m_target->size().contains(source_rect.size()))) {
// TODO: We probably don't need the entire source_rect, we could inflate
// the target_rect appropriately
apply_cache.m_target = Gfx::Bitmap::create(source.format(), source_rect.size()).release_value_but_fixme_should_propagate_errors();
target_rect.translate_by(-target_rect.location());
}
Bitmap* render_target_bitmap = (&target != &source) ? &target : apply_cache.m_target.ptr();
// FIXME: Help! I am naive!
constexpr static ssize_t offset = N / 2;
for (auto i_ = 0; i_ < target_rect.width(); ++i_) {
ssize_t i = i_ + target_rect.x();
for (auto j_ = 0; j_ < target_rect.height(); ++j_) {
ssize_t j = j_ + target_rect.y();
FloatVector3 value(0, 0, 0);
for (auto k = 0l; k < (ssize_t)N; ++k) {
auto ki = i + k - offset;
if (ki < source_rect.x() || ki >= source_rect.right()) {
if (parameters.should_wrap())
ki = (ki + source.size().width()) % source.size().width(); // TODO: fix up using source_rect
else
continue;
}
for (auto l = 0l; l < (ssize_t)N; ++l) {
auto lj = j + l - offset;
if (lj < source_rect.y() || lj >= source_rect.bottom()) {
if (parameters.should_wrap())
lj = (lj + source.size().height()) % source.size().height(); // TODO: fix up using source_rect
else
continue;
}
auto pixel = source.get_pixel(ki, lj);
FloatVector3 pixel_value(pixel.red(), pixel.green(), pixel.blue());
value = value + pixel_value * parameters.kernel().elements()[k][l];
}
}
value.clamp(0, 255);
render_target_bitmap->set_pixel(i, j, Color(value.x(), value.y(), value.z(), source.get_pixel(i + source_delta_x, j + source_delta_y).alpha()));
}
}
if (render_target_bitmap != &target) {
// FIXME: Substitute for some sort of faster "blit" method.
for (auto i_ = 0; i_ < target_rect.width(); ++i_) {
auto i = i_ + target_rect.x();
for (auto j_ = 0; j_ < target_rect.height(); ++j_) {
auto j = j_ + target_rect.y();
target.set_pixel(i, j, render_target_bitmap->get_pixel(i_, j_));
}
}
}
}
};
}

View file

@ -1,25 +0,0 @@
/*
* Copyright (c) 2021, David Savary <david.savarymartinez@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
namespace Gfx {
class GrayscaleFilter : public ColorFilter {
public:
using ColorFilter::ColorFilter;
virtual ~GrayscaleFilter() = default;
virtual StringView class_name() const override { return "GrayscaleFilter"sv; }
protected:
Color convert_color(Color original) override { return original.to_grayscale(); }
};
}

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Filters/MatrixFilter.h>
namespace Gfx {
class HueRotateFilter : public MatrixFilter {
public:
HueRotateFilter(float angle_degrees)
: MatrixFilter(calculate_hue_rotate_matrix(angle_degrees))
{
}
virtual bool amount_handled_in_filter() const override
{
return true;
}
virtual StringView class_name() const override { return "HueRotateFilter"sv; }
private:
static FloatMatrix3x3 calculate_hue_rotate_matrix(float angle_degrees)
{
float angle_rads = AK::to_radians(angle_degrees);
float cos_angle = 0;
float sin_angle = 0;
AK::sincos(angle_rads, sin_angle, cos_angle);
// The matrices here are taken directly from the SVG filter specification:
// https://drafts.fxtf.org/filter-effects-1/#feColorMatrixElement
// clang-format off
return FloatMatrix3x3 {
+0.213, +0.715, +0.072,
+0.213, +0.715, +0.072,
+0.213, +0.715, +0.072
} + cos_angle * FloatMatrix3x3 {
+0.787, -0.715, -0.072,
-0.213, +0.285, -0.072,
-0.213, -0.715, +0.928
} + sin_angle * FloatMatrix3x3 {
-0.213, -0.715, +0.928,
+0.143, +0.140, -0.283,
-0.787, +0.715, +0.072
};
// clang-format on
}
};
}

View file

@ -1,25 +0,0 @@
/*
* Copyright (c) 2021, Musab Kılıç <musabkilic@protonmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
namespace Gfx {
class InvertFilter : public ColorFilter {
public:
using ColorFilter::ColorFilter;
virtual ~InvertFilter() = default;
virtual StringView class_name() const override { return "InvertFilter"sv; }
protected:
Color convert_color(Color original) override { return original.inverted(); }
};
}

View file

@ -1,22 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "GenericConvolutionFilter.h"
#include <AK/StringView.h>
namespace Gfx {
class LaplacianFilter : public GenericConvolutionFilter<3> {
public:
LaplacianFilter() = default;
virtual ~LaplacianFilter() = default;
virtual StringView class_name() const override { return "LaplacianFilter"sv; }
};
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "LumaFilter.h"
namespace Gfx {
void LumaFilter::apply(u8 lower_bound, u8 upper_bound)
{
if (upper_bound < lower_bound)
return;
int height = m_bitmap.height();
int width = m_bitmap.width();
auto format = m_bitmap.format();
VERIFY(format == BitmapFormat::BGRA8888 || format == BitmapFormat::BGRx8888);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
Color color;
color = m_bitmap.get_pixel(x, y);
auto luma = color.luminosity();
if (lower_bound > luma || upper_bound < luma)
m_bitmap.set_pixel(x, y, { 0, 0, 0, color.alpha() });
}
}
}
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Bitmap.h>
namespace Gfx {
class LumaFilter {
public:
LumaFilter(Bitmap& bitmap)
: m_bitmap(bitmap) {};
void apply(u8 lower_bound, u8 upper_bound);
private:
Bitmap& m_bitmap;
};
}

View file

@ -1,46 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Filters/ColorFilter.h>
#include <LibGfx/Matrix3x3.h>
namespace Gfx {
class MatrixFilter : public ColorFilter {
public:
MatrixFilter(FloatMatrix3x3 operation, float amount = 1.0f)
: ColorFilter(amount)
, m_operation(operation)
{
}
protected:
Color convert_color(Color original) override
{
auto safe_float_to_u8 = [](float value) -> u8 {
return AK::clamp(static_cast<int>(value), 0, AK::NumericLimits<u8>::max());
};
FloatVector3 rgb = {
float(original.red()),
float(original.green()),
float(original.blue())
};
rgb = m_operation * rgb;
return Color {
safe_float_to_u8(rgb[0]),
safe_float_to_u8(rgb[1]),
safe_float_to_u8(rgb[2]),
original.alpha()
};
}
private:
FloatMatrix3x3 const m_operation;
};
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
namespace Gfx {
class OpacityFilter : public ColorFilter {
public:
using ColorFilter::ColorFilter;
virtual ~OpacityFilter() = default;
virtual StringView class_name() const override { return "OpacityFilter"sv; }
virtual bool amount_handled_in_filter() const override
{
return true;
}
protected:
Color convert_color(Color original) override
{
return original.with_alpha(m_amount * 255);
}
};
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Filters/MatrixFilter.h>
namespace Gfx {
class SaturateFilter : public MatrixFilter {
public:
SaturateFilter(float amount)
: MatrixFilter(calculate_saturate_matrix(amount))
{
}
virtual bool amount_handled_in_filter() const override
{
return true;
}
virtual StringView class_name() const override { return "SaturateFilter"sv; }
private:
static FloatMatrix3x3 calculate_saturate_matrix(float amount)
{
// The matrix is taken directly from the SVG filter specification:
// https://drafts.fxtf.org/filter-effects-1/#feColorMatrixElement
return FloatMatrix3x3 {
0.213f + 0.787f * amount, 0.715f - 0.715f * amount, 0.072f - 0.072f * amount,
0.213f - 0.213f * amount, 0.715f + 0.285f * amount, 0.072f - 0.072f * amount,
0.213f - 0.213f * amount, 0.715f - 0.715f * amount, 0.072f + 0.928f * amount
};
}
};
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2022, Xavier Defrang <xavier.defrang@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
#include <math.h>
namespace Gfx {
class SepiaFilter : public ColorFilter {
public:
using ColorFilter::ColorFilter;
virtual ~SepiaFilter() = default;
virtual StringView class_name() const override { return "SepiaFilter"sv; }
virtual bool amount_handled_in_filter() const override
{
return true;
}
protected:
Color convert_color(Color original) override { return original.sepia(m_amount); }
};
}

View file

@ -1,22 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "GenericConvolutionFilter.h"
#include <AK/StringView.h>
namespace Gfx {
class SharpenFilter : public GenericConvolutionFilter<3> {
public:
SharpenFilter() = default;
virtual ~SharpenFilter() = default;
virtual StringView class_name() const override { return "SharpenFilter"sv; }
};
}

View file

@ -1,22 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "GenericConvolutionFilter.h"
#include <AK/StringView.h>
namespace Gfx {
template<size_t N>
requires(N % 2 == 1) class SpatialGaussianBlurFilter : public GenericConvolutionFilter<N> {
public:
SpatialGaussianBlurFilter() = default;
virtual ~SpatialGaussianBlurFilter() = default;
virtual StringView class_name() const override { return "SpatialGaussianBlurFilter"sv; }
};
}

View file

@ -1,312 +0,0 @@
/*
* Copyright (c) 2010, Mario Klingemann <mario@quasimondo.com>
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#if defined(AK_COMPILER_GCC)
# pragma GCC optimize("O3")
#endif
#include <AK/Array.h>
#include <AK/IntegralMath.h>
#include <AK/Math.h>
#include <AK/Vector.h>
#include <LibGfx/Filters/StackBlurFilter.h>
namespace Gfx {
using uint = unsigned;
constexpr size_t MAX_RADIUS = 256;
// Magic lookup tables!
// `(value * sum_mult[radius - 2]) >> shift_table[radius - 2]` closely approximates value/(radius*radius)
// These LUTs are the same as the original, but converted to constexpr functions rather than magic numbers.
constexpr auto shift_table = [] {
Array<u8, MAX_RADIUS> lut {};
for (size_t r = 2; r <= MAX_RADIUS + 1; r++)
lut[r - 2] = static_cast<u8>(AK::ceil_log2(256 * (r * r + 1)));
return lut;
}();
constexpr auto mult_table = [] {
Array<u16, MAX_RADIUS> lut {};
for (size_t r = 2; r <= MAX_RADIUS + 1; r++)
lut[r - 2] = static_cast<u16>(AK::ceil(static_cast<double>(1 << shift_table[r - 2]) / (r * r)));
return lut;
}();
// Note: This is named to be consistent with the algorithm, but it's actually a simple circular buffer.
struct BlurStack {
BlurStack(size_t size)
{
m_data.resize(size);
}
struct Iterator {
friend BlurStack;
ALWAYS_INLINE Color& operator*()
{
return m_data.at(m_idx);
}
ALWAYS_INLINE Color* operator->()
{
return &m_data.at(m_idx);
}
ALWAYS_INLINE Iterator operator++()
{
// Note: This seemed to profile slightly better than %
if (++m_idx >= m_data.size())
m_idx = 0;
return *this;
}
ALWAYS_INLINE Iterator operator++(int)
{
auto prev_it = *this;
++*(this);
return prev_it;
}
private:
Iterator(size_t idx, Span<Color> data)
: m_idx(idx)
, m_data(data)
{
}
size_t m_idx;
Span<Color> m_data;
};
Iterator iterator_from_position(size_t position)
{
VERIFY(position < m_data.size());
return Iterator(position, m_data);
}
private:
Vector<Color, 512> m_data;
};
// This is an implementation of StackBlur by Mario Klingemann (https://observablehq.com/@jobleonard/mario-klingemans-stackblur)
// (Link is to a secondary source as the original site is now down)
FLATTEN void StackBlurFilter::process_rgba(u8 radius, Color fill_color)
{
// TODO: Implement a plain RGB version of this (if required)
if (radius == 0)
return;
fill_color = fill_color.with_alpha(0);
uint width = m_bitmap.width();
uint height = m_bitmap.height();
uint div = 2 * radius + 1;
uint radius_plus_1 = radius + 1;
uint sum_factor = radius_plus_1 * (radius_plus_1 + 1) / 2;
auto get_pixel = [&](int x, int y) {
auto color = m_bitmap.get_pixel<StorageFormat::BGRA8888>(x, y);
if (color.alpha() == 0)
return fill_color;
return color;
};
auto set_pixel = [&](int x, int y, Color color) {
return m_bitmap.set_pixel<StorageFormat::BGRA8888>(x, y, color);
};
BlurStack blur_stack { div };
auto const stack_start = blur_stack.iterator_from_position(0);
auto const stack_end = blur_stack.iterator_from_position(radius_plus_1);
auto stack_iterator = stack_start;
auto const sum_mult = mult_table[radius - 1];
auto const sum_shift = shift_table[radius - 1];
for (uint y = 0; y < height; y++) {
stack_iterator = stack_start;
auto color = get_pixel(0, y);
for (uint i = 0; i < radius_plus_1; i++)
*(stack_iterator++) = color;
// All the sums here work to approximate a gaussian.
// Note: Only about 17 bits are actually used in each sum.
uint red_in_sum = 0;
uint green_in_sum = 0;
uint blue_in_sum = 0;
uint alpha_in_sum = 0;
uint red_out_sum = radius_plus_1 * color.red();
uint green_out_sum = radius_plus_1 * color.green();
uint blue_out_sum = radius_plus_1 * color.blue();
uint alpha_out_sum = radius_plus_1 * color.alpha();
uint red_sum = sum_factor * color.red();
uint green_sum = sum_factor * color.green();
uint blue_sum = sum_factor * color.blue();
uint alpha_sum = sum_factor * color.alpha();
for (uint i = 1; i <= radius; i++) {
auto color = get_pixel(min(i, width - 1), y);
auto bias = radius_plus_1 - i;
*stack_iterator = color;
red_sum += color.red() * bias;
green_sum += color.green() * bias;
blue_sum += color.blue() * bias;
alpha_sum += color.alpha() * bias;
red_in_sum += color.red();
green_in_sum += color.green();
blue_in_sum += color.blue();
alpha_in_sum += color.alpha();
++stack_iterator;
}
auto stack_in_iterator = stack_start;
auto stack_out_iterator = stack_end;
for (uint x = 0; x < width; x++) {
auto alpha = (alpha_sum * sum_mult) >> sum_shift;
if (alpha != 0)
set_pixel(x, y, Color((red_sum * sum_mult) >> sum_shift, (green_sum * sum_mult) >> sum_shift, (blue_sum * sum_mult) >> sum_shift, alpha));
else
set_pixel(x, y, fill_color);
red_sum -= red_out_sum;
green_sum -= green_out_sum;
blue_sum -= blue_out_sum;
alpha_sum -= alpha_out_sum;
red_out_sum -= stack_in_iterator->red();
green_out_sum -= stack_in_iterator->green();
blue_out_sum -= stack_in_iterator->blue();
alpha_out_sum -= stack_in_iterator->alpha();
auto color = get_pixel(min(x + radius_plus_1, width - 1), y);
*stack_in_iterator = color;
red_in_sum += color.red();
green_in_sum += color.green();
blue_in_sum += color.blue();
alpha_in_sum += color.alpha();
red_sum += red_in_sum;
green_sum += green_in_sum;
blue_sum += blue_in_sum;
alpha_sum += alpha_in_sum;
++stack_in_iterator;
color = *stack_out_iterator;
red_out_sum += color.red();
green_out_sum += color.green();
blue_out_sum += color.blue();
alpha_out_sum += color.alpha();
red_in_sum -= color.red();
green_in_sum -= color.green();
blue_in_sum -= color.blue();
alpha_in_sum -= color.alpha();
++stack_out_iterator;
}
}
for (uint x = 0; x < width; x++) {
stack_iterator = stack_start;
auto color = get_pixel(x, 0);
for (uint i = 0; i < radius_plus_1; i++)
*(stack_iterator++) = color;
uint red_in_sum = 0;
uint green_in_sum = 0;
uint blue_in_sum = 0;
uint alpha_in_sum = 0;
uint red_out_sum = radius_plus_1 * color.red();
uint green_out_sum = radius_plus_1 * color.green();
uint blue_out_sum = radius_plus_1 * color.blue();
uint alpha_out_sum = radius_plus_1 * color.alpha();
uint red_sum = sum_factor * color.red();
uint green_sum = sum_factor * color.green();
uint blue_sum = sum_factor * color.blue();
uint alpha_sum = sum_factor * color.alpha();
for (uint i = 1; i <= radius; i++) {
auto color = get_pixel(x, min(i, height - 1));
auto bias = radius_plus_1 - i;
*stack_iterator = color;
red_sum += color.red() * bias;
green_sum += color.green() * bias;
blue_sum += color.blue() * bias;
alpha_sum += color.alpha() * bias;
red_in_sum += color.red();
green_in_sum += color.green();
blue_in_sum += color.blue();
alpha_in_sum += color.alpha();
++stack_iterator;
}
auto stack_in_iterator = stack_start;
auto stack_out_iterator = stack_end;
for (uint y = 0; y < height; y++) {
auto alpha = (alpha_sum * sum_mult) >> sum_shift;
if (alpha != 0)
set_pixel(x, y, Color((red_sum * sum_mult) >> sum_shift, (green_sum * sum_mult) >> sum_shift, (blue_sum * sum_mult) >> sum_shift, alpha));
else
set_pixel(x, y, fill_color);
red_sum -= red_out_sum;
green_sum -= green_out_sum;
blue_sum -= blue_out_sum;
alpha_sum -= alpha_out_sum;
red_out_sum -= stack_in_iterator->red();
green_out_sum -= stack_in_iterator->green();
blue_out_sum -= stack_in_iterator->blue();
alpha_out_sum -= stack_in_iterator->alpha();
auto color = get_pixel(x, min(y + radius_plus_1, height - 1));
*stack_in_iterator = color;
red_in_sum += color.red();
green_in_sum += color.green();
blue_in_sum += color.blue();
alpha_in_sum += color.alpha();
red_sum += red_in_sum;
green_sum += green_in_sum;
blue_sum += blue_in_sum;
alpha_sum += alpha_in_sum;
++stack_in_iterator;
color = *stack_out_iterator;
red_out_sum += color.red();
green_out_sum += color.green();
blue_out_sum += color.blue();
alpha_out_sum += color.alpha();
red_in_sum -= color.red();
green_in_sum -= color.green();
blue_in_sum -= color.blue();
alpha_in_sum -= color.alpha();
++stack_out_iterator;
}
}
}
}

View file

@ -1,27 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Bitmap.h>
namespace Gfx {
class StackBlurFilter {
public:
StackBlurFilter(Bitmap& bitmap)
: m_bitmap(bitmap)
{
}
// Note: The radius is a u8 for reason! This implementation can only handle radii from 0 to 255.
void process_rgba(u8 radius, Color fill_color = Color::NamedColor::White);
private:
Bitmap& m_bitmap;
};
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibGfx/Filters/ColorFilter.h>
namespace Gfx {
class TintFilter : public ColorFilter {
public:
TintFilter(Color color, float amount)
: ColorFilter(amount)
, m_color(Color::from_rgb(color.value()))
{
}
virtual bool amount_handled_in_filter() const override { return true; }
virtual StringView class_name() const override { return "TintFilter"sv; }
protected:
Color convert_color(Color dest) override
{
return Color::from_rgb(dest.value())
.mixed_with(m_color, m_amount)
.with_alpha(dest.alpha());
}
private:
Gfx::Color m_color;
};
}

View file

@ -23,7 +23,6 @@
#include <gpu/ganesh/SkSurfaceGanesh.h>
#include <pathops/SkPathOps.h>
#include <LibGfx/Filters/StackBlurFilter.h>
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/Painting/DisplayListPlayerSkia.h>
#include <LibWeb/Painting/ShadowPainting.h>

View file

@ -4,15 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Filters/BrightnessFilter.h>
#include <LibGfx/Filters/ContrastFilter.h>
#include <LibGfx/Filters/GrayscaleFilter.h>
#include <LibGfx/Filters/HueRotateFilter.h>
#include <LibGfx/Filters/InvertFilter.h>
#include <LibGfx/Filters/OpacityFilter.h>
#include <LibGfx/Filters/SaturateFilter.h>
#include <LibGfx/Filters/SepiaFilter.h>
#include <LibGfx/Filters/StackBlurFilter.h>
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
#include <LibWeb/Painting/FilterPainting.h>

View file

@ -8,7 +8,6 @@
#include <AK/NumericLimits.h>
#include <LibGfx/DisjointRectSet.h>
#include <LibGfx/Filters/StackBlurFilter.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Painter.h>
#include <LibWeb/Layout/LineBoxFragment.h>