diff --git a/CHANGELOG.md b/CHANGELOG.md index cee4c9a..12050e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,25 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +This file follows the convention described at +[Keep a Changelog](http://keepachangelog.com/en/1.0.0/). + +## [0.7.0] - 2017-08-26 +### Changed +- Upgrade bindgen to 0.29 +- Change to MagickWand 7.0; this introduces backward incompatible changes +- `get_quantum` and `set_quantum` now take `Quantum` instead of `u16` +- `resize_image` no longer takes a `blur_factor` argument +- `InterpolatePixelMethod` was renamed `PixelInterpolateMethod` ## [0.6.6] - 2017-07-08 ### Changed - Downgrade to version 0.25.5 of `bindgen` library to avoid errors on Linux. ## [0.6.5] - 2017-07-07 -### Changed +### Added - Add `compare_images()` method to `MagickWand` type. +### Changed - Update to latest release of `bindgen` library. ## [0.6.4] - 2017-04-08 @@ -48,11 +59,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - hjr3: Changed `read_image_blob()` to borrow data rather than take ownership. ## [0.5.0] - 2016-05-18 -### Changed +### Added - marjakm: Added numerous functions and enabled cross-compile support. ## [0.4.0] - 2016-03-29 -### Changed +### Added - Add functions for detecting and correcting image orientation. ## [0.3.3] - 2016-03-17 @@ -68,7 +79,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix bug `get_image_property()` to ensure C string is copied. ## [0.3.0] - 2016-01-02 -### Changed +### Added - Add `get_image_property()` function to retrieve, for example, EXIF data. ## [0.2.3] - 2015-12-26 @@ -84,7 +95,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix the cargo package name (replace dash with underscore). ## [0.2.0] - 2015-06-10 -### Changed +### Added - Add a `fit()` function for fitting an image to a given bounds. ## [0.1.0] - 2015-06-09 diff --git a/Cargo.toml b/Cargo.toml index 68cc838..92987ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "magick_rust" -version = "0.6.6" +version = "0.7.0" authors = ["Nathan Fiedler "] description = "Selection of Rust bindings for the ImageMagick library." homepage = "https://github.com/nlfiedler/magick-rust" @@ -14,6 +14,5 @@ build = "build.rs" libc = ">=0.2" [build-dependencies] -# bindgen 0.26.x results in "already been defined in this module" errors (bug #687) -bindgen = "0.25.5" +bindgen = "0.29" pkg-config = "0.3.9" diff --git a/README.md b/README.md index 78bbb63..af3a694 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,19 @@ # magick-rust -A somewhat safe Rust interface to the [ImageMagick](http://www.imagemagick.org/) system, in particular, the MagickWand library. Many of the functions in the MagickWand API are still missing, and those that are needed will be gradually added. +A somewhat safe Rust interface to the [ImageMagick](http://www.imagemagick.org/) system, in particular, the MagickWand library. Many of the functions in the MagickWand API are still missing, but over time more will be added. Pull requests are welcome. -## Dependenices +## Dependencies * Rust (~latest release) * Cargo (~latest release) -* ImageMagick (version 6.9) - - [FreeBSD](https://www.freebsd.org) provides this version - - [Homebrew](http://brew.sh) requires special steps: - + `brew install imagemagick@6` - + `brew link --force imagemagick@6` - - Linux may require building ImageMagick from source -* Clang (version 3.5 or higher) +* ImageMagick (version 7.0.x) + - Does _not_ work with ImageMagick 6.x due to backward incompatible changes. + - [FreeBSD](https://www.freebsd.org): `sudo pkg install ImageMagick7` + - [Homebrew](http://brew.sh): `brew install imagemagick` + - Linux may require building ImageMagick from source, see the `Dockerfile` for an example +* [Clang](https://clang.llvm.org) (version 3.5 or higher) - Or whatever version is dictated by [rust-bindgen](https://github.com/servo/rust-bindgen) -* Must have `pkg-config` in order to link with MagickWand. - -See the `docs/Development_Setup.md` file for details particular to each platform. +* `pkg-config`, to facilitate linking with ImageMagick. ## Build and Test @@ -51,3 +48,20 @@ fn resize() -> Result, &'static str> { ``` Writing the image to a file rather than an in-memory blob is done by replacing the call to `write_image_blob()` with `write_image()`, which takes a string for the path to the file. + +## Contributing + +There are still many missing functions, so if you find there is something you would like to see added to this library, feel free to file an issue. Even better, fork the repo, and write the thin wrapper necessary to expose the MagickWand function. For getters and setters this is often very easy, just add a row to the table in `wand/magick.rs`, and it will work with no additional coding. Tests are optional, as this crate is basically a thin wrapper around code that is assumed to be thoroughly tested already. If you make a change that you want to contribute, please feel free to submit a pull request. + +## Docker + +[Docker](https://www.docker.com) can be used to build and test the code without affecting your development environment, which may have a different version of ImageMagick installed. The use of `docker-compose`, as shown in the example below, is optional, but it makes the process very simple. + +``` +$ cd docker +$ docker-compose build +$ docker-compose start +$ docker-compose run magick-rust +$ cargo build +$ cargo test +``` diff --git a/build.rs b/build.rs index c0efb2b..ed9b436 100644 --- a/build.rs +++ b/build.rs @@ -22,10 +22,10 @@ use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; -const MIN_VERSION: &'static str = "6.9"; -const MAX_VERSION: &'static str = "6.10"; +const MIN_VERSION: &'static str = "7.0"; +const MAX_VERSION: &'static str = "7.1"; -static HEADER: &'static str = "#include \n"; +static HEADER: &'static str = "#include \n"; fn main() { // Assert that the appropriate version of MagickWand is installed, @@ -42,7 +42,7 @@ fn main() { .arg(format!("--max-version={}", MAX_VERSION)) .arg("MagickWand") .status().unwrap().success() { - panic!("MagickWand version must be no higher than 6.9"); + panic!(format!("MagickWand version must be no higher than {}", MAX_VERSION)); } // 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). @@ -60,20 +60,26 @@ fn main() { // Geneate the bindings. let mut builder = bindgen::Builder::default() - .no_unstable_rust() .emit_builtins() .ctypes_prefix("libc") .raw_line("extern crate libc;") - .header(gen_h_path.to_str().unwrap()); + .header(gen_h_path.to_str().unwrap()) + /* https://github.com/rust-lang-nursery/rust-bindgen/issues/687 */ + .hide_type("FP_NAN") + .hide_type("FP_INFINITE") + .hide_type("FP_ZERO") + .hide_type("FP_SUBNORMAL") + .hide_type("FP_NORMAL"); + for include_path in library.include_paths { builder = builder.clang_arg(format!("-I{}", include_path.to_string_lossy())); } if cfg!(target_os = "freebsd") { // pkg_config does not seem to work properly on FreeBSD, so // hard-code the builder settings for the time being. - builder = builder.clang_arg("-I/usr/local/include/ImageMagick-6"); + builder = builder.clang_arg("-I/usr/local/include/ImageMagick-7"); // Need to hack the linker flags as well. - println!("cargo:rustc-link-lib=dylib=MagickWand-6"); + println!("cargo:rustc-link-lib=dylib=MagickWand-7"); println!("cargo:rustc-link-search=native=/usr/local/lib"); } let bindings = builder.generate().unwrap(); diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..8235d67 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,23 @@ +FROM rust:1.19.0-stretch + +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.6-7 + +RUN curl https://www.imagemagick.org/download/ImageMagick-${MAGICK_VERSION}.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 + +USER magick-rust + +ENV USER=magick-rust LD_LIBRARY_PATH=/usr/local/lib + +WORKDIR /src diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..f5e6b3b --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' + +services: + magick-rust: + build: + context: . + dockerfile: Dockerfile + volumes: + - ..:/src + stdin_open: true + tty: true diff --git a/docs/Development_Setup.md b/docs/Development_Setup.md deleted file mode 100644 index 33afa76..0000000 --- a/docs/Development_Setup.md +++ /dev/null @@ -1,42 +0,0 @@ -# Development Setup - -## Mac OS X - -[Homebrew](http://brew.sh) is the easiest way to install everything on Mac. - -1. Install Xcode -1. Install Homebrew -1. Install Rust and Cargo -1. Install ImageMagick - -``` -$ xcode-select --install -$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" -$ brew install rust -$ brew install imagemagick@6 -$ brew link --force imagemagick@6 -$ brew install pkg-config -``` - -Then build in the usual manner, as shown in the `README.md` file (i.e. `cargo build` and `cargo test`). - -## FreeBSD - -1. Install Rust -1. Install Cargo -1. Install ImageMagick -1. Install the Clang libraries - -See the FreeBSD `fabfile.py` for an example of how to install everything. In particular, note that it may be necessary to set `LIBCLANG_PATH` to the path containing the `libclang.so` library. - -Then build in the usual manner, as shown in the `README.md` file (i.e. `cargo build` and `cargo test`). - -## Ubuntu Linux - -1. Install Rust and Cargo -1. Install ImageMagick -1. Install the Clang libraries - -See the Ubuntu `fabfile.py` for an example of how to install everything. In particular, note that it may be necessary to set `LIBCLANG_PATH` to the path containing the `libclang.so` library. - -Then build in the usual manner, as shown in the `README.md` file (i.e. `cargo build` and `cargo test`). If running the tests fails because the MagickWand library cannot be found, try rebuilding the ldconfig cache (`sudo ldconfig`). diff --git a/src/wand/macros.rs b/src/wand/macros.rs index a519771..ff48c96 100644 --- a/src/wand/macros.rs +++ b/src/wand/macros.rs @@ -204,13 +204,13 @@ macro_rules! color_set_get { pub fn $get(&self) -> f64 { unsafe { ::bindings::$c_get(self.wand) } } - pub fn $get_quantum(&self) -> u16 { + pub fn $get_quantum(&self) -> bindings::Quantum { unsafe { ::bindings::$c_get_quantum(self.wand) } } pub fn $set(&mut self, v: f64) { unsafe { ::bindings::$c_set(self.wand, v) } } - pub fn $set_quantum(&mut self, v: u16) { + pub fn $set_quantum(&mut self, v: bindings::Quantum) { unsafe { ::bindings::$c_set_quantum(self.wand, v) } } )* diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 087dec0..ffea7bb 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -16,7 +16,7 @@ use std::fmt; use std::ptr; use std::ffi::{CStr, CString}; -use libc::{c_double, c_void}; +use libc::{c_void}; #[cfg(target_os = "freebsd")] use libc::{size_t, ssize_t}; @@ -152,6 +152,15 @@ impl MagickWand { } } + /// Retrieve the page geometry (width, height, x offset, y offset) of the image. + pub fn get_image_page(&self) -> (usize, usize, isize, isize) { + let (mut width, mut height, mut x, mut y) = (0usize, 0usize, 0isize, 0isize); + unsafe { + bindings::MagickGetImagePage(self.wand, &mut width, &mut height, &mut x, &mut y); + } + (width, height, x, y) + } + /// Retrieve the named image property value. pub fn get_image_property(&self, name: &str) -> Result { let c_name = CString::new(name).unwrap(); @@ -172,16 +181,12 @@ impl MagickWand { } /// Resize the image to the specified width and height, using the - /// specified filter type with the specified blur / sharpness factor. - /// - /// blur_factor values greater than 1 create blurriness, while values - /// less than 1 create sharpness. + /// specified filter type. pub fn resize_image(&self, width: usize, height: usize, - filter: bindings::FilterTypes, blur_factor: f64) { + filter: bindings::FilterType) { unsafe { bindings::MagickResizeImage( - self.wand, width as size_t, height as size_t, - filter, blur_factor as c_double + self.wand, width as size_t, height as size_t, filter ); } } @@ -206,7 +211,7 @@ impl MagickWand { bindings::MagickResetIterator(self.wand); while bindings::MagickNextImage(self.wand) != bindings::MagickBooleanType::MagickFalse { bindings::MagickResizeImage(self.wand, new_width, new_height, - bindings::FilterTypes::LanczosFilter, 1.0); + bindings::FilterType::LanczosFilter); } } } @@ -270,7 +275,7 @@ impl MagickWand { let c_format = CString::new(format).unwrap(); let mut length: size_t = 0; let blob = unsafe { - bindings::MagickSetImageIndex(self.wand, 0); + bindings::MagickSetIteratorIndex(self.wand, 0); bindings::MagickSetImageFormat(self.wand, c_format.as_ptr()); bindings::MagickGetImagesBlob(self.wand, &mut length) }; @@ -307,9 +312,8 @@ impl MagickWand { get_image_fuzz, set_image_fuzz, MagickGetImageFuzz, MagickSetImageFuzz, f64 get_image_gamma, set_image_gamma, MagickGetImageGamma, MagickSetImageGamma, f64 get_image_gravity, set_image_gravity, MagickGetImageGravity, MagickSetImageGravity, bindings::GravityType - get_image_index, set_image_index, MagickGetImageIndex, MagickSetImageIndex, ssize_t get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, bindings::InterlaceType - get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, bindings::InterpolatePixelMethod + get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, bindings::PixelInterpolateMethod get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, size_t get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, bindings::OrientationType get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, bindings::RenderingIntent @@ -317,7 +321,7 @@ impl MagickWand { get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, bindings::ResolutionType get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, bindings::InterlaceType - get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, bindings::InterpolatePixelMethod + get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, bindings::PixelInterpolateMethod get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, ssize_t get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, bindings::OrientationType get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64 diff --git a/src/wand/pixel.rs b/src/wand/pixel.rs index 97fa80d..76aad8d 100644 --- a/src/wand/pixel.rs +++ b/src/wand/pixel.rs @@ -86,7 +86,7 @@ impl PixelWand { set_get_unchecked!( get_color_count, set_color_count, PixelGetColorCount, PixelSetColorCount, size_t - get_index, set_index, PixelGetIndex, PixelSetIndex, u16 + get_index, set_index, PixelGetIndex, PixelSetIndex, bindings::Quantum get_fuzz, set_fuzz, PixelGetFuzz, PixelSetFuzz, f64 ); diff --git a/tests/data/rust.gif b/tests/data/rust.gif new file mode 100644 index 0000000..06a1fde Binary files /dev/null and b/tests/data/rust.gif differ diff --git a/tests/lib.rs b/tests/lib.rs index 3f37d6f..6002e21 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -24,7 +24,7 @@ use std::io::Read; use std::path::Path; use std::sync::{Once, ONCE_INIT}; -// TODO: nathan does not understand how to expose the FilterTypes without +// TODO: nathan does not understand how to expose the FilterType without // this ugliness, his Rust skills are sorely lacking use magick_rust::bindings; @@ -57,7 +57,7 @@ fn test_resize_image() { 1 => 1, height => height / 2 }; - wand.resize_image(halfwidth, halfheight, bindings::FilterTypes::LanczosFilter, 1.0); + wand.resize_image(halfwidth, halfheight, bindings::FilterType::LanczosFilter); assert_eq!(256, wand.get_image_width()); assert_eq!(192, wand.get_image_height()); } @@ -208,3 +208,15 @@ fn test_set_option() { assert_eq!(192, wand.get_image_width()); assert_eq!(144, wand.get_image_height()); } + +#[test] +fn test_page_geometry() { + START.call_once(|| { + magick_wand_genesis(); + }); + let wand = MagickWand::new(); + assert!(wand.read_image("tests/data/rust.gif").is_ok()); + assert_eq!((156, 150, 39, 36), wand.get_image_page()); /* width, height, x offset, y offset */ + assert_eq!(80, wand.get_image_width()); + assert_eq!(76, wand.get_image_height()); +} diff --git a/vagrant/ubuntu16/Vagrantfile b/vagrant/ubuntu16/Vagrantfile deleted file mode 100644 index 8f2898b..0000000 --- a/vagrant/ubuntu16/Vagrantfile +++ /dev/null @@ -1,20 +0,0 @@ -# -# Vagrantfile for Ubuntu Linux 16.04 test environment. -# -Vagrant.configure(2) do |config| - - config.vm.box = 'ubuntu/xenial64' - - # need enough memory to build syntex_syntax crate - config.vm.provider 'virtualbox' do |vb| - vb.memory = 2048 - end - - # bring the system up to date - config.vm.provision 'shell', inline: <<-SHELL - sudo apt-get -y autoremove - sudo apt-get -y update - sudo apt-get -y upgrade - SHELL - -end diff --git a/vagrant/ubuntu16/fabfile.py b/vagrant/ubuntu16/fabfile.py deleted file mode 100644 index 13c7967..0000000 --- a/vagrant/ubuntu16/fabfile.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -# ------------------------------------------------------------------- -# -# Copyright (c) 2016-2017 Nathan Fiedler -# -# This file is provided to you under the Apache License, -# Version 2.0 (the "License"); you may not use this file -# except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# ------------------------------------------------------------------- -"""Fabric file for installing requirements on Ubuntu Linux.""" - -import os - -from fabric.api import cd, env, run, sudo, task - -env.hosts = ["default"] -env.use_ssh_config = True -if os.path.exists("user_ssh_config"): - env.ssh_config_path = "user_ssh_config" -else: - env.ssh_config_path = "ssh_config" - - -@task -def all(): - """Install everything needed to build magick-rust.""" - sudo('apt-get -q -y install git') - sudo('apt-get -q -y install pkg-config') - # need the latest possible release of rust for bindgen to work - run('wget -O rustup-init https://sh.rustup.rs') - run('chmod +x rustup-init') - run('./rustup-init -y') - run('rm -f rustup-init') - sudo('apt-get -q -y build-dep imagemagick') - run('wget -q https://www.imagemagick.org/download/ImageMagick-6.9.8-10.tar.gz') - run('tar zxf ImageMagick-6.9.8-10.tar.gz') - with cd('ImageMagick-*'): - run('./configure') - run('make') - sudo('make install') - run('rm -rf ImageMagick*') - sudo('apt-get -q -y install clang libclang-dev') - # set LIBCLANG_PATH so rustc can find libclang.so in its hidden place - # (using the append operation results in 'Unmatched ".' error) - run("echo 'export LIBCLANG_PATH=/usr/lib/llvm-3.8/lib' >> .bashrc")