Reputation: 1111
I'm using carrierwave and mini_magick to resize my images, generate thumbnails. It works great for still images such as jpg and png, but when I try gif, it distorts it. You can see original vs the resized example below. Any idea how I make make it generate gif thumb without breaking it?
Original GIF :) https://i.sstatic.net/iCSdl.jpg
Generated Thumbnail GIF :( https://i.sstatic.net/rfEcO.jpg
ps how came the thumbnail's size is bigger than the original? Original is 800*600px and thumbnail is 400*300px. The whole point of generating thumbnail is having smaller file size anyways.
Thank you!
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url(*args)
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
def default_url(*args)
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "post.jpg"].compact.join('_'))
end
# Process files as they are uploaded:
# process scale: [800, 600]
# process :resize_to_fit => [800, 600]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process resize_to_fit: [50, 50]
# end
version :thumb do
process resize_to_fit: [400, 300]
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_whitelist
%w(jpg jpeg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
Upvotes: 1
Views: 1327
Reputation: 81
The gif you're trying to resize is optimized so that, after the first frame, each subsequent frame contains only the pixels that need to change. When the gif animates, the frames are stacked on top of one another creating the full animation.
Additionally, each frame is only large enough to contain the pixels that need to change. So, the size of each frame is different. When you resize the gif, Imagemagick is resizing each frame to be 400 by 300px regardless of its original size, which is causing the distortion you see.
You can fix this applying an Imagemagick command (with Minimagick bindings in Ruby) called coalesce
. It de-optimizes the original image so that each frame is the full size of the canvas.
Calling coalesce
makes the file size much larger, so it's necessary to re-optimize the gif once you're done resizing it.
Here's an example:
version :thumb do
process my_resize: [400, 300]
end
def my_resize(width, height)
if @file.content_type == "image/gif"
gif_safe_transform! do |image|
image.resize "#{width}x#{height}" # Perform any transformations here.
end
else
# Process other filetypes if necessary.
end
end
def gif_safe_transform!
MiniMagick::Tool::Convert.new do |image|
image << @file.path
image.coalesce # Remove optimizations so each layer shows the full image.
yield image
image.layers "Optimize" # Re-optimize the image.
image << @file.path
end
end
I wrote a more in-depth explanation with some examples here
Upvotes: 5