diff --git a/Cargo.toml b/Cargo.toml index e29e0d9..f8e3b78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ build = "build.rs" libc = "0.2.70" [build-dependencies] -bindgen = "0.53.2" +bindgen = "0.56.0" pkg-config = "0.3.17" [features] diff --git a/build.rs b/build.rs index 9dce66c..881b69a 100644 --- a/build.rs +++ b/build.rs @@ -24,9 +24,17 @@ use std::path::{Path, PathBuf}; use std::process::Command; const MIN_VERSION: &'static str = "7.0"; -const MAX_VERSION: &'static str = "7.1"; +const MAX_VERSION: &'static str = "7.2"; -static HEADER: &'static str = "#include \n"; +static HEADER: &'static str = r#"#define MAGICKCORE_QUANTUM_DEPTH 16 +#include +"#; + +//on windows path env always contain : like c: +pub const PATH_SEPARATOR: &str = match cfg!(target_os = "windows") { + true => ";", + _ => ":", +}; fn main() { if cfg!(target_os = "freebsd") { @@ -41,14 +49,14 @@ fn main() { env_var_set_default("IMAGE_MAGICK_LIBS", "MagickWand-7"); } - let cppflags = Command::new("MagickCore-config") - .arg("--cppflags") - .output() - .unwrap() - .stdout; - let cppflags = String::from_utf8(cppflags).unwrap(); + let check_cppflags = Command::new("MagickCore-config").arg("--cppflags").output(); + //on windows can not exec + if check_cppflags.is_ok() { + let cppflags = check_cppflags.unwrap().stdout; + let cppflags = String::from_utf8(cppflags).unwrap(); - env_var_set_default("BINDGEN_EXTRA_CLANG_ARGS", &cppflags); + env_var_set_default("BINDGEN_EXTRA_CLANG_ARGS", &cppflags); + } let lib_dirs = find_image_magick_lib_dirs(); for d in &lib_dirs { @@ -75,7 +83,7 @@ fn main() { let target = env::var("TARGET").unwrap(); let libs_env = env::var("IMAGE_MAGICK_LIBS").ok(); let libs = match libs_env { - Some(ref v) => v.split(":").map(|x| x.to_owned()).collect(), + Some(ref v) => v.split(PATH_SEPARATOR).map(|x| x.to_owned()).collect(), None => { if target.contains("windows") { vec!["CORE_RL_MagickWand_".to_string()] @@ -172,7 +180,11 @@ fn env_var_set_default(name: &str, value: &str) { fn find_image_magick_lib_dirs() -> Vec { println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_LIB_DIRS"); env::var("IMAGE_MAGICK_LIB_DIRS") - .map(|x| x.split(":").map(PathBuf::from).collect::>()) + .map(|x| { + x.split(PATH_SEPARATOR) + .map(PathBuf::from) + .collect::>() + }) .or_else(|_| Ok(vec![find_image_magick_dir()?.join("lib")])) .or_else(|_: env::VarError| -> Result<_, env::VarError> { Ok(run_pkg_config().link_paths) }) .expect("Couldn't find ImageMagick library directory") @@ -181,7 +193,11 @@ fn find_image_magick_lib_dirs() -> Vec { fn find_image_magick_include_dirs() -> Vec { println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_INCLUDE_DIRS"); env::var("IMAGE_MAGICK_INCLUDE_DIRS") - .map(|x| x.split(":").map(PathBuf::from).collect::>()) + .map(|x| { + x.split(PATH_SEPARATOR) + .map(PathBuf::from) + .collect::>() + }) .or_else(|_| Ok(vec![find_image_magick_dir()?.join("include")])) .or_else(|_: env::VarError| -> Result<_, env::VarError> { Ok(run_pkg_config().include_paths) @@ -258,10 +274,7 @@ fn run_pkg_config() -> pkg_config::Library { .unwrap() .success() { - panic!(format!( - "MagickWand version must be no higher than {}", - MAX_VERSION - )); + panic!("MagickWand version must be less than 7.1"); } // We have to split the version check and the cflags/libs check because // you can't do both at the same time on RHEL (apparently). diff --git a/docker/Dockerfile b/docker/Dockerfile index 96662b4..d1f46f3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,9 +4,9 @@ RUN apt-get update \ && apt-get -y install curl build-essential clang pkg-config libjpeg-turbo-progs libpng-dev \ && rm -rfv /var/lib/apt/lists/* -ENV MAGICK_VERSION 7.0 +ENV MAGICK_VERSION 7.1 -RUN curl https://www.imagemagick.org/download/ImageMagick.tar.gz | tar xz \ +RUN curl https://download.imagemagick.org/ImageMagick/download/ImageMagick.tar.gz | tar xz \ && cd ImageMagick-${MAGICK_VERSION}* \ && ./configure --with-magick-plus-plus=no --with-perl=no \ && make \ @@ -16,6 +16,8 @@ RUN curl https://www.imagemagick.org/download/ImageMagick.tar.gz | tar xz \ RUN adduser --disabled-password --gecos '' magick-rust +ADD policy.xml /usr/local/lib/ImageMagick-7.0.11/config-Q16HDRI/policy.xml + USER magick-rust ENV USER=magick-rust LD_LIBRARY_PATH=/usr/local/lib diff --git a/docker/Dockerfile.docs b/docker/Dockerfile.docs new file mode 100644 index 0000000..de8913e --- /dev/null +++ b/docker/Dockerfile.docs @@ -0,0 +1,34 @@ +FROM rust:latest as builder + +RUN apt-get update \ + && apt-get -y install curl build-essential clang pkg-config libjpeg-turbo-progs libpng-dev \ + && rm -rfv /var/lib/apt/lists/* + +ENV MAGICK_VERSION 7.0 + +RUN curl https://www.imagemagick.org/download/ImageMagick.tar.gz | tar xz \ + && cd ImageMagick-${MAGICK_VERSION}* \ + && ./configure --with-magick-plus-plus=no --with-perl=no \ + && make \ + && make install \ + && cd .. \ + && rm -r ImageMagick-${MAGICK_VERSION}* + +RUN adduser --disabled-password --gecos '' magick-rust \ + && mkdir /src \ + && chown magick-rust:magick-rust /src + +USER magick-rust + +ENV USER=magick-rust LD_LIBRARY_PATH=/usr/local/lib + +WORKDIR /src + +COPY --chown=magick-rust:magick-rust Cargo.toml Cargo.lock build.rs ./ +COPY --chown=magick-rust:magick-rust ./src ./src + +RUN cargo doc --no-deps + +FROM arm64v8/nginx:mainline-alpine + +COPY --from=builder --chown=nginx:nginx /src/target/doc /usr/share/nginx/html diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f5e6b3b..b59c405 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -9,3 +9,8 @@ services: - ..:/src stdin_open: true tty: true + + magick-docs: + build: + context: .. + dockerfile: docker/Dockerfile.docs diff --git a/docker/policy.xml b/docker/policy.xml new file mode 100644 index 0000000..12719e7 --- /dev/null +++ b/docker/policy.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/wand/magick.rs b/src/wand/magick.rs index a94d991..a60e12b 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -37,6 +37,23 @@ wand_common!( MagickGetException ); +/// Resource type to use with [set_resource_limit](MagickWand::set_resource_limit) +#[derive(Debug, Clone, Copy)] +pub enum ResourceType { + Undefined = bindings::ResourceType_UndefinedResource as isize, + Area = bindings::ResourceType_AreaResource as isize, + Disk = bindings::ResourceType_DiskResource as isize, + File = bindings::ResourceType_FileResource as isize, + Height = bindings::ResourceType_HeightResource as isize, + Map = bindings::ResourceType_MapResource as isize, + Memory = bindings::ResourceType_MemoryResource as isize, + Thread = bindings::ResourceType_ThreadResource as isize, + Throttle = bindings::ResourceType_ThrottleResource as isize, + Time = bindings::ResourceType_TimeResource as isize, + Width = bindings::ResourceType_WidthResource as isize, + ListLength = bindings::ResourceType_ListLengthResource as isize, +} + /// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick. /// /// Instantiating a `MagickWand` will construct an ImageMagick "wand" @@ -56,6 +73,19 @@ impl MagickWand { } } + pub fn set_resource_limit(resource: ResourceType, limit: u64) -> Result<(), &'static str> { + let result = unsafe { + bindings::SetMagickResourceLimit( + resource as bindings::ResourceType, + limit as bindings::MagickSizeType, + ) + }; + match result { + bindings::MagickBooleanType_MagickTrue => Ok(()), + _ => Err("failed to set resource limit"), + } + } + pub fn set_option(&mut self, key: &str, value: &str) -> Result<(), &'static str> { let c_key = CString::new(key).unwrap(); let c_value = CString::new(value).unwrap(); @@ -267,21 +297,21 @@ impl MagickWand { // Define two 'quantum_range' functions because the bindings::QuantumRange symbol // is not available if hdri is disabled in the compiled ImageMagick libs - #[cfg(not(feature="disable-hdri"))] + #[cfg(not(feature = "disable-hdri"))] fn quantum_range(&self) -> Result { return Ok(bindings::QuantumRange); } // with disable-hdri enabled we define our own quantum_range // values lifted directly from magick-type.h - #[cfg(feature="disable-hdri")] + #[cfg(feature = "disable-hdri")] fn quantum_range(&self) -> Result { match bindings::MAGICKCORE_QUANTUM_DEPTH { 8 => Ok(255.0f64), 16 => Ok(65535.0f64), 32 => Ok(4294967295.0f64), 64 => Ok(18446744073709551615.0f64), - _ => Err("Quantum depth must be one of 8, 16, 32 or 64") + _ => Err("Quantum depth must be one of 8, 16, 32 or 64"), } } @@ -309,7 +339,6 @@ impl MagickWand { } } - /// Extend the image as defined by the geometry, gravity, and wand background color. Set the /// (x,y) offset of the geometry to move the original wand relative to the extended wand. pub fn extend_image( @@ -702,20 +731,31 @@ impl MagickWand { } } - /// Rescale the image using seam carving algorithm - pub fn liquid_rescale_image(&self, width: usize, height: usize, delta_x: f64, rigidity: f64) -> Result<(), &'static str> { - match unsafe { bindings::MagickLiquidRescaleImage(self.wand, width, height, delta_x, rigidity) } { + pub fn liquid_rescale_image( + &self, + width: usize, + height: usize, + delta_x: f64, + rigidity: f64, + ) -> Result<(), &'static str> { + match unsafe { + bindings::MagickLiquidRescaleImage(self.wand, width, height, delta_x, rigidity) + } { bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err("failed to liquid-rescale image") + _ => Err("failed to liquid-rescale image"), } } /// Implodes the image towards the center by the specified percentage - pub fn implode(&self, amount: f64, method: bindings::PixelInterpolateMethod) -> Result<(), &'static str> { + pub fn implode( + &self, + amount: f64, + method: bindings::PixelInterpolateMethod, + ) -> Result<(), &'static str> { match unsafe { bindings::MagickImplodeImage(self.wand, amount, method) } { bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err("failed to implode image") + _ => Err("failed to implode image"), } } @@ -830,6 +870,9 @@ impl MagickWand { // Sets the image to the specified alpha level. MagickSetImageAlpha => set_image_alpha(alpha: f64) + // Control the brightness, saturation, and hue of an image + MagickModulateImage => modulate_image(brightness: f64, saturation: f64, hue: f64) + /// Set the image alpha channel mode. MagickSetImageAlphaChannel => set_image_alpha_channel( alpha_channel: bindings::AlphaChannelOption) @@ -846,6 +889,9 @@ impl MagickWand { /// Discard all but one of any pixel color. MagickUniqueImageColors => unique_image_colors() + + /// Applies k-means color reduction to the image. + MagickKmeansImage => kmeans(number_colors: size_t, max_iterations: size_t, tolerance: f64) ); get!(get_image_colors, MagickGetImageColors, size_t); diff --git a/src/wand/mod.rs b/src/wand/mod.rs index 29601df..2cf9201 100644 --- a/src/wand/mod.rs +++ b/src/wand/mod.rs @@ -20,5 +20,5 @@ mod magick; mod pixel; pub use self::drawing::DrawingWand; -pub use self::magick::MagickWand; +pub use self::magick::{MagickWand, ResourceType}; pub use self::pixel::{PixelWand, HSL}; diff --git a/tests/lib.rs b/tests/lib.rs index 89d31ff..eefbc0a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -16,7 +16,7 @@ extern crate magick_rust; -use magick_rust::{bindings, magick_wand_genesis, MagickWand, PixelWand}; +use magick_rust::{bindings, magick_wand_genesis, MagickWand, PixelWand, ResourceType}; use magick_rust::ToMagick; use std::error::Error; @@ -361,3 +361,13 @@ fn test_negate_image() { pixel_color.get_color_as_string().unwrap() ); } + +#[test] +fn test_resource_limits() { + START.call_once(|| { + magick_wand_genesis(); + }); + MagickWand::set_resource_limit(ResourceType::Thread, 1).unwrap(); + let wand = MagickWand::new(); + assert!(wand.read_image("tests/data/rust.png").is_ok()); +}