Resizing Animated GIFs with acts_as_attachment
If you’ve ever tried uploading an animated gif using Techno Weenie’s superb acts_as_attachment plugin you will have noticed that it only ever picks up the first frame!
This is actually due to the way that it’s interfacing with RMagick and is a relatively simple fix. Two files need to be updated, the first is in class_methods.rb:
# Yields a block containing an RMagick Image for the given binary data.
def with_image(data, &block)
begin
imgs = Magick::ImageList.new
binary_data = data.is_a?(Magick::ImageList) ? data : imgs.from_blob(data) unless !Object.const_defined?(:Magick)
rescue
# Log the failure to load the image. This should match ::Magick::ImageMagickError
# but that would cause acts_as_attachment to require rmagick.
logger.debug("Exception working with image: #{$!}")
binary_data = nil
end
block.call binary_data if block && binary_data
ensure
!binary_data.nil?
end
The changes here are very simple, we’re simply swapping out Magick::Image for Magick::ImageList as ImageMagick deals with animated gif’s as multiple images rather than a single file.
The next file that needs modifying is instance_methods.rb:
# Performs the actual resizing operation for a thumbnail
def thumbnail_for_image(img, size)
size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
size = [size, size] if size.is_a?(Fixnum)
img.each { |i| i.thumbnail(size.first, size[1]) }
else
img.each { |i| i.change_geometry!(size.to_s) { |cols, rows, image| image.resize!(cols, rows) } }
end
end
These changes are necessary as we are now dealing with an ImageList rather than an Image instance. Again the modifications are very simple, all that’s happening here is that instead of applying image modifications to just one image we loop through all the images in the ImageList and apply the modifications to each one – this is especially important for animated GIF’s as they won’t save properly unless all frames are the same dimensions.
Hopefully someone will find this useful, if you run into any problems or have suggestions on a better way to do this please leave a comment!