use crate::{GeometryInfo, KernelInfoType, bindings};
use crate::{MagickError, Result};
use std::ffi::CString;
#[derive(Debug, Clone, Default)]
pub struct KernelBuilder {
size: Option<(usize, usize)>,
center: Option<(usize, usize)>,
values: Option<Vec<f64>>,
info_type: Option<KernelInfoType>,
geom_info: Option<GeometryInfo>,
}
impl KernelBuilder {
pub fn set_size(mut self, size: (usize, usize)) -> KernelBuilder {
self.size = Some(size);
self
}
pub fn set_center(mut self, center: (usize, usize)) -> KernelBuilder {
self.center = Some(center);
self
}
pub fn set_values(mut self, values: &[f64]) -> KernelBuilder {
self.values = Some(values.into());
self
}
pub fn build(&self) -> Result<KernelInfo> {
let size = self
.size
.ok_or(MagickError("no kernel size given".to_string()))?;
let values = self
.values
.as_ref()
.ok_or(MagickError("no kernel values given".to_string()))?;
if values.len() != size.0 * size.1 {
return Err(MagickError(
"kernel size doesn't match kernel values size".to_string(),
));
}
let mut kernel_string = if let Some(center) = self.center {
format!("{}x{}+{}+{}:", size.0, size.1, center.0, center.1)
} else {
format!("{}x{}:", size.0, size.1,)
};
values.iter().for_each(|x| {
kernel_string.push_str(&format!("{x},"));
});
kernel_string.pop();
let c_kernel_string = CString::new(kernel_string).expect("CString::new() has failed");
let kernel_info =
unsafe { bindings::AcquireKernelInfo(c_kernel_string.as_ptr(), std::ptr::null_mut()) };
if kernel_info.is_null() {
return Err(MagickError("failed to acquire kernel info".to_string()));
}
Ok(KernelInfo::new(kernel_info))
}
pub fn set_info_type(mut self, info_type: crate::KernelInfoType) -> KernelBuilder {
self.info_type = Some(info_type);
self
}
pub fn set_geom_info(mut self, geom_info: crate::GeometryInfo) -> KernelBuilder {
self.geom_info = Some(geom_info);
self
}
pub fn build_builtin(&self) -> Result<KernelInfo> {
let info_type = self
.info_type
.ok_or(MagickError("no info type given".to_string()))?;
let geom_info = self
.geom_info
.ok_or(MagickError("no geometry info given".to_string()))?;
let kernel_info = unsafe {
bindings::AcquireKernelBuiltIn(info_type, geom_info.inner(), std::ptr::null_mut())
};
if kernel_info.is_null() {
return Err(MagickError(
"failed to acquire builtin kernel info".to_string(),
));
}
Ok(KernelInfo::new(kernel_info))
}
}
pub struct KernelInfo {
kernel_info: *mut bindings::KernelInfo,
}
impl KernelInfo {
fn new(kernel_info: *mut bindings::KernelInfo) -> KernelInfo {
KernelInfo { kernel_info }
}
pub fn scale(&mut self, factor: f64) {
unsafe {
bindings::ScaleKernelInfo(self.kernel_info, factor, bindings::GeometryFlags::NoValue)
}
}
pub fn normalize(&mut self) {
unsafe {
bindings::ScaleKernelInfo(
self.kernel_info,
1.0,
bindings::GeometryFlags::NormalizeValue,
)
}
}
pub fn correlate_normalize(&mut self) {
unsafe {
bindings::ScaleKernelInfo(
self.kernel_info,
1.0,
bindings::GeometryFlags::CorrelateNormalizeValue,
)
}
}
pub fn unity_add(&mut self, scale: f64) {
unsafe { bindings::UnityAddKernelInfo(self.kernel_info, scale) }
}
pub (crate) unsafe fn get_ptr(&self) -> *mut bindings::KernelInfo {
self.kernel_info
}
}
impl Drop for KernelInfo {
fn drop(&mut self) {
unsafe { bindings::DestroyKernelInfo(self.kernel_info) };
}
}
impl Clone for KernelInfo {
fn clone(&self) -> Self {
let kernel_info = unsafe { bindings::CloneKernelInfo(self.kernel_info) };
if kernel_info.is_null() {
panic!("failed to clone kernel info");
}
KernelInfo::new(kernel_info)
}
}
impl std::fmt::Debug for KernelInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unsafe { write!(f, "{:?}", *self.kernel_info) }
}
}