Image overlays with Rmagick and Rails

I knew it wouldn’t be long before I had another foray into Rmagick. This time is pretty similar to last time, except this time I needed to overlay one image on top of another image. There are a few people who have posted about how to do this but I found the information pretty sparse. As I was looking I noticed that some people were using the ImageList class and others the Image class. After ping-ponging between the various tutorials and finding the differences between them pretty confusing, I thought I would sort myself out by using both.

So here is the code that generated that stunning image overlay. I tend to figure this stuff out by sticking it in a controller and tinkering  with it. Once it works I will move it into a model. The code below is taken from my controller. First, the version using the Rmagick’s ImageList class:

def rubylist
ruby = ImageList.new(‘public/images/ruby.jpg’)
rails = ImageList.new(‘public/images/rails.png’)
ruby.gravity=EastGravity
ruby.geometry=’0x0+20+20′
rails_on_ruby = ruby.composite_layers(rails, Magick::OverCompositeOp)
rails_on_ruby.format = ‘jpeg’
send_data rails_on_ruby.to_blob, :stream => ‘false’, :filename => ‘test.jpg’, :type => ‘image/jpeg’, :disposition => ‘inline’
end

So what we have are two ImageList objects; ruby and rails. I am going to use ruby as my “destination” ImageList, meaning it will be the base image that  the other image is applied to. Setting the Gravity and Geometry attributes on the “destination” object means that Rmagick will position the “source” image according to those settings. Calling composite_layers on ruby and feeding it rails and a composite operator let Rmagick know what should be combine with the base image and how it should combine them. Since we already gave it the “where” part of the equation with the gravity and geometry, the rest is easy.

Doing the same thing with the Image class instead of ImageList is just different enough to trip you (read: Me) up:

def ruby
ruby = Image.read(‘public/images/ruby.jpg’)[0] #This returns an Array! Get the first element.
rails = Image.read(‘public/images/rails.png’)[0]
rails_on_ruby = ruby.composite(rails, Magick::EastGravity, 0, 0, Magick::OverCompositeOp) #the 0,0 is the x,y
rails_on_ruby.format = ‘jpeg’
send_data rails_on_ruby.to_blob, :stream => ‘false’, :filename => ‘test.jpg’, :type => ‘image/jpeg’, :disposition => ‘inline’
end

Rmagick pretty consistently works in a way that is the opposite of what I expect. Where I would expect Image.read to return, say, an image, instead it returns an Array. So after some head scratching and a little RTFM time I learned you could do what you see above. Alternately you could do “Image.read(‘public/images/rails.png’).first” if you think it’s prettier. The geometry stuff takes a little fooling around to get sorted out and to see how it works with the gravity. My understanding is that the x,y is just to offset the object from where the gravity setting alone would have placed it.

Like the image annotations before, its not difficult to do, but its a pain to figure out. While there are a bunch of other ways to write this code, this will at least give a running start.

Advertisements

4 thoughts on “Image overlays with Rmagick and Rails”

  1. Hi Mike,

    Thanks for the article, I was looking for something similar. It helped me a lot.

    I had a situation where, i had given a kind of background image and set of other images to which are selected by user to superimpose on the background image. I achieved this by looping the above code, it worked like a charm..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s