Taka’s blog

A software engineer's blog who works at a start-up in London

Make Images Watermarked with RMagick (+ CarrierWave)

Summary

This post explains how to cover images with an array of a watermark.
Plus, I show an example how to use it with CarrierWave.

What is Watermark?

Watermark is an image which often covers sample images.
I made a sample of fotowa*1 logo:

f:id:necojackarc:20161014223629p:plain

You almost can't see it as it's really transparent. Let's say combine it with this picture, the image would be like this:

f:id:necojackarc:20161205234711j:plain

This is also the final product of this post.

Make an array of a watermark by mosaic of RMagick

It takes a lot of time to combine images multiply when you make images covered with an array of a watermark. I've tried it once, but it didn't work in realistic time.

So, make an array of a watermark at first and combine it with target images. Then, it really speeds up the process.

mosaic is, as the name shows, a feature which makes mosaic by arraying a lot of images.

image = Magick::Image.read("target.png").first

mark = Magick::Image.read("watermark.png").first
mark.background_color = "none" # If background color is specified, alpha channel becomes that color after combining images

tile = Magick::ImageList.new
page = Magick::Rectangle.new(0, 0, 0, 0)

# Array watermarks to make it appropriate size
(image.columns / mark.columns.to_f).ceil.times do |x|
  (image.rows / mark.rows.to_f).ceil.times do |y|
    tile << mark.dup
    page.x = x * tile.columns
    page.y = y * tile.rows
    tile.page = page
  end
end

One point is the background color of a watermark. For example, the background color of the watermark I already showed is "white".

If you use a watermark whose background color is specified, images will be covered with the background color like this:

f:id:necojackarc:20161206001409j:plain

Use it with CarrierWave

You can easily manipulate images with CarrierWave and RMagick, like process resize_to_fit: [2000, 1000].

As it's okay to use any methods you define for process, so define a method and use it in version:

class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

  version :watermarked do
    process :watermark
  end

  def watermark
    mark = Magick::Image.read(Rails.root.join("lib", "assets", "watermark.png")).first
    mark.background_color = "none"

    manipulate! do |image|
      tile = Magick::ImageList.new
      page = Magick::Rectangle.new(0, 0, 0, 0)

      (image.columns / mark.columns.to_f).ceil.times do |x|
        (image.rows / mark.rows.to_f).ceil.times do |y|
          tile << mark.dup
          page.x = x * tile.columns
          page.y = y * tile.rows
          tile.page = page
        end
      end

      image.composite(tile.mosaic, 0, 0, Magick::OverCompositeOp)
    end
  end
end

Finally, this uploader makes and stores watermarked images on uploading images!

References

The original post was published on Jul. 8, 2016, by me in Japanese.

*1:Web service that I'm involved in as Software Architect / Lead Software Engineer