-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Efficiently converting image formats
When uploading images, adding more that 1 process to a version can cause RMagick or MimiMagick to run multiple commands. This leads to slower processing times and increased file sizes.
MiniMagick has a solution for this.
https://github.com/probablycorey/mini_magick/blob/master/lib/mini_magick.rb#L346
# You can use multiple commands together using this method. Very easy to use!
#
# @example
# image.combine_options do |c|
# c.draw "image Over 0,0 10,10 '#{MINUS_IMAGE_PATH}'"
# c.thumbnail "300x500>"
# c.background background
# end
#
So to use this we can create an uploader like this.
class PhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
version :normal do
process :efficient_conversion => [640, 960]
end
private
def efficient_conversion(width, height)
manipulate! do |img|
img.combine_options do |c|
c.fuzz "3%"
c.trim
c.resize "#{width}x#{height}>"
c.resize "#{width}x#{height}<"
end
img
end
end
end
There is a catch with changing the format. To change format we must change the name of the output file. This is how ImageMagick changes the format. So we need to use the MiniMagicks#format
method, luckily format
takes a block just like combine_options
.
def efficient_conversion(width, height)
manipulate! do |img|
img.format("png") do |c|
c.fuzz "3%"
c.trim
c.resize "#{width}x#{height}>"
c.resize "#{width}x#{height}<"
end
img
end
end
There is another catch with changing the format. Using the format
method will only change the name of the tmp file. The final version of the file is named by carrier wave and even though the file it creates will be a genuine file of the specified format with the correct mime type, it's extension will be not be changed. We need to use the filename
method to set this.
def filename
"#{model.nicely_formatted_filename}.png"
end
TLDR; use this
c.push '+profile'
c.+ "!xmp,*"
c.profile "path/to/color_profiles/sRGB_v4_ICC_preference_displayclass.icc"
- Most of ImageMagick's args look like
-resize \"640x960\"
. - Adding a profile is done with
-profile
. - Removing a profile is done with
+profile
. Not exactly intuitive, but it does the job. - MiniMagick uses method missing to create the args, so
c.rotate "90"
turns into-rotate \"90\"
.
To add an arg that starts with a '+', like the +profile
arg, we need to use the push
method followed by the +
method. The +
method turns the previously pushed arg into a '+' arg and then adds the passed options to it.
This code will strip all profiles except 'xmp' then add the 'sRGB.icc' profile.
c.push '+profile'
c.+ "!xmp,*"
c.profile "path/to/color_profiles/sRGB_v4_ICC_preference_displayclass.icc"
- Full list of
mogrify
arguments - http://www.imagemagick.org/www/command-line-options.html - Notes on geometry which is used by many of the commands - http://www.imagemagick.org/www/command-line-processing.html#geometry
class PhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
version :normal do
process :mogrify => [{
:resolution => '108x369'
}]
end
def default_url
"#{model.class.to_s.underscore.downcase}/#{mounted_as}/missing/" + [version_name, 'missing.png'].compact.join('_')
end
def extension_white_list
%w(jpg jpeg gif png bmp tif tiff)
end
def filename
"#{model.nicely_formatted_filename}.png"
end
private
def mogrify(options = {})
manipulate! do |img|
img.format("png") do |c|
c.fuzz "3%"
c.trim
c.rotate "#{options[:rotate]}" if options.has_key?(:rotate)
c.resize "#{options[:resolution]}>" if options.has_key?(:resolution)
c.resize "#{options[:resolution]}<" if options.has_key?(:resolution)
c.push '+profile'
c.+ "!xmp,*"
c.profile "#{Rails.root}/lib/color_profiles/sRGB_v4_ICC_preference_displayclass.icc"
c.colorspace "sRGB"
end
img
end
end
end