Add artifact related functions
This makes it possible to blend images with user configurable percent (see `set_image_artifact` documentation)
This commit is contained in:
@ -15,6 +15,7 @@ mod pixel_interpolate_method;
|
|||||||
mod rendering_intent;
|
mod rendering_intent;
|
||||||
mod resolution_type;
|
mod resolution_type;
|
||||||
mod resource_type;
|
mod resource_type;
|
||||||
|
mod statistic_type;
|
||||||
|
|
||||||
pub use self::alpha_channel_option::AlphaChannelOption;
|
pub use self::alpha_channel_option::AlphaChannelOption;
|
||||||
pub use self::colorspace_type::ColorspaceType;
|
pub use self::colorspace_type::ColorspaceType;
|
||||||
@ -33,3 +34,4 @@ pub use self::pixel_interpolate_method::PixelInterpolateMethod;
|
|||||||
pub use self::rendering_intent::RenderingIntent;
|
pub use self::rendering_intent::RenderingIntent;
|
||||||
pub use self::resolution_type::ResolutionType;
|
pub use self::resolution_type::ResolutionType;
|
||||||
pub use self::resource_type::ResourceType;
|
pub use self::resource_type::ResourceType;
|
||||||
|
pub use self::statistic_type::StatisticType;
|
||||||
|
|||||||
46
src/types/statistic_type.rs
Normal file
46
src/types/statistic_type.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use crate::bindings;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum StatisticType {
|
||||||
|
Undefined = bindings::StatisticType_UndefinedStatistic,
|
||||||
|
Gradient = bindings::StatisticType_GradientStatistic,
|
||||||
|
Maximum = bindings::StatisticType_MaximumStatistic,
|
||||||
|
Mean = bindings::StatisticType_MeanStatistic,
|
||||||
|
Median = bindings::StatisticType_MedianStatistic,
|
||||||
|
Minimum = bindings::StatisticType_MinimumStatistic,
|
||||||
|
Mode = bindings::StatisticType_ModeStatistic,
|
||||||
|
Nonpeak = bindings::StatisticType_NonpeakStatistic,
|
||||||
|
RootMeanSquare = bindings::StatisticType_RootMeanSquareStatistic,
|
||||||
|
StandardDeviation = bindings::StatisticType_StandardDeviationStatistic,
|
||||||
|
Contrast = bindings::StatisticType_ContrastStatistic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StatisticType {
|
||||||
|
fn default() -> Self {
|
||||||
|
return StatisticType::Undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StatisticType> for bindings::StatisticType {
|
||||||
|
fn from(value: StatisticType) -> Self {
|
||||||
|
return value as bindings::StatisticType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bindings::StatisticType> for StatisticType {
|
||||||
|
fn from(value: bindings::StatisticType) -> Self {
|
||||||
|
/*
|
||||||
|
* SAFETY:
|
||||||
|
*
|
||||||
|
* `StatisticType` has the same repr as `bindings::StatisticType` - u32
|
||||||
|
*
|
||||||
|
* If `value` is less than Contrast than it is in the vaild range and can be safely
|
||||||
|
* reinterpreted as `StatisticType`
|
||||||
|
*/
|
||||||
|
if value <= bindings::StatisticType_ContrastStatistic {
|
||||||
|
return unsafe { std::mem::transmute(value) };
|
||||||
|
}
|
||||||
|
return StatisticType::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -47,7 +47,8 @@ use crate::{
|
|||||||
PixelInterpolateMethod,
|
PixelInterpolateMethod,
|
||||||
RenderingIntent,
|
RenderingIntent,
|
||||||
ResolutionType,
|
ResolutionType,
|
||||||
ResourceType
|
ResourceType,
|
||||||
|
StatisticType,
|
||||||
};
|
};
|
||||||
|
|
||||||
wand_common!(
|
wand_common!(
|
||||||
@ -414,11 +415,21 @@ impl MagickWand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Apply sigmoidal contrast to the image
|
/// Apply sigmoidal contrast to the image
|
||||||
/// Midpoint is a number in range [0, 1]
|
///
|
||||||
|
/// Adjusts the contrast of an image with a non-linear sigmoidal contrast algorithm. Increase
|
||||||
|
/// the contrast of the image using a sigmoidal transfer function without saturating highlights
|
||||||
|
/// or shadows. Contrast indicates how much to increase the contrast (0 is none; 3 is typical;
|
||||||
|
/// 20 is pushing it); mid-point indicates where midtones fall in the resultant image (0.0 is
|
||||||
|
/// white; 0.5 is middle-gray; 1.0 is black). Set sharpen to `true` to increase the image
|
||||||
|
/// contrast otherwise the contrast is reduced.
|
||||||
|
///
|
||||||
|
/// * `sharpen`: increase or decrease image contrast
|
||||||
|
/// * `strength`: strength of the contrast, the larger the number the more 'threshold-like' it becomes.
|
||||||
|
/// * `midpoint`: midpoint of the function as a number in range [0, 1]
|
||||||
pub fn sigmoidal_contrast_image(
|
pub fn sigmoidal_contrast_image(
|
||||||
&self,
|
&self,
|
||||||
sharpen: bool,
|
sharpen: bool,
|
||||||
contrast: f64,
|
strength: f64,
|
||||||
midpoint: f64,
|
midpoint: f64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let quantum_range = self.quantum_range()?;
|
let quantum_range = self.quantum_range()?;
|
||||||
@ -427,7 +438,7 @@ impl MagickWand {
|
|||||||
bindings::MagickSigmoidalContrastImage(
|
bindings::MagickSigmoidalContrastImage(
|
||||||
self.wand,
|
self.wand,
|
||||||
sharpen.to_magick(),
|
sharpen.to_magick(),
|
||||||
contrast,
|
strength,
|
||||||
midpoint * quantum_range,
|
midpoint * quantum_range,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -521,6 +532,37 @@ impl MagickWand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace each pixel with corresponding statistic from the neighborhood of the specified width and height.
|
||||||
|
///
|
||||||
|
/// * `statistic_type`: the statistic type (e.g. `StatisticType::Median`, `StatisticType::Mode`, etc.).
|
||||||
|
/// * `width`: the width of the pixel neighborhood.
|
||||||
|
/// * `height`: the height of the pixel neighborhood.
|
||||||
|
pub fn statistic_image(
|
||||||
|
&self,
|
||||||
|
statistic_type: StatisticType,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
) -> Result<()> {
|
||||||
|
match unsafe {
|
||||||
|
bindings::MagickStatisticImage(
|
||||||
|
self.wand,
|
||||||
|
statistic_type.into(),
|
||||||
|
width.into(),
|
||||||
|
height.into()
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
MagickTrue => Ok(()),
|
||||||
|
_ => Err(MagickError("failed to calculate statistics for image")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate median for each pixel's neighborhood.
|
||||||
|
///
|
||||||
|
/// See [statistic_image](Self::statistic_image)
|
||||||
|
pub fn median_blur_image(&self, width: usize, height: usize) -> Result<()> {
|
||||||
|
return self.statistic_image(StatisticType::Median, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
/// Adaptively resize the currently selected image.
|
/// Adaptively resize the currently selected image.
|
||||||
pub fn adaptive_resize_image(&self, width: usize, height: usize) -> Result<()> {
|
pub fn adaptive_resize_image(&self, width: usize, height: usize) -> Result<()> {
|
||||||
match unsafe { bindings::MagickAdaptiveResizeImage(self.wand, width, height) } {
|
match unsafe { bindings::MagickAdaptiveResizeImage(self.wand, width, height) } {
|
||||||
@ -577,6 +619,131 @@ impl MagickWand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a value associated with the specified artifact.
|
||||||
|
///
|
||||||
|
/// * `artifact`: the artifact.
|
||||||
|
pub fn get_image_artifact(&self, artifact: &str) -> Result<String> {
|
||||||
|
let c_artifact = CString::new(artifact).map_err(|_| MagickError("artifact string contains null byte"))?;
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
bindings::MagickGetImageArtifact(
|
||||||
|
self.wand,
|
||||||
|
c_artifact.as_ptr()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = if result.is_null() {
|
||||||
|
Err(MagickError("missing artifact"))
|
||||||
|
} else {
|
||||||
|
// convert (and copy) the C string to a Rust string
|
||||||
|
let cstr = unsafe { CStr::from_ptr(result) };
|
||||||
|
Ok(cstr.to_string_lossy().into_owned())
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
bindings::MagickRelinquishMemory(result as *mut c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_image_artifacts(&self, pattern: &str) -> Result<Vec<String>> {
|
||||||
|
let c_pattern = CString::new(pattern).map_err(|_| MagickError("artifact string contains null byte"))?;
|
||||||
|
let mut num_of_artifacts: size_t = 0;
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
bindings::MagickGetImageArtifacts(
|
||||||
|
self.wand,
|
||||||
|
c_pattern.as_ptr(),
|
||||||
|
&mut num_of_artifacts
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = if result.is_null() {
|
||||||
|
Err(MagickError("no artifacts found"))
|
||||||
|
} else {
|
||||||
|
let mut artifacts: Vec<String> = Vec::with_capacity(num_of_artifacts);
|
||||||
|
for i in 0..num_of_artifacts {
|
||||||
|
// convert (and copy) the C string to a Rust string
|
||||||
|
let cstr = unsafe { CStr::from_ptr(*result.add(i)) };
|
||||||
|
artifacts.push(cstr.to_string_lossy().into_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(artifacts)
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
bindings::MagickRelinquishMemory(result as *mut c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a key-value pair in the image artifact namespace. Artifacts differ from properties.
|
||||||
|
/// Properties are public and are generally exported to an external image format if the format
|
||||||
|
/// supports it. Artifacts are private and are utilized by the internal ImageMagick API to
|
||||||
|
/// modify the behavior of certain algorithms.
|
||||||
|
///
|
||||||
|
/// * `artifact`: the artifact.
|
||||||
|
/// * `value`: the value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// This example shows how you can blend an image with its blurred copy with 50% opacity by
|
||||||
|
/// setting "compose:args" to "50". This is equivalent to having `-define compose:args=50` when
|
||||||
|
/// using imagemagick cli.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let mut wand1 = MagickWand::new();
|
||||||
|
/// wand1.read_image("test.jpg")?;
|
||||||
|
/// let wand2 = wand1.clone();
|
||||||
|
///
|
||||||
|
/// wand1.median_blur_image(10, 10)?;
|
||||||
|
///
|
||||||
|
/// wand1.set_image_artifact("compose:args", "50")?;
|
||||||
|
/// wand1.compose_images(&wand2, CompositeOperator::Blend, false, 0, 0)?;
|
||||||
|
///
|
||||||
|
/// wand1.write_image("res.jpeg")?;
|
||||||
|
/// ```
|
||||||
|
pub fn set_image_artifact(
|
||||||
|
&mut self,
|
||||||
|
artifact: &str,
|
||||||
|
value: &str
|
||||||
|
) -> Result<()> {
|
||||||
|
let c_artifact = CString::new(artifact).map_err(|_| MagickError("artifact string contains null byte"))?;
|
||||||
|
let c_value = CString::new(value).map_err(|_| MagickError("value string contains null byte"))?;
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
bindings::MagickSetImageArtifact(
|
||||||
|
self.wand,
|
||||||
|
c_artifact.as_ptr(),
|
||||||
|
c_value.as_ptr()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
MagickTrue => Ok(()),
|
||||||
|
_ => Err(MagickError("failed to set image artifact")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes a wand artifact.
|
||||||
|
///
|
||||||
|
/// * `artifact`: the artifact.
|
||||||
|
pub fn delete_image_artifact(&mut self, artifact: &str) -> Result<()> {
|
||||||
|
let c_artifact = CString::new(artifact).map_err(|_| MagickError("artifact string contains null byte"))?;
|
||||||
|
|
||||||
|
match unsafe {
|
||||||
|
bindings::MagickDeleteImageArtifact(
|
||||||
|
self.wand,
|
||||||
|
c_artifact.as_ptr()
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
MagickTrue => Ok(()),
|
||||||
|
_ => Err(MagickError("failed to delete image artifact")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve the named image property value.
|
/// Retrieve the named image property value.
|
||||||
pub fn get_image_property(&self, name: &str) -> Result<String> {
|
pub fn get_image_property(&self, name: &str) -> Result<String> {
|
||||||
let c_name = CString::new(name).unwrap();
|
let c_name = CString::new(name).unwrap();
|
||||||
@ -594,6 +761,38 @@ impl MagickWand {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_image_properties(&self, pattern: &str) -> Result<Vec<String>> {
|
||||||
|
let c_pattern = CString::new(pattern).map_err(|_| MagickError("artifact string contains null byte"))?;
|
||||||
|
let mut num_of_artifacts: size_t = 0;
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
bindings::MagickGetImageProperties(
|
||||||
|
self.wand,
|
||||||
|
c_pattern.as_ptr(),
|
||||||
|
&mut num_of_artifacts
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = if result.is_null() {
|
||||||
|
Err(MagickError("no artifacts found"))
|
||||||
|
} else {
|
||||||
|
let mut artifacts: Vec<String> = Vec::with_capacity(num_of_artifacts);
|
||||||
|
for i in 0..num_of_artifacts {
|
||||||
|
// convert (and copy) the C string to a Rust string
|
||||||
|
let cstr = unsafe { CStr::from_ptr(*result.add(i)) };
|
||||||
|
artifacts.push(cstr.to_string_lossy().into_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(artifacts)
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
bindings::MagickRelinquishMemory(result as *mut c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the named image property.
|
/// Set the named image property.
|
||||||
pub fn set_image_property(&self, name: &str, value: &str) -> Result<()> {
|
pub fn set_image_property(&self, name: &str, value: &str) -> Result<()> {
|
||||||
let c_name = CString::new(name).unwrap();
|
let c_name = CString::new(name).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user