|
@@ -0,0 +1,59 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
|
|
|
|
+ *
|
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "Median.h"
|
|
|
|
+#include <AK/QuickSort.h>
|
|
|
|
+#include <Applications/PixelPaint/Filters/MedianSettingsGML.h>
|
|
|
|
+#include <LibGUI/SpinBox.h>
|
|
|
|
+
|
|
|
|
+namespace PixelPaint::Filters {
|
|
|
|
+
|
|
|
|
+void Median::apply(Gfx::Bitmap& target_bitmap, Gfx::Bitmap const& source_bitmap) const
|
|
|
|
+{
|
|
|
|
+ // FIXME: Is there a better way to work around aliasing in the source and target?
|
|
|
|
+ auto target = MUST(source_bitmap.clone());
|
|
|
|
+
|
|
|
|
+ int filter_size = static_cast<int>(this->filter_size());
|
|
|
|
+ for (int x = 0; x < target_bitmap.width(); ++x) {
|
|
|
|
+ for (int y = 0; y < target_bitmap.height(); ++y) {
|
|
|
|
+ int left = x - static_cast<int>(m_filter_radius - 1);
|
|
|
|
+ int top = y - static_cast<int>(m_filter_radius - 1);
|
|
|
|
+ Vector<Color, 16> values;
|
|
|
|
+ values.ensure_capacity(static_cast<size_t>(filter_size * filter_size));
|
|
|
|
+ for (int i = left; i < left + filter_size; ++i) {
|
|
|
|
+ for (int j = top; j < top + filter_size; ++j) {
|
|
|
|
+ if (j < 0 || i < 0 || j >= source_bitmap.height() || i >= source_bitmap.width())
|
|
|
|
+ continue;
|
|
|
|
+ values.unchecked_append(source_bitmap.get_pixel(i, j));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // FIXME: If there was an insertion sort in AK, we should better use that here.
|
|
|
|
+ // Sort the values to be able to extract the median. The median is determined by grey value (luminosity).
|
|
|
|
+ quick_sort(values, [](auto& a, auto& b) { return a.luminosity() < b.luminosity(); });
|
|
|
|
+ target->set_pixel(x, y, values[values.size() / 2]);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // FIXME: Can we move the `target`s data into the actual target bitmap? Can't be too hard, right?
|
|
|
|
+ Gfx::Painter painter(target_bitmap);
|
|
|
|
+ painter.blit({}, target, target->rect());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+RefPtr<GUI::Widget> Median::get_settings_widget()
|
|
|
|
+{
|
|
|
|
+ if (!m_settings_widget) {
|
|
|
|
+ m_settings_widget = GUI::Widget::construct();
|
|
|
|
+ m_settings_widget->load_from_gml(median_settings_gml);
|
|
|
|
+ m_settings_widget->find_descendant_of_type_named<GUI::SpinBox>("filter_radius")->on_change = [this](auto value) {
|
|
|
|
+ m_filter_radius = value;
|
|
|
|
+ update_preview();
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return m_settings_widget;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+}
|