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:
parent
44cf3ac60f
commit
e9d3f3793c
11 changed files with 155 additions and 4 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -13,11 +13,29 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::Widget {
|
@GUI::Widget {
|
||||||
name: "config_widget"
|
|
||||||
layout:@GUI::VerticalBoxLayout {
|
layout:@GUI::VerticalBoxLayout {
|
||||||
margins: [4]
|
margins: [4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
name: "config_widget"
|
||||||
|
|
||||||
|
layout:@GUI::VerticalBoxLayout {
|
||||||
|
margins: [4]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::GroupBox {
|
||||||
|
title: "Preview"
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
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"
|
||||||
|
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
|
67
Userland/Applications/PixelPaint/FilterPreviewWidget.cpp
Normal file
67
Userland/Applications/PixelPaint/FilterPreviewWidget.cpp
Normal 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
Userland/Applications/PixelPaint/FilterPreviewWidget.h
Normal file
35
Userland/Applications/PixelPaint/FilterPreviewWidget.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,4 +37,10 @@ void Filter::apply() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Filter::update_preview()
|
||||||
|
{
|
||||||
|
if (on_settings_change)
|
||||||
|
on_settings_change();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue