mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
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:
parent
ba56cb6e51
commit
44e23dce77
Notes:
github-actions[bot]
2024-07-21 17:31:47 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/44e23dce776 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/754
29 changed files with 0 additions and 1471 deletions
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
|
@ -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()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
|
@ -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()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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_));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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(); }
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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(); }
|
||||
};
|
||||
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
|
@ -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() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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); }
|
||||
};
|
||||
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue