View on GitHub

photoflare

Quick, simple but powerful Cross Platform image editor.

Plugin system

Photoflare supports a plugin system that allows third-party developers to extend the application with new image filters and file format exporters. Plugins are compiled as shared libraries (.dll on Windows, .so on Linux, .dylib on macOS) and loaded at startup from a plugins/ folder alongside the Photoflare executable.

Installing plugins

Copy the plugin shared library into the plugins/ folder next to the Photoflare executable, then launch Photoflare. To reload plugins without restarting, open Edit → Plugins… and click Rescan.

Plugin Manager dialog

Open Edit → Plugins… to view all loaded plugins. Filter plugins can also be applied from this dialog by selecting one and clicking Apply. Click Open Folder to open the plugins directory in your file manager.


Developing a plugin

Plugins are Qt 6 C++ shared libraries that implement one of the plugin interfaces defined in the Photoflare SDK headers. Example plugins are provided in the examples/plugins/ directory of the source repository.

Requirements

SDK headers

Copy or reference these two headers into your plugin project — no linking against Photoflare itself is required.

Plugin types

InterfaceWhat it doesWhere it appears
IFilterPlugin Transforms a QImage and returns a new one Plugin Filters menu & Plugin Manager dialog
IExporterPlugin Writes a QImage to a file in a custom format File → Save As format list

Creating a filter plugin

A filter plugin receives the current canvas image, applies a transformation, and returns the result. The host runs apply() on a background thread automatically, so the UI stays responsive.

Minimal example

#include <QObject>
#include <QtPlugin>
#include <QImage>
#include "IPhotoflarePlugin.h"
#include "AppContext.h"

class MyFilterPlugin : public QObject, public IFilterPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.photoflare.FilterPlugin/1.0" FILE "myplugin.json")
    Q_INTERFACES(IPhotoflarePlugin IFilterPlugin)
public:
    QString pluginId()    const override { return "com.example.myplugin"; }
    QString displayName() const override { return "My Filter"; }
    QString version()     const override { return "1.0.0"; }
    QString author()      const override { return "Your Name"; }
    QString menuPath()    const override { return "Filters/My Plugins"; }

    void initialise(AppContext* ctx) override { m_ctx = ctx; }

    QList<PluginParam> parameters() const override
    {
        return {
            { "strength", "Strength", PluginParam::Float, 0.5, 0.0, 1.0, {} },
        };
    }

    QImage apply(const QImage& input, const QVariantMap& params) const override
    {
        const float strength = params.value("strength", 0.5f).toFloat();
        // ... transform the image ...
        return input; // replace with your result
    }

private:
    AppContext* m_ctx = nullptr;
};

#include "MyFilterPlugin.moc"

Parameter types

The PluginParam struct describes each parameter. The host auto-generates a dialog from your parameters() list.

TypeWidget shownValue in QVariantMap
PluginParam::FloatSlider (uses minValue / maxValue)toFloat()
PluginParam::IntSlider (integer steps)toInt()
PluginParam::BoolCheck boxtoBool()

menuPath

The menuPath() return value determines the submenu structure under Plugin Filters. The first segment is always replaced with "Plugin Filters" by the host, so "Filters/Noise" and "Plugin Filters/Noise" both produce the same result: Plugin Filters → Noise → My Filter. Return a single word (e.g. "Filters") to place the action directly under Plugin Filters with no submenu.

Threading in apply()

apply() is always called on a background QThread — never on the main thread. Do not access any Qt UI objects inside apply(). For additional parallelism you can use QtConcurrent::blockingMap to distribute row-level work across CPU cores. See examples/plugins/grain/GrainFilterPlugin.cpp for a worked example.


Creating an exporter plugin

An exporter plugin adds a new file format to File → Save As.

Minimal example

#include <QObject>
#include <QtPlugin>
#include <QImage>
#include <QImageWriter>
#include "IPhotoflarePlugin.h"
#include "AppContext.h"

class MyExporterPlugin : public QObject, public IExporterPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.photoflare.ExporterPlugin/1.0" FILE "myexporter.json")
    Q_INTERFACES(IPhotoflarePlugin IExporterPlugin)
public:
    QString     pluginId()    const override { return "com.example.myexporter"; }
    QString     displayName() const override { return "My Format Exporter"; }
    QString     version()     const override { return "1.0.0"; }
    QString     author()      const override { return "Your Name"; }
    QString     formatName()  const override { return "My Format"; }
    QStringList supportedExtensions() const override { return { "myf" }; }

    void initialise(AppContext* ctx) override { m_ctx = ctx; }

    bool exportImage(const QImage&      image,
                     const QString&     filePath,
                     const QVariantMap& settings,
                     QString&           errorMessage) const override
    {
        // write image to filePath; return false and set errorMessage on failure
        if (!image.save(filePath)) {
            errorMessage = "Failed to save image.";
            return false;
        }
        return true;
    }

private:
    AppContext* m_ctx = nullptr;
};

#include "MyExporterPlugin.moc"

Optional settings widget

Override settingsWidget(QWidget* parent) to return a widget shown in the export dialog. Use QObject::setObjectName() on child widgets — their object names become the keys in the settings map passed to exportImage(). See examples/plugins/webp_exporter/WebpExporterPlugin.cpp for a worked example.


JSON metadata file

Every plugin needs a small JSON file referenced in Q_PLUGIN_METADATA. The content is embedded into the compiled library at build time by the Qt MOC — the file does not need to be distributed alongside the DLL.

{
    "id":          "com.example.myplugin",
    "name":        "My Plugin",
    "version":     "1.0.0",
    "description": "A short description."
}

qmake project file

QT       += core gui widgets
CONFIG   += plugin c++17
CONFIG   -= app_bundle
TEMPLATE  = lib

TARGET = myplugin

INCLUDEPATH += /path/to/photoflare/src/plugins

SOURCES += MyPlugin.cpp
OTHER_FILES += myplugin.json

DESTDIR = $$OUT_PWD/plugins

AppContext API reference

AppContext* is passed to your plugin via initialise() and remains valid for the lifetime of the plugin.

MethodDescription
QImage* currentImage()Returns a pointer to the currently open image, or nullptr if no image is open. Stable for the duration of a single UI operation.
void markCanvasDirty()Commits any direct modifications to *currentImage() back to the canvas and triggers a repaint. Call after modifying the image directly.
void pushUndoState(const QString& label)Snapshots the current image into the undo stack. Not required when using IFilterPlugin::apply() — the host handles it automatically.
void registerMenuAction(const QString& path, QAction* action)Adds an action to the menu bar. Use "/" as a separator, e.g. "Tools/My Plugin".
void registerDockPanel(const QString& title, QWidget* panel)Registers a widget as a dockable side panel.
void showStatusMessage(const QString& msg, int ms)Shows a transient message in the status bar.
QSettings& pluginSettings(const QString& pluginId)Returns a QSettings instance scoped to your plugin ID for persistent settings storage.
QWidget* mainWindow()Returns the main window pointer. Use only to parent dialogs.
QString appVersion()Returns the Photoflare version string, e.g. "2.1.0".

Return to table of contents >>