mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 07:37:46 +00:00
PixelPaint: Create wrappers for all supported filters
This commit is contained in:
parent
5cf0357be1
commit
0deadcdb28
20 changed files with 438 additions and 1 deletions
|
@ -17,7 +17,16 @@ set(SOURCES
|
||||||
FilterGallery.cpp
|
FilterGallery.cpp
|
||||||
FilterGalleryGML.h
|
FilterGalleryGML.h
|
||||||
FilterModel.cpp
|
FilterModel.cpp
|
||||||
|
Filters/BoxBlur3.cpp
|
||||||
|
Filters/BoxBlur5.cpp
|
||||||
Filters/Filter.cpp
|
Filters/Filter.cpp
|
||||||
|
Filters/GaussBlur3.cpp
|
||||||
|
Filters/GaussBlur5.cpp
|
||||||
|
Filters/Grayscale.cpp
|
||||||
|
Filters/Invert.cpp
|
||||||
|
Filters/LaplaceCardinal.cpp
|
||||||
|
Filters/LaplaceDiagonal.cpp
|
||||||
|
Filters/Sharpen.cpp
|
||||||
Image.cpp
|
Image.cpp
|
||||||
ImageEditor.cpp
|
ImageEditor.cpp
|
||||||
Layer.cpp
|
Layer.cpp
|
||||||
|
|
26
Userland/Applications/PixelPaint/Filters/BoxBlur3.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/BoxBlur3.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BoxBlur3.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void BoxBlur3::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::BoxBlurFilter<3> filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::BoxBlurFilter<3>>::get()) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/BoxBlur3.h
Normal file
22
Userland/Applications/PixelPaint/Filters/BoxBlur3.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class BoxBlur3 final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Box Blur (3x3)"sv; }
|
||||||
|
|
||||||
|
BoxBlur3(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
Userland/Applications/PixelPaint/Filters/BoxBlur5.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/BoxBlur5.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BoxBlur5.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void BoxBlur5::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::BoxBlurFilter<5> filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::BoxBlurFilter<5>>::get()) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/BoxBlur5.h
Normal file
22
Userland/Applications/PixelPaint/Filters/BoxBlur5.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class BoxBlur5 final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Box Blur (5x5)"sv; }
|
||||||
|
|
||||||
|
BoxBlur5(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ImageEditor* m_editor { nullptr };
|
ImageEditor* m_editor { nullptr };
|
||||||
StringView m_filter_name;
|
|
||||||
RefPtr<GUI::Widget> m_settings_widget { nullptr };
|
RefPtr<GUI::Widget> m_settings_widget { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
26
Userland/Applications/PixelPaint/Filters/GaussBlur3.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/GaussBlur3.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GaussBlur3.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void GaussBlur3::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::SpatialGaussianBlurFilter<3> filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::SpatialGaussianBlurFilter<3>>::get()) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
Userland/Applications/PixelPaint/Filters/GaussBlur3.h
Normal file
23
Userland/Applications/PixelPaint/Filters/GaussBlur3.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
// FIXME: Make a generic gaussian blur that does not need the templated radius
|
||||||
|
class GaussBlur3 final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Gaussian Blur (3x3)"sv; }
|
||||||
|
|
||||||
|
GaussBlur3(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
Userland/Applications/PixelPaint/Filters/GaussBlur5.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/GaussBlur5.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GaussBlur5.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void GaussBlur5::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::SpatialGaussianBlurFilter<5> filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::SpatialGaussianBlurFilter<5>>::get()) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/GaussBlur5.h
Normal file
22
Userland/Applications/PixelPaint/Filters/GaussBlur5.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class GaussBlur5 final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Gaussian Blur (5x5)"sv; }
|
||||||
|
|
||||||
|
GaussBlur5(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
24
Userland/Applications/PixelPaint/Filters/Grayscale.cpp
Normal file
24
Userland/Applications/PixelPaint/Filters/Grayscale.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Grayscale.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void Grayscale::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::GrayscaleFilter filter;
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect());
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/Grayscale.h
Normal file
22
Userland/Applications/PixelPaint/Filters/Grayscale.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class Grayscale final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Grayscale"sv; }
|
||||||
|
|
||||||
|
Grayscale(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
24
Userland/Applications/PixelPaint/Filters/Invert.cpp
Normal file
24
Userland/Applications/PixelPaint/Filters/Invert.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Invert.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void Invert::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::InvertFilter filter;
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect());
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/Invert.h
Normal file
22
Userland/Applications/PixelPaint/Filters/Invert.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class Invert final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Invert"sv; }
|
||||||
|
|
||||||
|
Invert(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
Userland/Applications/PixelPaint/Filters/LaplaceCardinal.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/LaplaceCardinal.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LaplaceCardinal.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void LaplaceCardinal::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::LaplacianFilter filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::LaplacianFilter>::get(false)) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/LaplaceCardinal.h
Normal file
22
Userland/Applications/PixelPaint/Filters/LaplaceCardinal.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class LaplaceCardinal final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Laplacian Cardinal"sv; }
|
||||||
|
|
||||||
|
LaplaceCardinal(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
Userland/Applications/PixelPaint/Filters/LaplaceDiagonal.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/LaplaceDiagonal.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LaplaceDiagonal.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void LaplaceDiagonal::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::LaplacianFilter filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::LaplacianFilter>::get(true)) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/LaplaceDiagonal.h
Normal file
22
Userland/Applications/PixelPaint/Filters/LaplaceDiagonal.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class LaplaceDiagonal final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Laplacian Diagonal"sv; }
|
||||||
|
|
||||||
|
LaplaceDiagonal(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
26
Userland/Applications/PixelPaint/Filters/Sharpen.cpp
Normal file
26
Userland/Applications/PixelPaint/Filters/Sharpen.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Sharpen.h"
|
||||||
|
#include "../FilterParams.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
void Sharpen::apply() const
|
||||||
|
{
|
||||||
|
if (!m_editor)
|
||||||
|
return;
|
||||||
|
if (auto* layer = m_editor->active_layer()) {
|
||||||
|
Gfx::SharpenFilter filter;
|
||||||
|
if (auto parameters = PixelPaint::FilterParameters<Gfx::SharpenFilter>::get()) {
|
||||||
|
filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters);
|
||||||
|
layer->did_modify_bitmap(layer->rect());
|
||||||
|
m_editor->did_complete_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
Userland/Applications/PixelPaint/Filters/Sharpen.h
Normal file
22
Userland/Applications/PixelPaint/Filters/Sharpen.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Filter.h"
|
||||||
|
|
||||||
|
namespace PixelPaint::Filters {
|
||||||
|
|
||||||
|
class Sharpen final : public Filter {
|
||||||
|
public:
|
||||||
|
virtual void apply() const override;
|
||||||
|
virtual StringView filter_name() override { return "Sharpen"sv; }
|
||||||
|
|
||||||
|
Sharpen(ImageEditor* editor)
|
||||||
|
: Filter(editor) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue