#![allow(dead_code, unused_imports, unused_variables, unused_macros, unreachable_patterns)] use std::sync::{OnceLock, atomic::{AtomicU8, Ordering}}; use std::path::{Path, PathBuf}; use std::os::raw::c_char; use std::ffi::CString; use libloading::Library; /// Global vision backend preference mirrored into the dynamic `hcie-vision` plugin. /// 0 = CPU, 1 = GPU. Stored here so it can be set before the plugin is loaded. static VISION_BACKEND: AtomicU8 = AtomicU8::new(1); pub fn map_brush_style(style: hcie_protocol::BrushStyle) -> hcie_brush_engine::BrushStyle { match style { hcie_protocol::BrushStyle::Round => hcie_brush_engine::BrushStyle::Round, hcie_protocol::BrushStyle::Square => hcie_brush_engine::BrushStyle::Square, hcie_protocol::BrushStyle::HardRound => hcie_brush_engine::BrushStyle::HardRound, hcie_protocol::BrushStyle::SoftRound => hcie_brush_engine::BrushStyle::SoftRound, hcie_protocol::BrushStyle::Star => hcie_brush_engine::BrushStyle::Star, hcie_protocol::BrushStyle::Noise => hcie_brush_engine::BrushStyle::Noise, hcie_protocol::BrushStyle::Texture => hcie_brush_engine::BrushStyle::Texture, hcie_protocol::BrushStyle::Spray => hcie_brush_engine::BrushStyle::Spray, hcie_protocol::BrushStyle::Pencil => hcie_brush_engine::BrushStyle::Pencil, hcie_protocol::BrushStyle::Pen => hcie_brush_engine::BrushStyle::Pen, hcie_protocol::BrushStyle::Calligraphy => hcie_brush_engine::BrushStyle::Calligraphy, hcie_protocol::BrushStyle::Oil => hcie_brush_engine::BrushStyle::Oil, hcie_protocol::BrushStyle::Charcoal => hcie_brush_engine::BrushStyle::Charcoal, hcie_protocol::BrushStyle::Leaf => hcie_brush_engine::BrushStyle::Leaf, hcie_protocol::BrushStyle::Rock => hcie_brush_engine::BrushStyle::Rock, hcie_protocol::BrushStyle::Meadow => hcie_brush_engine::BrushStyle::Meadow, hcie_protocol::BrushStyle::Wood => hcie_brush_engine::BrushStyle::Wood, hcie_protocol::BrushStyle::Watercolor => hcie_brush_engine::BrushStyle::Watercolor, hcie_protocol::BrushStyle::Marker => hcie_brush_engine::BrushStyle::Marker, hcie_protocol::BrushStyle::Sketch => hcie_brush_engine::BrushStyle::Sketch, hcie_protocol::BrushStyle::Hatch => hcie_brush_engine::BrushStyle::Hatch, hcie_protocol::BrushStyle::Glow => hcie_brush_engine::BrushStyle::Glow, hcie_protocol::BrushStyle::Airbrush => hcie_brush_engine::BrushStyle::Airbrush, hcie_protocol::BrushStyle::Crayon => hcie_brush_engine::BrushStyle::Crayon, hcie_protocol::BrushStyle::WetPaint => hcie_brush_engine::BrushStyle::WetPaint, hcie_protocol::BrushStyle::InkPen => hcie_brush_engine::BrushStyle::InkPen, hcie_protocol::BrushStyle::Clouds => hcie_brush_engine::BrushStyle::Clouds, hcie_protocol::BrushStyle::Dirt => hcie_brush_engine::BrushStyle::Dirt, hcie_protocol::BrushStyle::Tree => hcie_brush_engine::BrushStyle::Tree, hcie_protocol::BrushStyle::Bristle => hcie_brush_engine::BrushStyle::Bristle, hcie_protocol::BrushStyle::Mixer => hcie_brush_engine::BrushStyle::Mixer, hcie_protocol::BrushStyle::Blender => hcie_brush_engine::BrushStyle::Blender, hcie_protocol::BrushStyle::Default => hcie_brush_engine::BrushStyle::Round, hcie_protocol::BrushStyle::Bitmap => hcie_brush_engine::BrushStyle::Bitmap, } } pub fn blend_pixels(dst: [u8; 4], src: [u8; 4], mode: hcie_protocol::BlendMode, opacity: f32) -> [u8; 4] { hcie_blend::blend_pixels(dst, src, mode.into(), opacity) } pub fn blend_buffers(dst: &mut [u8], src: &[u8], mode: hcie_protocol::BlendMode, opacity: f32) { hcie_blend::blend_buffers(dst, src, mode.into(), opacity) } pub fn apply_filter(filter_id: &str, params: &serde_json::Value, pixels: &mut [u8], width: u32, height: u32) -> Result<(), String> { let mut fl = hcie_protocol::Layer::new_transparent("", width, height); fl.pixels.copy_from_slice(pixels); hcie_filter::apply_filter(&mut fl, filter_id, params); let len = pixels.len().min(fl.pixels.len()); pixels[..len].copy_from_slice(&fl.pixels[..len]); Ok(()) } pub fn draw_line(layer: &mut hcie_protocol::Layer, x0: f32, y0: f32, x1: f32, y1: f32, color: [u8; 4], stroke_size: f32, mask: Option<&[u8]>) { hcie_draw::draw_line(layer, x0, y0, x1, y1, color, stroke_size, mask); layer.dirty = true; } pub fn draw_filled_rect(layer: &mut hcie_protocol::Layer, x1: f32, y1: f32, x2: f32, y2: f32, color: [u8; 4], mask: Option<&[u8]>) { hcie_draw::draw_filled_rect(layer, x1, y1, x2, y2, color, mask); layer.dirty = true; } pub fn draw_filled_ellipse(layer: &mut hcie_protocol::Layer, cx: f32, cy: f32, rx: f32, ry: f32, color: [u8; 4], mask: Option<&[u8]>) { hcie_draw::draw_filled_ellipse(layer, cx, cy, rx, ry, color, mask); layer.dirty = true; } pub fn flood_fill(layer: &mut hcie_protocol::Layer, x: u32, y: u32, color: [u8; 4], tolerance: u8, mask: Option<&[u8]>) { hcie_draw::flood_fill(layer, x, y, color, tolerance, mask); layer.dirty = true; } pub fn draw_brush_stroke(layer: &mut hcie_protocol::Layer, points: &[(f32, f32, f32)], color: [u8; 4], tip: &hcie_protocol::BrushTip, is_eraser: bool, mask: Option<&[u8]>, stroke_mask: Option<&mut [u8]>, bg_pixels: Option<&[u8]>) { let style = map_brush_style(tip.style); let ct = hcie_brush_engine::BrushTip { style, size: tip.size, opacity: tip.opacity, hardness: tip.hardness, spacing: tip.spacing, flow: tip.flow, jitter_amount: tip.jitter_amount, scatter_amount: tip.scatter_amount, angle: tip.angle, roundness: tip.roundness, spray_particle_size: tip.spray_particle_size, spray_density: tip.spray_density, bitmap_pixels: tip.bitmap_pixels.clone(), bitmap_w: tip.bitmap_w, bitmap_h: tip.bitmap_h, color_variant: tip.color_variant, variant_amount: tip.variant_amount, density: tip.density, drawing_angle: false, rotation_random: 0.0, }; hcie_draw::draw_brush_stroke(layer, points, color, &ct, is_eraser, mask, stroke_mask, bg_pixels); layer.dirty = true; } pub fn draw_specialized_stroke(pixels: &mut [u8], width: u32, height: u32, points: &[(f32, f32, f32)], brush_style: hcie_protocol::BrushStyle, size: f32, hardness: f32, color: [u8; 4], opacity: f32, spacing_ratio: f32, is_eraser: bool, sketch_history: Option<&mut Vec<(f32, f32)>>, spray_particle_size: f32, spray_density: u32, mask: Option<&[u8]>, stroke_mask: Option<&mut [u8]>, bg_pixels: Option<&[u8]>, color_variant: bool, variant_amount: f32, density: f32) { let style = map_brush_style(brush_style); hcie_brush_engine::draw_specialized_stroke(pixels, width, height, points, style, size, hardness, color, opacity, spacing_ratio, is_eraser, sketch_history, spray_particle_size, spray_density, mask, stroke_mask, bg_pixels, color_variant, variant_amount, density) } pub fn composite_layers(layers: &[hcie_protocol::Layer], cw: u32, ch: u32) -> Vec { hcie_composite::composite_layers(layers, cw, ch) } pub fn composite_layers_region(layers: &[hcie_protocol::Layer], cw: u32, ch: u32, x0: u32, y0: u32, x1: u32, y1: u32, out: &mut [u8]) { hcie_composite::composite_layers_region(layers, cw, ch, x0, y0, x1, y1, out) } pub mod tiled { pub fn composite_tiled_into(layers: &[hcie_protocol::Layer], tile_layers: &[Option], cw: u32, ch: u32, x0: u32, y0: u32, x1: u32, y1: u32, out: &mut [u8]) { hcie_composite::tiled::composite_tiled_into(layers, tile_layers, cw, ch, x0, y0, x1, y1, out) } } pub fn load_image(path: &Path) -> Result { hcie_io::load_image(path) } pub fn save_image(layer: &hcie_protocol::Layer, path: &Path, format: &str) -> Result<(), String> { hcie_io::save_image(layer, path, format) } pub fn import_psd(path: &Path) -> Result, String> { hcie_psd::import_psd(path) } pub fn export_psd(layers: &[hcie_protocol::Layer], cw: u32, ch: u32, composited: &[u8], path: &Path) -> Result<(), String> { hcie_psd::save_psd(layers, cw, ch, composited, path) } pub fn import_kra(path: &Path) -> Result, String> { hcie_kra::import_kra(path) } pub fn export_kra(layers: &[hcie_protocol::Layer], cw: u32, ch: u32, composited: &[u8], path: &Path) -> Result<(), String> { hcie_kra::export_kra(layers, cw, ch, composited, path) } pub fn load_native(path: &Path) -> Result, String> { hcie_native::load_native(path) } pub fn save_native(layers: &[hcie_protocol::Layer], path: &Path) -> Result<(), String> { hcie_native::save_native(layers, path) } pub mod svg_import { pub fn import_svg(data: &[u8]) -> Result, String> { hcie_io::svg_import::import_svg(data) } } pub mod vector { pub fn render_vector_shapes(layer: &mut hcie_protocol::Layer) { hcie_vector::render_vector_shapes(layer) } #[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)] pub enum BooleanOp { Union, Intersect, Subtract, Xor } pub fn boolean_shapes(op: BooleanOp, a: &hcie_protocol::VectorShape, b: &hcie_protocol::VectorShape) -> Option { let vop = match op { BooleanOp::Union => hcie_vector::path_boolean::BooleanOp::Union, BooleanOp::Intersect => hcie_vector::path_boolean::BooleanOp::Intersect, BooleanOp::Subtract => hcie_vector::path_boolean::BooleanOp::Subtract, BooleanOp::Xor => hcie_vector::path_boolean::BooleanOp::Xor }; hcie_vector::path_boolean::boolean_shapes(vop, a, b) } } // ───────────────────────────────────────────────────────────────────────────── // FFI — only hcie-ai and hcie-vision // ───────────────────────────────────────────────────────────────────────────── fn find_plugin_path(name: &str) -> Result { let filename = if cfg!(target_os = "windows") { format!("{}.dll", name) } else if cfg!(target_os = "macos") { format!("lib{}.dylib", name) } else { format!("lib{}.so", name) }; let candidates: Vec = { let mut v = Vec::new(); // 1. Traditional plugins directory next to the executable or cwd. v.push(PathBuf::from("plugins")); if let Some(exe_plugins) = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.join("plugins"))) { v.push(exe_plugins.clone()); } // 2. Cargo workspace layout: the dynamic library is placed directly in // the target profile directory (e.g. target/debug/libhcie_ai.so). if let Some(exe_dir) = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.to_path_buf())) { v.push(exe_dir.clone()); // 3. Also check the deps subdirectory used by Cargo. v.push(exe_dir.join("deps")); // 4. If the executable is in target/, also try the sibling // target/ directory so debug/release builds can find // each other during development. if let Some(profile) = exe_dir.file_name().and_then(|s| s.to_str()) { if let Some(target_dir) = exe_dir.parent() { for other in &["debug", "release"] { if *other != profile { v.push(target_dir.join(other)); v.push(target_dir.join(other).join("deps")); } } } } } // 5. Walk up from the current working directory looking for plugins/ or // target/ directories. This helps when running from the project root. if let Ok(cwd) = std::env::current_dir() { let mut c = cwd.clone(); for _ in 0..5 { v.push(c.join("plugins")); v.push(c.join("target").join("debug")); v.push(c.join("target").join("debug").join("deps")); v.push(c.join("target").join("release")); v.push(c.join("target").join("release").join("deps")); if !c.pop() { break; } } } v }; for base in candidates { let p = base.join(&filename); if p.exists() { return Ok(p); } } Err(format!("Plugin '{}' not found", name)) } macro_rules! sym { ($lib:ident, $name:literal) => { *$lib.get(concat!($name, "\0").as_bytes()).expect(concat!("Failed to resolve ", $name)) }; } struct AiPlugins { _lib_ai: Library, _lib_vision: Library, ai_chat_complete_fn: unsafe extern "C" fn(*const c_char, *const c_char, *mut *mut u8, *mut usize) -> bool, ai_session_create_fn: unsafe extern "C" fn(*const c_char) -> i64, ai_session_push_user_fn: unsafe extern "C" fn(i64, *const c_char) -> bool, ai_session_push_assistant_fn: unsafe extern "C" fn(i64, *const c_char) -> bool, ai_session_clear_fn: unsafe extern "C" fn(i64) -> bool, ai_session_destroy_fn: unsafe extern "C" fn(i64), ai_session_serialize_fn: unsafe extern "C" fn(i64, *mut *mut u8, *mut usize) -> bool, ai_template_names_fn: unsafe extern "C" fn(*mut *mut u8, *mut usize) -> bool, ai_template_get_fn: unsafe extern "C" fn(*const c_char, *mut *mut u8, *mut usize) -> bool, ai_template_search_fn: unsafe extern "C" fn(*const c_char, *mut *mut u8, *mut usize) -> bool, ai_template_execute_fn: unsafe extern "C" fn(*const c_char, *const c_char, *mut *mut u8, *mut usize) -> bool, ai_execute_action_fn: unsafe extern "C" fn(*const c_char, *mut *mut u8, *mut usize) -> bool, ai_free_buffer_fn: unsafe extern "C" fn(*mut u8, usize), vision_background_removal_fn: unsafe extern "C" fn(*const u8, usize, u32, u32, *mut *mut u8, *mut usize) -> bool, vision_object_segment_fn: unsafe extern "C" fn(*const u8, usize, u32, u32, u32, u32, u32, u32, u32, u32, *const c_char, *mut *mut u8, *mut usize) -> bool, vision_super_resolution_fn: unsafe extern "C" fn(*const u8, usize, u32, u32, *mut *mut u8, *mut usize) -> bool, vision_inpaint_fn: unsafe extern "C" fn(*mut u8, usize, u32, u32, *const u8, usize, i32) -> bool, vision_build_polygon_mask_fn: unsafe extern "C" fn(*const c_char, u32, u32, *mut *mut u8, *mut usize) -> bool, vision_build_stroke_mask_fn: unsafe extern "C" fn(*const c_char, *const u8, usize, f32, u32, u32, *mut *mut u8, *mut usize) -> bool, vision_set_backend_fn: unsafe extern "C" fn(u8), vision_free_buffer_fn: unsafe extern "C" fn(*mut u8, usize), } static AI: OnceLock = OnceLock::new(); fn ai_plugins() -> &'static AiPlugins { AI.get_or_init(|| { let ap = find_plugin_path("hcie_ai").expect("Critical: hcie_ai plugin not found"); let vp = find_plugin_path("hcie_vision").expect("Critical: hcie_vision plugin not found"); unsafe { let la = Library::new(ap).expect("Failed hcie_ai"); let lv = Library::new(vp).expect("Failed hcie_vision"); let ai_chat_complete_fn = *la.get(b"hcie_ai_chat_complete_c\0").unwrap(); let ai_session_create_fn = *la.get(b"hcie_ai_session_create_c\0").unwrap(); let ai_session_push_user_fn = *la.get(b"hcie_ai_session_push_user_c\0").unwrap(); let ai_session_push_assistant_fn = *la.get(b"hcie_ai_session_push_assistant_c\0").unwrap(); let ai_session_clear_fn = *la.get(b"hcie_ai_session_clear_c\0").unwrap(); let ai_session_destroy_fn = *la.get(b"hcie_ai_session_destroy_c\0").unwrap(); let ai_session_serialize_fn = *la.get(b"hcie_ai_session_serialize_c\0").unwrap(); let ai_template_names_fn = *la.get(b"hcie_ai_template_names_c\0").unwrap(); let ai_template_get_fn = *la.get(b"hcie_ai_template_get_c\0").unwrap(); let ai_template_search_fn = *la.get(b"hcie_ai_template_search_c\0").unwrap(); let ai_template_execute_fn = *la.get(b"hcie_ai_template_execute_c\0").unwrap(); let ai_execute_action_fn = *la.get(b"hcie_ai_execute_action_c\0").unwrap(); let ai_free_buffer_fn = *la.get(b"free_buffer_c\0").unwrap(); let vision_background_removal_fn = *lv.get(b"hcie_vision_background_removal_c\0").unwrap(); let vision_object_segment_fn = *lv.get(b"hcie_vision_object_segment_c\0").unwrap(); let vision_super_resolution_fn = *lv.get(b"hcie_vision_super_resolution_c\0").unwrap(); let vision_inpaint_fn = *lv.get(b"hcie_vision_inpaint_c\0").unwrap(); let vision_build_polygon_mask_fn = *lv.get(b"hcie_vision_build_polygon_mask_c\0").unwrap(); let vision_build_stroke_mask_fn = *lv.get(b"hcie_vision_build_stroke_mask_c\0").unwrap(); let vision_set_backend_fn: unsafe extern "C" fn(u8) = *lv.get(b"hcie_vision_set_backend_c\0").unwrap(); let vision_free_buffer_fn = *lv.get(b"free_buffer_c\0").unwrap(); // Mirror the current backend preference into the freshly loaded plugin. let backend = VISION_BACKEND.load(Ordering::Relaxed); (vision_set_backend_fn)(backend); AiPlugins { _lib_ai: la, _lib_vision: lv, ai_chat_complete_fn, ai_session_create_fn, ai_session_push_user_fn, ai_session_push_assistant_fn, ai_session_clear_fn, ai_session_destroy_fn, ai_session_serialize_fn, ai_template_names_fn, ai_template_get_fn, ai_template_search_fn, ai_template_execute_fn, ai_execute_action_fn, ai_free_buffer_fn, vision_background_removal_fn, vision_object_segment_fn, vision_super_resolution_fn, vision_inpaint_fn, vision_build_polygon_mask_fn, vision_build_stroke_mask_fn, vision_set_backend_fn, vision_free_buffer_fn, } } }) } macro_rules! ai { ($fn:ident, $($a:expr),*) => { unsafe { (ai_plugins().$fn)($($a),*) } }; } /// Set the vision backend preference. If `hcie-vision` is already loaded the /// change is forwarded immediately via FFI; otherwise it is applied when the /// plugin is first initialised. pub fn vision_set_backend(backend: u8) { VISION_BACKEND.store(backend, Ordering::Relaxed); if AI.get().is_some() { ai!(vision_set_backend_fn, backend); } } pub mod ai { use super::*; macro_rules! c { ($e:expr) => { CString::new($e).unwrap() }; } pub fn chat_complete(config: &str, messages: &str) -> Result, String> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); if ai!(ai_chat_complete_fn, c!(config).as_ptr(), c!(messages).as_ptr(), &mut op, &mut ol) { let data = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Ok(data) } else { Err("AI chat failed".into()) } } pub fn session_create(s: &str) -> i64 { ai!(ai_session_create_fn, c!(s).as_ptr()) } pub fn session_push_user(sid: i64, t: &str) -> bool { ai!(ai_session_push_user_fn, sid, c!(t).as_ptr()) } pub fn session_push_assistant(sid: i64, t: &str) -> bool { ai!(ai_session_push_assistant_fn, sid, c!(t).as_ptr()) } pub fn session_clear(sid: i64) -> bool { ai!(ai_session_clear_fn, sid) } pub fn session_destroy(sid: i64) { ai!(ai_session_destroy_fn, sid) } pub fn session_serialize(sid: i64) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); if ai!(ai_session_serialize_fn, sid, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn template_names() -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); if ai!(ai_template_names_fn, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn template_get(name: &str) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let n = CString::new(name).ok()?; if ai!(ai_template_get_fn, n.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } } /// # Purpose /// Searches for AI prompt/action templates matching a specific query or tag. /// /// # Logic & Workflow /// 1. Converts the query string to a C-compatible null-terminated string. /// 2. Invokes the dynamically loaded FFI function `ai_template_search_fn`. /// 3. If successful, copies the returned raw byte slice into a Rust Vec and frees the FFI buffer. /// /// # Arguments /// * `query` - A search query string to filter templates. /// /// # Returns /// * `Some(Vec)` containing the serialized search results (JSON), or `None` if the call failed. /// /// # Side Effects / Dependencies /// * Relies on the dynamically loaded `hcie_ai` library. pub fn template_search(query: &str) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let q = CString::new(query).ok()?; if ai!(ai_template_search_fn, q.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn template_execute(name: &str, params: &str) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let (n, j) = (CString::new(name).ok()?, CString::new(params).ok()?); if ai!(ai_template_execute_fn, n.as_ptr(), j.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn execute_action(json: &str) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let j = CString::new(json).ok()?; if ai!(ai_execute_action_fn, j.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } } } pub mod vision { use super::*; pub fn background_removal(img: &[u8], w: u32, h: u32) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); if ai!(vision_background_removal_fn, img.as_ptr(), img.len(), w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn object_segment(img: &[u8], w: u32, h: u32, px: u32, py: u32, bx0: u32, by0: u32, bx1: u32, by1: u32, model_path: &str) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let mp = CString::new(model_path).unwrap_or_default(); if ai!(vision_object_segment_fn, img.as_ptr(), img.len(), w, h, px, py, bx0, by0, bx1, by1, mp.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn super_resolution(img: &[u8], w: u32, h: u32) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); if ai!(vision_super_resolution_fn, img.as_ptr(), img.len(), w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn inpaint(pixels: &mut [u8], w: u32, h: u32, mask: &[u8], r: i32) -> bool { ai!(vision_inpaint_fn, pixels.as_mut_ptr(), pixels.len(), w, h, mask.as_ptr(), mask.len(), r) } pub fn build_polygon_mask(json: &str, w: u32, h: u32) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let j = CString::new(json).ok()?; if ai!(vision_build_polygon_mask_fn, j.as_ptr(), w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None } } pub fn build_stroke_mask(json: &str, sel: &[u8], r: f32, w: u32, h: u32) -> Option> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let j = CString::new(json).ok()?; if ai!(vision_build_stroke_mask_fn, j.as_ptr(), sel.as_ptr(), sel.len(), r, w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None } } /// Forward the CPU/GPU backend preference to the dynamic `hcie-vision` plugin. pub fn set_backend(backend: u8) { crate::dynamic_loader::vision_set_backend(backend); } }