mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:27:43 +00:00
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.
This commit is contained in:
parent
9483adee46
commit
ade868aa56
3 changed files with 146 additions and 0 deletions
|
@ -40,6 +40,7 @@ set(SOURCES
|
||||||
IconBag.cpp
|
IconBag.cpp
|
||||||
Image.cpp
|
Image.cpp
|
||||||
ImageEditor.cpp
|
ImageEditor.cpp
|
||||||
|
ImageProcessor.cpp
|
||||||
Layer.cpp
|
Layer.cpp
|
||||||
LayerListWidget.cpp
|
LayerListWidget.cpp
|
||||||
LayerPropertiesWidget.cpp
|
LayerPropertiesWidget.cpp
|
||||||
|
|
69
Userland/Applications/PixelPaint/ImageProcessor.cpp
Normal file
69
Userland/Applications/PixelPaint/ImageProcessor.cpp
Normal file
|
@ -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 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
76
Userland/Applications/PixelPaint/ImageProcessor.h
Normal file
76
Userland/Applications/PixelPaint/ImageProcessor.h
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue