From bc0db4e812a67667c20ca21f710568d84fb3ceaa Mon Sep 17 00:00:00 2001 From: GustavoCaso Date: Tue, 19 Sep 2017 08:27:06 +0200 Subject: [PATCH] - Added ruby-vips gem - [MISSING] some commands --- image_processing.gemspec | 1 + lib/image_processing/vips.rb | 83 ++++++++++++++++++++++++++++++++ test/vips_test.rb | 92 ++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 lib/image_processing/vips.rb create mode 100644 test/vips_test.rb diff --git a/image_processing.gemspec b/image_processing.gemspec index 09ad40f..54bf8fc 100644 --- a/image_processing.gemspec +++ b/image_processing.gemspec @@ -20,5 +20,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "minitest-hooks" spec.add_development_dependency "minispec-metadata" spec.add_development_dependency "mini_magick", ">= 4.3.5" + spec.add_development_dependency "ruby-vips", ">= 2.0.0" spec.add_development_dependency "phashion" unless RUBY_ENGINE == "jruby" end diff --git a/lib/image_processing/vips.rb b/lib/image_processing/vips.rb new file mode 100644 index 0000000..2e76625 --- /dev/null +++ b/lib/image_processing/vips.rb @@ -0,0 +1,83 @@ +require "image_processing/version" +require "vips" +require "tempfile" + +module ImageProcessing + module Vips + module_function + + def convert!(image, format, page = nil, &block) + with_ruby_vips(image) do |img| + tmp_name = tmp_name(image.path, "_tmp.#{format}") + img.write_to_file(tmp_name) + tmp_name + end + end + + def auto_orient!(image) + with_ruby_vips(image) do |img| + img.autorot + tmp_name = tmp_name(image.path) + img.write_to_file(tmp_name) + tmp_name + end + end + + def resize_to_limit!(image, width, height) + with_ruby_vips(image) do |img| + img = resize_image(img, width, height) if width < img.width || height < img.height + tmp_name = tmp_name(image.path) + img.write_to_file(tmp_name) + tmp_name + end + end + + def resize_to_fit!(image, width, height) + with_ruby_vips(image) do |img| + img = resize_image(img, width, height) + tmp_name = tmp_name(image.path) + img.write_to_file(tmp_name) + tmp_name + end + end + # Convert an image into a MiniMagick::Image for the duration of the block, + # and at the end return a File object. + def with_ruby_vips(image) + image = ::Vips::Image.new_from_file image.path + file_path = yield image + File.new(file_path) + end + + # Creates a copy of the file and stores it into a Tempfile. Works for any + # IO object that responds to `#read(length = nil, outbuf = nil)`. + def _copy_to_tempfile(file) + extension = File.extname(file.path) if file.respond_to?(:path) + tempfile = Tempfile.new(["vips", extension.to_s], binmode: true) + IO.copy_stream(file, tempfile.path) + file.rewind + tempfile + end + + def tmp_name(path, ext='_tmp\1') + ext_regex = /(\.[[:alnum:]]+)$/ + path.sub(ext_regex, ext) + end + + def resize_image(image, width, height, min_or_max = :min) + ratio = get_ratio image, width, height, min_or_max + return image if ratio == 1 + image = if ratio > 1 + image.resize(ratio, kernel: :nearest) + else + image.resize(ratio, kernel: :cubic) + end + image + end + + def get_ratio(image, width,height, min_or_max = :min) + width_ratio = width.to_f / image.width + height_ratio = height.to_f / image.height + [width_ratio, height_ratio].send(min_or_max) + end + end +end diff --git a/test/vips_test.rb b/test/vips_test.rb new file mode 100644 index 0000000..ce70d63 --- /dev/null +++ b/test/vips_test.rb @@ -0,0 +1,92 @@ +require "test_helper" +require "mini_magick" +require "image_processing/vips" +require "stringio" + +describe ImageProcessing::Vips do + include ImageProcessing::Vips + + def assert_similar(expected, actual) + return if RUBY_ENGINE == "jruby" + + a = Phashion::Image.new(expected.path) + b = Phashion::Image.new(actual.path) + + distance = a.distance_from(b).abs + + assert_operator distance, :<, 2 + end + + def assert_dimensions(dimensions, file) + assert_equal dimensions, MiniMagick::Image.new(file.path).dimensions + end + + def assert_type(type, file) + assert_equal type, MiniMagick::Image.new(file.path).type + end + + def assert_resolution(resolution, file) + actual_resolution = MiniMagick::Image.new(file.path).resolution + # Travis has old imagemagick version + actual_resolution = actual_resolution.select(&:nonzero?) if ENV["CI"] + assert_equal resolution, actual_resolution + end + + def fixture_image(name) + File.open("test/fixtures/#{name}") + end + + before do + @portrait = _copy_to_tempfile(fixture_image("portrait.jpg")) + @landscape = _copy_to_tempfile(fixture_image("landscape.jpg")) + end + + + describe "#convert!" do + it "changes the image format" do + result = convert!(@portrait, "png") + assert_type "PNG", result + end + end + + describe "#auto_orient!" do + it "fixes the orientation of the image" do + result = auto_orient!(@portrait) + assert_equal "1", MiniMagick::Image.new(result.path).exif["Orientation"] + end + end + + describe "#resize_to_limit!" do + it "resizes the image up to a given limit" do + result = resize_to_limit!(@portrait, 400, 400) + assert_dimensions [300, 400], result + end + + it "does not resize the image if it is smaller than the limit" do + result = resize_to_limit!(@portrait, 1000, 1000) + assert_dimensions [600, 800], result + end + + it "produces correct image" do + result = resize_to_limit!(@portrait, 400, 400) + assert_similar fixture_image("limit.jpg"), result + end + end + + describe "#resize_to_fit!" do + it "resizes the image to fit given dimensions" do + result = resize_to_fit!(@portrait, 400, 400) + assert_dimensions [300, 400], result + end + + it "enlarges image if it is smaller than given dimensions" do + result = resize_to_fit!(@portrait, 1000, 1000) + assert_dimensions [750, 1000], result + end + + it "produces correct image" do + result = resize_to_fit!(@portrait, 400, 400) + assert_similar fixture_image("fit.jpg"), result + end + end +end