Writing plugins
QuickPaint plugins are dynamic libraries (.so / .dll / .dylib) that export a single C function:
const QpPluginInfo* qp_plugin_entry(void);The returned descriptor and everything it points to must stay valid for the lifetime of the process. All types are defined as #[repr(C)] in the qp-plugin-api crate, so plugins can also be written in C, C++, Zig and other languages.
Where plugins go
At startup QuickPaint loads the libraries in these folders:
- The
plugins/folder next to the executable - The
plugins/folder in the user settings folder (Linux:~/.config/quickpaint/plugins, Windows:%APPDATA%\EarthQuick\QuickPaint\config\plugins)
Loaded plugins and load errors are listed in Help > Plugins.
Writing an effect in Rust
[lib]
crate-type = ["cdylib"]
[dependencies]
qp-plugin-api = { path = "../../crates/qp-plugin-api" }use qp_plugin_api::sdk::{self, Render};
use qp_plugin_api::*;
extern "C" fn invert_red(args: *const QpRenderArgs) -> i32 {
let mut r = unsafe { Render::from_raw(args) };
let amount = r.int(0) as f32 / 100.0; // value by property order
r.map(|_x, _y, c| QpColor { r: c.r + ((255 - 2 * c.r as i32) as f32 * amount) as u8, ..c });
0 // success
}
export_plugin!(|| {
sdk::plugin("My Plugin", "Me", "1.0.0", "Description",
vec![sdk::effect("me.invertred", "Invert Red", "My Menu", menu::EFFECTS,
vec![sdk::int("amount", "Amount", 0, 100, 100)], invert_red)],
vec![])
});Property types
QuickPaint builds the settings dialog, with live preview, from the properties you declare.
| SDK function | Control | Value field |
|---|---|---|
int | slider | i |
float | slider | f |
boolean | check box | i (0/1) |
choice | radio buttons / drop-down list | i (index) |
color | color picker (primary / secondary / fixed default) | color |
angle | angle dial | f (degrees) |
point | 2D position picker, -1..1 around the center | f, f2 |
seed | "Reseed" button | i |
The render contract
srcanddstare straight (not premultiplied) RGBA, with a row stride ofwidth * 4bytes.dststarts as a copy ofsrc; you only need to write theroi_*rectangle.- The app applies the selection mask afterwards (pixels outside the selection are restored).
- In long loops check
Render::cancelled()and report progress withRender::progress(). - Render runs on a background thread; mind thread safety if you use global state.
File type plugins
QpFileTypeInfo gives the list of extensions ("raw;bin") and the load, save and free_image functions. The QpImage that load fills is handed back to your free_image once the app is done with it. The sample plugin that ships with QuickPaint adds a .qraw file type this way.
ABI version
QpPluginInfo::abi_version must be QP_ABI_VERSION. Plugins with another version are not loaded and are listed as errors.