Skip to content

Instantly share code, notes, and snippets.

@maxivak
Last active April 25, 2023 03:27
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save maxivak/3924976 to your computer and use it in GitHub Desktop.
Save maxivak/3924976 to your computer and use it in GitHub Desktop.
Crop and resize an image using MiniMagick in Ruby on Rails. Two approaches.
def resize_nocrop_noscale(image, w,h)
w_original = image[:width].to_f
h_original = image[:height].to_f
if w_original < w && h_original < h
return image
end
# resize
image.resize("#{w}x#{h}")
return image
end
def resize_with_crop(img, w, h, options = {})
gravity = options[:gravity] || :center
w_original, h_original = [img[:width].to_f, img[:height].to_f]
op_resize = ''
# check proportions
if w_original * h < h_original * w
op_resize = "#{w.to_i}x"
w_result = w
h_result = (h_original * w / w_original)
else
op_resize = "x#{h.to_i}"
w_result = (w_original * h / h_original)
h_result = h
end
w_offset, h_offset = crop_offsets_by_gravity(gravity, [w_result, h_result], [ w, h])
img.combine_options do |i|
i.resize(op_resize)
i.gravity(gravity)
i.crop "#{w.to_i}x#{h.to_i}+#{w_offset}+#{h_offset}!"
end
img
end
# from http://www.dweebd.com/ruby/resizing-and-cropping-images-to-fixed-dimensions/
GRAVITY_TYPES = [ :north_west, :north, :north_east, :east, :south_east, :south, :south_west, :west, :center ]
def crop_offsets_by_gravity(gravity, original_dimensions, cropped_dimensions)
raise(ArgumentError, "Gravity must be one of #{GRAVITY_TYPES.inspect}") unless GRAVITY_TYPES.include?(gravity.to_sym)
raise(ArgumentError, "Original dimensions must be supplied as a [ width, height ] array") unless original_dimensions.kind_of?(Enumerable) && original_dimensions.size == 2
raise(ArgumentError, "Cropped dimensions must be supplied as a [ width, height ] array") unless cropped_dimensions.kind_of?(Enumerable) && cropped_dimensions.size == 2
original_width, original_height = original_dimensions
cropped_width, cropped_height = cropped_dimensions
vertical_offset = case gravity
when :north_west, :north, :north_east then 0
when :center, :east, :west then [ ((original_height - cropped_height) / 2.0).to_i, 0 ].max
when :south_west, :south, :south_east then (original_height - cropped_height).to_i
end
horizontal_offset = case gravity
when :north_west, :west, :south_west then 0
when :center, :north, :south then [ ((original_width - cropped_width) / 2.0).to_i, 0 ].max
when :north_east, :east, :south_east then (original_width - cropped_width).to_i
end
return [ horizontal_offset, vertical_offset ]
end
# see details - http://maxivak.com/crop-and-resize-an-image-using-minimagick-ruby-on-rails
def resize_with_nocrop(image, w, h)
w_original = image[:width].to_f
h_original = image[:height].to_f
if (w_original*h != h_original * w)
if w_original*h >= h_original * w
# long width
w_result = w
h_result = w_result * (h_original / w_original)
elsif w_original*h <= h_original * w
# long height
h_result = h
w_result = h_result * (w_original / h_original)
end
else
# good proportions
h_result = h
w_result = w
end
#
image.resize("#{w_result}x#{h_result}")
return image
end
@Dinuz
Copy link

Dinuz commented Feb 2, 2015

@maxivak I believe that in the resize_with_crop method, there is a mistake respect to the original code that you mentioned.

The fact that the line 23 in the gravity function there is not always 'Northwest' but the option gravity that we pass, mess up the cropping region. Effectively if you try your code without gravity option, it will set it up that to :center, and the cropping result will be just not around the center but starting from the bottom of the image, and that is weird.
What do you think?

Actually I just think that condition is completely wrong. It shouldn't have any gravity before crop (the gravity anyway is used to calculate the offset). Using a gravity flag that is not fixed as for example the original code 'NorthWest', will just create a lot of trouble in the image cropping: e.g. don't give option will set the option gravity to center, and with the gravity as center before the crop image, it will move it to the bottom.

I believe that you can be without any gravity before the crop, or if you want one just put it as fixed value as 'NorthWest'.

My 2 cents!

Cheers

@jkotchoff
Copy link

Awesome script, thanks.

Btw, this didn’t maintain the correct gravity orientation for me when I used this exact code for the resize_with_crop function.

I needed to remove the:

i.gravity(gravity)

line of code to make it work.

@Sinozet
Copy link

Sinozet commented Feb 22, 2017

Doesn't work properly. Two strings works wrong in my case. Should be:
when :center, :east, :west then [ ((original_height - cropped_height) / 2.0).to_i, 0 ].min
when :center, :north, :south then [ ((original_width - cropped_width) / 2.0).to_i, 0 ].min

@9mm
Copy link

9mm commented Feb 4, 2018

@Sinozet THANK YOU

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment