diff options
author | kleines Filmröllchen <filmroellchen@serenityos.org> | 2022-08-13 13:05:03 +0200 |
---|---|---|
committer | Sam Atkins <atkinssj@gmail.com> | 2022-09-27 14:23:11 +0100 |
commit | ade868aa56bf789d6a97b5a8ee2bc3b339de65da (patch) | |
tree | d7a1dfad99959f127b4f2492b37bbfa1f248635c | |
parent | 9483adee465732cd8acc2bb1991dffdae813673c (diff) | |
download | serenity-ade868aa56bf789d6a97b5a8ee2bc3b339de65da.zip |
PixelPaint: Add a general-purpose parallel image processing pipeline
The ImageProcessor singleton is intended to be used by all sorts of
image processing which might take some time to complete; or other
background actions. We're not using BackgroundTask here because this
system is specifically designed to work with task queues and PixelPaint
interaction; e.g. it provides common image processing tasks such as
filter application.
-rw-r--r-- | Userland/Applications/PixelPaint/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/ImageProcessor.cpp | 69 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/ImageProcessor.h | 76 |
3 files changed, 146 insertions, 0 deletions
diff --git a/Userland/Applications/PixelPaint/CMakeLists.txt b/Userland/Applications/PixelPaint/CMakeLists.txt index 7f75f18ed3..bb44e2cb66 100644 --- a/Userland/Applications/PixelPaint/CMakeLists.txt +++ b/Userland/Applications/PixelPaint/CMakeLists.txt @@ -40,6 +40,7 @@ set(SOURCES IconBag.cpp Image.cpp ImageEditor.cpp + ImageProcessor.cpp Layer.cpp LayerListWidget.cpp LayerPropertiesWidget.cpp diff --git a/Userland/Applications/PixelPaint/ImageProcessor.cpp b/Userland/Applications/PixelPaint/ImageProcessor.cpp new file mode 100644 index 0000000000..c6573cb46d --- /dev/null +++ b/Userland/Applications/PixelPaint/ImageProcessor.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ImageProcessor.h" + +namespace PixelPaint { + +FilterApplicationCommand::FilterApplicationCommand(NonnullRefPtr<Filter> filter, NonnullRefPtr<Layer> target_layer) + : m_filter(move(filter)) + , m_target_layer(move(target_layer)) +{ +} + +void FilterApplicationCommand::execute() +{ + m_filter->apply(m_target_layer->content_bitmap(), m_target_layer->content_bitmap()); + m_filter->m_editor->gui_event_loop().deferred_invoke([strong_this = NonnullRefPtr(*this)]() { + // HACK: we can't tell strong_this to not be const + (*const_cast<NonnullRefPtr<Layer>*>(&strong_this->m_target_layer))->did_modify_bitmap(strong_this->m_target_layer->rect()); + strong_this->m_filter->m_editor->did_complete_action(); + }); +} + +static Singleton<ImageProcessor> s_image_processor; + +ImageProcessor::ImageProcessor() + : m_command_queue(MUST(Queue::try_create())) + , m_processor_thread(Threading::Thread::construct([this]() { + while (true) { + if (auto next_command = m_command_queue.try_dequeue(); !next_command.is_error()) { + next_command.value()->execute(); + } else { + Threading::MutexLocker locker { m_wakeup_mutex }; + m_wakeup_variable.wait_while([this]() { return m_command_queue.weak_used() == 0; }); + } + } + return 0; + }, + "Image Processor"sv)) + , m_wakeup_variable(m_wakeup_mutex) +{ +} + +ImageProcessor* ImageProcessor::the() +{ + return s_image_processor; +} + +ErrorOr<void> ImageProcessor::enqueue_command(NonnullRefPtr<ImageProcessingCommand> command) +{ + if (auto queue_status = m_command_queue.try_enqueue(move(command)); queue_status.is_error()) + return ENOSPC; + + if (!m_processor_thread->is_started()) { + m_processor_thread->start(); + m_processor_thread->detach(); + } + + m_wakeup_mutex.lock(); + m_wakeup_variable.signal(); + m_wakeup_mutex.unlock(); + + return {}; +} + +} diff --git a/Userland/Applications/PixelPaint/ImageProcessor.h b/Userland/Applications/PixelPaint/ImageProcessor.h new file mode 100644 index 0000000000..4566aae279 --- /dev/null +++ b/Userland/Applications/PixelPaint/ImageProcessor.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Filters/Filter.h" +#include <AK/Function.h> +#include <AK/NonnullRefPtr.h> +#include <AK/RefCounted.h> +#include <AK/Singleton.h> +#include <LibCore/SharedCircularQueue.h> +#include <LibThreading/ConditionVariable.h> +#include <LibThreading/Thread.h> + +namespace PixelPaint { + +class ImageProcessingCommand : public RefCounted<ImageProcessingCommand> { +public: + virtual void execute() = 0; + virtual ~ImageProcessingCommand() = default; +}; + +// A command applying a filter from a source to a target bitmap. +class FilterApplicationCommand : public ImageProcessingCommand { +public: + FilterApplicationCommand(NonnullRefPtr<Filter>, NonnullRefPtr<Layer>); + + virtual void execute() override; + virtual ~FilterApplicationCommand() = default; + +private: + NonnullRefPtr<Filter> m_filter; + NonnullRefPtr<Layer> m_target_layer; +}; + +// A command based on a custom user function. +class FunctionCommand : public ImageProcessingCommand { +public: + FunctionCommand(Function<void()> function) + : m_function(move(function)) + { + } + + virtual void execute() override { m_function(); } + +private: + Function<void()> m_function; +}; + +// A utility class that allows various PixelPaint systems to execute image processing commands asynchronously on another thread. +class ImageProcessor final { + friend struct AK::SingletonInstanceCreator<ImageProcessor>; + +public: + static ImageProcessor* the(); + + ErrorOr<void> enqueue_command(NonnullRefPtr<ImageProcessingCommand> command); + +private: + ImageProcessor(); + void processor_main(); + + // Only the memory in the queue is in shared memory, i.e. the smart pointers themselves. + // The actual data will remain in normal memory, but for this application we're not using multiple processes so it's fine. + using Queue = Core::SharedSingleProducerCircularQueue<RefPtr<ImageProcessingCommand>>; + Queue m_command_queue; + + NonnullRefPtr<Threading::Thread> m_processor_thread; + Threading::Mutex m_wakeup_mutex {}; + Threading::ConditionVariable m_wakeup_variable; +}; + +} |