PixelPaint: Add a Bloom filter

A bloom filter creates fringes around bright areas in the image
mimicking the behavior of real-world cameras.
It gets its own category "Artistic" in the Filter Gallery since its not
one filter per se but a combination of multiple.

The filter works as follows:
- Get only the light areas (above a threshold) of the image
- Blur that image
- Compose onto the original image
This commit is contained in:
Tobias Christiansen 2022-01-04 16:00:05 +01:00 committed by Idan Horowitz
parent e4b7d38e18
commit f92172c7c3
4 changed files with 122 additions and 0 deletions

View file

@ -17,6 +17,7 @@ set(SOURCES
FilterGallery.cpp
FilterGalleryGML.h
FilterModel.cpp
Filters/Bloom.cpp
Filters/BoxBlur3.cpp
Filters/BoxBlur5.cpp
Filters/Filter.cpp

View file

@ -7,6 +7,7 @@
#include "FilterModel.h"
#include "FilterParams.h"
#include "Filters/Bloom.h"
#include "Filters/BoxBlur3.h"
#include "Filters/BoxBlur5.h"
#include "Filters/FastBoxBlur.h"
@ -23,6 +24,11 @@
namespace PixelPaint {
FilterModel::FilterModel(ImageEditor* editor)
{
auto artistic_category = FilterInfo::create_category("Artistic");
auto bloom_filter = FilterInfo::create_filter<Filters::Bloom>(editor, artistic_category);
m_filters.append(artistic_category);
auto spatial_category = FilterInfo::create_category("Spatial");
auto edge_detect_category = FilterInfo::create_category("Edge Detection", spatial_category);

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Bloom.h"
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Label.h>
#include <LibGUI/ValueSlider.h>
#include <LibGUI/Widget.h>
#include <LibGfx/BitmapMixer.h>
#include <LibGfx/Filters/FastBoxBlurFilter.h>
#include <LibGfx/Filters/LumaFilter.h>
#include <LibGfx/FontDatabase.h>
namespace PixelPaint::Filters {
void Bloom::apply() const
{
if (!m_editor)
return;
if (auto* layer = m_editor->active_layer()) {
auto intermediate_bitmap_or_error = layer->bitmap().clone();
if (intermediate_bitmap_or_error.is_error())
return;
auto intermediate_bitmap = intermediate_bitmap_or_error.release_value();
Gfx::LumaFilter luma_filter(intermediate_bitmap);
luma_filter.apply(m_luma_lower, 255);
Gfx::FastBoxBlurFilter blur_filter(intermediate_bitmap);
blur_filter.apply_three_passes(m_blur_radius);
Gfx::BitmapMixer mixer(layer->bitmap());
mixer.mix_with(intermediate_bitmap, Gfx::BitmapMixer::MixingMethod::Lightest);
}
}
RefPtr<GUI::Widget> Bloom::get_settings_widget()
{
if (!m_settings_widget) {
m_settings_widget = GUI::Widget::construct();
m_settings_widget->set_layout<GUI::VerticalBoxLayout>();
auto& name_label = m_settings_widget->add<GUI::Label>("Bloom Filter");
name_label.set_font_weight(Gfx::FontWeight::Bold);
name_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
name_label.set_fixed_height(20);
auto& luma_lower_container = m_settings_widget->add<GUI::Widget>();
luma_lower_container.set_fixed_height(50);
luma_lower_container.set_layout<GUI::VerticalBoxLayout>();
luma_lower_container.layout()->set_margins({ 4, 0, 4, 0 });
auto& luma_lower_label = luma_lower_container.add<GUI::Label>("Luma lower bound:");
luma_lower_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
luma_lower_label.set_fixed_height(20);
auto& luma_lower_slider = luma_lower_container.add<GUI::ValueSlider>(Orientation::Horizontal);
luma_lower_slider.set_range(0, 255);
luma_lower_slider.set_value(m_luma_lower);
luma_lower_slider.on_change = [&](int value) {
m_luma_lower = value;
};
auto& radius_container = m_settings_widget->add<GUI::Widget>();
radius_container.set_fixed_height(50);
radius_container.set_layout<GUI::VerticalBoxLayout>();
radius_container.layout()->set_margins({ 4, 0, 4, 0 });
auto& radius_label = radius_container.add<GUI::Label>("Blur Radius:");
radius_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
radius_label.set_fixed_height(20);
auto& radius_slider = radius_container.add<GUI::ValueSlider>(Orientation::Horizontal, "px");
radius_slider.set_range(0, 50);
radius_slider.set_value(m_blur_radius);
radius_slider.on_change = [&](int value) {
m_blur_radius = value;
};
}
return m_settings_widget;
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Filter.h"
namespace PixelPaint::Filters {
class Bloom final : public Filter {
public:
virtual void apply() const override;
virtual RefPtr<GUI::Widget> get_settings_widget() override;
virtual StringView filter_name() override { return "Bloom Filter"sv; }
Bloom(ImageEditor* editor)
: Filter(editor) {};
private:
int m_luma_lower { 128 };
int m_blur_radius { 15 };
};
}