A project I’m currently working on requires image masking. I had already developed the site with plans to deploy to Heroku using Paperclip for image attachments using Amazon S3 for storage (you can read how I set that up here). I scoured the web for existing tutorials and documentation but found little that was relevant to my situation. My first inclination was to write a post-process method – grab the image, mask it and write it back to S3 which proved to be a dead-end (you can see the problem I ran into here – granted it’s possible you could still go that route). After a bit of Googling I ended up using a processor. Here’s what I did (I must confess the “boilerplate” code for the processor was some I found, unfortunately I did not keep the link – if you recognize it please let me know!):
In my model:
has_attached_file :image,
:styles =>{
:main_feature => {:geometry => "1020x470", :processors => [:masker] },
:large => "1020x470",
:event_page => "460x212",
:top_feature => "345x159",
:smallest => "229x131"#,
},
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => ":attachment/:id/:style.:extension",
:url => "/:id/:style/:basename.:extension",
:bucket => "yo-bucket-name"
Note line 3, where I’ve added a processor called “masker”. I created a folder called ‘paperclip_processors’ inside my lib directory and created masker.rb. In that same folder I included the png of my mask (mine is simply called mask.png). I’m using an alpha mask. In masker.rb I placed the following code:
module Paperclip
class Masker < Processor
def initialize file, options = {}, attachment = nil
super
@format = File.extname(@file.path)
@basename = File.basename(@file.path, @format)
end
def make
src = @file
dst = Tempfile.new([@basename, @format])
dst.binmode
begin
parameters = []
parameters << ':source'
parameters << ':mask'
parameters << '-alpha'
parameters << 'on'
parameters << '-compose'
parameters << 'CopyOpacity'
parameters << '-composite'
parameters << ':dest'
parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
mask_path = File.expand_path('lib/paperclip_processors/mask.png')
success = Paperclip.run("convert", parameters, :source => "#{File.expand_path(src.path)}[0]", :mask => "#{mask_path}[0]", :dest => File.expand_path(dst.path))
rescue PaperclipCommandLineError => e
raise PaperclipError, "There was an error during the mask for #{@basename}" if @whiny
end
dst
end
end
end
Lines 18-25 show the arguments we’ll be using to interface with ImageMagick (the documentation for this is found here). Basically we’re inputting the source and mask, turning the alpha flag on, using the compose method with CopyOpacity to copy the opacity of the mask to the final composite (masked) image and finally the destination. Now when an image is added to my model, a new image is sized and created – best of all, it works on Heroku!
















