Skip to content

Repository files navigation

image_ffi_project

Origin. Assignment from the Yandex Practicum Rust course (FFI module). The task: load image-processing plugins at runtime across a C ABI without letting panics unwind across the boundary. Developed locally, published as a snapshot.

A small CLI that loads a PNG, applies an image-processing plugin (a dynamic library loaded at runtime), and saves the result as PNG. The interesting part is the plugin boundary: pixels and parameters are passed across a C ABI, and each plugin isolates its own panics so nothing unwinds across FFI.

Workspace layout

Crate Kind Purpose
plugin_interface rlib Shared process_image signature, the one safe FFI adapter (run_plugin), and key=value parameter helpers.
image_processor bin+lib Host: CLI (clap), image I/O (image), dynamic loading (libloading), errors (thiserror).
mirror_plugin cdylib mirror.dll / libmirror.so — horizontal/vertical flip.
blur_plugin cdylib blur.dll / libblur.so — separable box blur.

Plugin ABI

Every plugin exports a single C-compatible function:

#[unsafe(no_mangle)]
pub extern "C" fn process_image(
    width: u32,
    height: u32,
    rgba_data: *mut u8,        // width * height * 4 bytes, modified in place
    params: *const c_char,     // NUL-terminated parameter string
);

The buffer is RGBA, 4 bytes per pixel, modified in place. The plugin owns parsing params and must not panic across the boundary — all plugins forward to plugin_interface::run_plugin, which reconstructs the slice, decodes the parameters and wraps the work in catch_unwind.

The host turns a bare --plugin <name> into a file name with the platform's DLL_PREFIX/DLL_SUFFIX (mirrormirror.dll on Windows, libmirror.so on Linux, libmirror.dylib on macOS) and looks for it under --plugin-path.

Build

cargo build --workspace

The plugin libraries land in target/debug/ (mirror.dll, blur.dll, …), which is also the default --plugin-path.

Run

# Mirror (horizontal flip)
cargo run -p image_processor -- \
  --input input.png \
  --output out_mirror.png \
  --plugin mirror \
  --params params/mirror.txt

# Blur
cargo run -p image_processor -- \
  --input input.png \
  --output out_blur.png \
  --plugin blur \
  --params params/blur.txt

--plugin-path is optional and defaults to target/debug. Set RUST_LOG=info (or debug) for progress logging from the host.

Parameters

Plain key=value, separated by commas or newlines; keys are case-insensitive.

Plugin Keys Defaults Example
mirror horizontal, vertical (bool) both false horizontal=true,vertical=false
blur radius, iterations (u32) radius=1,iterations=1 radius=4,iterations=3

Unknown keys, malformed entries and wrong value types are reported; on a parameter error the plugin leaves the image unchanged.

Test

cargo test --workspace

Covers parameter parsing for both plugins (valid / empty / unknown key / bad value), the mirror logic on a 2×2 buffer (byte-exact), and the blur logic (uniform image unchanged; a single bright pixel spreads to its neighbours).

About

Plugin-based image processor in Rust: cdylib plugins over a C ABI, libloading, panic isolation via catch_unwind, safe FFI adapter. Course assignment.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages