1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 06:17:35 +00:00

PixelPaint: Add a preview in FilterGallery

Now FilterGallery shows the preview of the currently selected filter
applied on the currently active layer
This commit is contained in:
snooze 2022-02-09 11:00:36 +05:30 committed by Andreas Kling
parent 44cf3ac60f
commit e9d3f3793c
11 changed files with 155 additions and 4 deletions

View file

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

View file

@ -29,11 +29,13 @@ FilterGallery::FilterGallery(GUI::Window* parent_window, ImageEditor* editor)
auto apply_button = main_widget.find_descendant_of_type_named<GUI::Button>("apply_button"); auto apply_button = main_widget.find_descendant_of_type_named<GUI::Button>("apply_button");
auto cancel_button = main_widget.find_descendant_of_type_named<GUI::Button>("cancel_button"); auto cancel_button = main_widget.find_descendant_of_type_named<GUI::Button>("cancel_button");
m_config_widget = main_widget.find_descendant_of_type_named<GUI::Widget>("config_widget"); m_config_widget = main_widget.find_descendant_of_type_named<GUI::Widget>("config_widget");
m_preview_widget = main_widget.find_descendant_of_type_named<FilterPreviewWidget>("preview_widget");
VERIFY(m_filter_tree); VERIFY(m_filter_tree);
VERIFY(apply_button); VERIFY(apply_button);
VERIFY(cancel_button); VERIFY(cancel_button);
VERIFY(m_config_widget); VERIFY(m_config_widget);
VERIFY(m_preview_widget);
auto filter_model = FilterModel::create(editor); auto filter_model = FilterModel::create(editor);
m_filter_tree->set_model(filter_model); m_filter_tree->set_model(filter_model);
@ -41,20 +43,30 @@ FilterGallery::FilterGallery(GUI::Window* parent_window, ImageEditor* editor)
m_filter_tree->on_selection_change = [this]() { m_filter_tree->on_selection_change = [this]() {
auto selected_index = m_filter_tree->selection().first(); auto selected_index = m_filter_tree->selection().first();
if (!selected_index.is_valid()) if (!selected_index.is_valid()) {
m_preview_widget->clear_filter();
return; return;
}
auto selected_filter = static_cast<const FilterModel::FilterInfo*>(selected_index.internal_data()); auto selected_filter = static_cast<const FilterModel::FilterInfo*>(selected_index.internal_data());
if (selected_filter->type != FilterModel::FilterInfo::Type::Filter) if (selected_filter->type != FilterModel::FilterInfo::Type::Filter) {
m_preview_widget->clear_filter();
return; return;
}
m_selected_filter = selected_filter->filter; m_selected_filter = selected_filter->filter;
m_selected_filter->on_settings_change = [&]() {
m_preview_widget->set_filter(m_selected_filter);
};
m_preview_widget->set_filter(m_selected_filter);
m_selected_filter_config_widget = m_selected_filter->get_settings_widget(); m_selected_filter_config_widget = m_selected_filter->get_settings_widget();
m_config_widget->remove_all_children(); m_config_widget->remove_all_children();
m_config_widget->add_child(*m_selected_filter_config_widget); m_config_widget->add_child(*m_selected_filter_config_widget);
}; };
m_preview_widget->set_bitmap(editor->active_layer()->bitmap().clone().release_value());
apply_button->on_click = [this](auto) { apply_button->on_click = [this](auto) {
if (!m_selected_filter) { if (!m_selected_filter) {
done(ExecResult::ExecAborted); done(ExecResult::ExecAborted);

View file

@ -12,12 +12,30 @@
fixed_width: 200 fixed_width: 200
} }
@GUI::Widget {
layout:@GUI::VerticalBoxLayout {
margins: [4]
}
@GUI::Widget { @GUI::Widget {
name: "config_widget" name: "config_widget"
layout:@GUI::VerticalBoxLayout {
margins: [4]
}
}
@GUI::GroupBox {
title: "Preview"
layout: @GUI::VerticalBoxLayout { layout: @GUI::VerticalBoxLayout {
margins: [4] margins: [4]
} }
@PixelPaint::FilterPreviewWidget {
name: "preview_widget"
}
}
} }
} }
@ -29,6 +47,8 @@
@GUI::Widget @GUI::Widget
@GUI::Button { @GUI::Button {
name: "apply_button" name: "apply_button"
text: "Apply" text: "Apply"

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include "FilterPreviewWidget.h"
#include "Filters/Filter.h" #include "Filters/Filter.h"
#include "ImageEditor.h" #include "ImageEditor.h"
#include <LibGUI/Dialog.h> #include <LibGUI/Dialog.h>
@ -19,6 +20,7 @@ private:
FilterGallery(GUI::Window* parent_window, ImageEditor*); FilterGallery(GUI::Window* parent_window, ImageEditor*);
GUI::TreeView* m_filter_tree { nullptr }; GUI::TreeView* m_filter_tree { nullptr };
GUI::Widget* m_config_widget { nullptr }; GUI::Widget* m_config_widget { nullptr };
FilterPreviewWidget* m_preview_widget { nullptr };
RefPtr<GUI::Widget> m_selected_filter_config_widget { nullptr }; RefPtr<GUI::Widget> m_selected_filter_config_widget { nullptr };
Filter* m_selected_filter { nullptr }; Filter* m_selected_filter { nullptr };
}; };

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "FilterPreviewWidget.h"
#include <LibGUI/Painter.h>
#include <LibGfx/Palette.h>
#include <LibGfx/Rect.h>
REGISTER_WIDGET(PixelPaint, FilterPreviewWidget);
namespace PixelPaint {
FilterPreviewWidget::FilterPreviewWidget()
{
}
FilterPreviewWidget::~FilterPreviewWidget()
{
}
void FilterPreviewWidget::set_bitmap(const RefPtr<Gfx::Bitmap>& bitmap)
{
m_bitmap = bitmap;
clear_filter();
}
void FilterPreviewWidget::set_filter(Filter* filter)
{
if (filter)
filter->apply(*m_filtered_bitmap, *m_bitmap);
else
m_filtered_bitmap = m_bitmap->clone().release_value();
repaint();
}
void FilterPreviewWidget::clear_filter()
{
set_filter(nullptr);
}
void FilterPreviewWidget::paint_event(GUI::PaintEvent& event)
{
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
auto preview_rect = event.rect();
auto bitmap_rect = m_filtered_bitmap->rect();
int scaled_width, scaled_height, dx = 0, dy = 0;
if (preview_rect.height() > preview_rect.width()) {
scaled_width = preview_rect.width();
scaled_height = ((float)bitmap_rect.height() / bitmap_rect.width()) * scaled_width;
dy = (preview_rect.height() - scaled_height) / 2;
} else {
scaled_height = preview_rect.height();
scaled_width = ((float)bitmap_rect.width() / bitmap_rect.height()) * scaled_height;
dx = (preview_rect.width() - scaled_width) / 2;
}
Gfx::IntRect scaled_rect(preview_rect.x() + dx, preview_rect.y() + dy, scaled_width, scaled_height);
painter.draw_scaled_bitmap(scaled_rect, *m_filtered_bitmap, m_filtered_bitmap->rect());
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Filters/Filter.h"
#include "ImageEditor.h"
#include "Layer.h"
#include <LibGUI/Frame.h>
#include <LibGfx/Bitmap.h>
namespace PixelPaint {
class FilterPreviewWidget final : public GUI::Frame {
C_OBJECT(FilterPreviewWidget);
public:
virtual ~FilterPreviewWidget() override;
void set_bitmap(const RefPtr<Gfx::Bitmap>& bitmap);
void set_filter(Filter* filter);
void clear_filter();
private:
explicit FilterPreviewWidget();
RefPtr<Gfx::Bitmap> m_bitmap;
RefPtr<Gfx::Bitmap> m_filtered_bitmap;
virtual void paint_event(GUI::PaintEvent&) override;
};
}

View file

@ -59,6 +59,7 @@ RefPtr<GUI::Widget> Bloom::get_settings_widget()
luma_lower_slider.set_value(m_luma_lower); luma_lower_slider.set_value(m_luma_lower);
luma_lower_slider.on_change = [&](int value) { luma_lower_slider.on_change = [&](int value) {
m_luma_lower = value; m_luma_lower = value;
update_preview();
}; };
auto& radius_container = m_settings_widget->add<GUI::Widget>(); auto& radius_container = m_settings_widget->add<GUI::Widget>();
@ -75,6 +76,7 @@ RefPtr<GUI::Widget> Bloom::get_settings_widget()
radius_slider.set_value(m_blur_radius); radius_slider.set_value(m_blur_radius);
radius_slider.on_change = [&](int value) { radius_slider.on_change = [&](int value) {
m_blur_radius = value; m_blur_radius = value;
update_preview();
}; };
} }

View file

@ -54,6 +54,7 @@ RefPtr<GUI::Widget> FastBoxBlur::get_settings_widget()
radius_slider.set_value(m_radius); radius_slider.set_value(m_radius);
radius_slider.on_change = [&](int value) { radius_slider.on_change = [&](int value) {
m_radius = value; m_radius = value;
update_preview();
}; };
auto& gaussian_container = m_settings_widget->add<GUI::Widget>(); auto& gaussian_container = m_settings_widget->add<GUI::Widget>();
@ -66,6 +67,7 @@ RefPtr<GUI::Widget> FastBoxBlur::get_settings_widget()
gaussian_checkbox.set_tooltip("A real gaussian blur can be approximated by running the box blur multiple times with different weights."); gaussian_checkbox.set_tooltip("A real gaussian blur can be approximated by running the box blur multiple times with different weights.");
gaussian_checkbox.on_checked = [this](bool checked) { gaussian_checkbox.on_checked = [this](bool checked) {
m_approximate_gauss = checked; m_approximate_gauss = checked;
update_preview();
}; };
} }

View file

@ -37,4 +37,10 @@ void Filter::apply() const
} }
} }
void Filter::update_preview()
{
if (on_settings_change)
on_settings_change();
}
} }

View file

@ -27,9 +27,12 @@ public:
Filter(ImageEditor* editor) Filter(ImageEditor* editor)
: m_editor(editor) {}; : m_editor(editor) {};
Function<void(void)> on_settings_change;
protected: protected:
ImageEditor* m_editor { nullptr }; ImageEditor* m_editor { nullptr };
RefPtr<GUI::Widget> m_settings_widget { nullptr }; RefPtr<GUI::Widget> m_settings_widget { nullptr };
void update_preview();
}; };
} }

View file

@ -42,6 +42,7 @@ RefPtr<GUI::Widget> Sepia::get_settings_widget()
amount_slider.set_value(m_amount * 100); amount_slider.set_value(m_amount * 100);
amount_slider.on_change = [&](int value) { amount_slider.on_change = [&](int value) {
m_amount = value * 0.01f; m_amount = value * 0.01f;
update_preview();
}; };
} }