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!