Overlays

Overview

The overlay feature overlays an image or text string on top of a derivative (output) image. This can be useful for description, branding, attribution, copyright notices, and so on.

With both types of overlays, the position, inset, and output dimensions threshold (below which the overlay won't be drawn) are configurable.


Modes of Operation

The overlay system offers two "strategies," or modes of operation: a basic strategy, where overlay properties are set in the configuration file and applied to all requests; and a script strategy, where the decision of whether to apply an overlay, and what kind, depends on the return value of a delegate method. The overlays.strategy configuration key is used to set the strategy.

BasicStrategy

With BasicStrategy, the overlays.BasicStrategy.* keys in the configuration file are used to set an overlay type (image or string), position, inset, and others. This strategy is easy to configure and is all that is needed when it is intended for the same overlay to be applied to all images.

ScriptStrategy

Perhaps you want to apply an image overlay to some of your images, and to others, you want to apply a string overlay, or no overlay. Perhaps you want to control overlays based on the client's IP address or user agent. Using the overlay() delegate method, this can be accomplished in just a few lines of code.


Positioning

Position and inset are configurable. Supported positions are top left, top center, top right, left center, center, right center, bottom left, bottom center, and bottom right. Image overlays also support repeat and scaled. All positions except repeat support an inset.


Image Overlays

An image overlay must be an 8-bit RGB or RGBA PNG image. It will be blended pixel-for-pixel into the output image.

Overlay images can be located on the filesystem or a web server. Regardless, they are cached in memory after being loaded the first time.

When using multiple image overlays, their filenames must all be different, regardless of where they reside.

Sample overlay image (PNG)

The sample image above is opaque. To achieve the semi-transparency that some of the examples below demonstrate, the alpha channel must be rescaled in an image editor.

BasicStrategy Examples

Image with image overlay applied.
overlays.strategy = BasicStrategy
overlays.BasicStrategy.type = image
overlays.BasicStrategy.image = /path/to/image.png
overlays.BasicStrategy.position = bottom right
Image with repeating image overlay applied.
overlays.strategy = BasicStrategy
overlays.BasicStrategy.type = image
overlays.BasicStrategy.image = /path/to/image.png
overlays.BasicStrategy.position = repeat
Image with scaled image overlay applied.
overlays.strategy = BasicStrategy
overlays.BasicStrategy.type = image
overlays.BasicStrategy.image = /path/to/image.png
overlays.BasicStrategy.position = scaled

ScriptStrategy Examples

class CustomDelegate
  MIN_SIZE_CUTOFF = 300

  def overlay(options = {})
    resulting_size = context['resulting_size']
    return nil if resulting_size['width'] < MIN_SIZE_CUTOFF or
        resulting_size['height'] < MIN_SIZE_CUTOFF
    {
      'image' => '/path/to/overlay.png',
      'position' => 'bottom right',
      'inset' => 5
    }
  end
end

String Overlays

String overlays are dynamically rendered onto an image using Java 2D. The font family, size, weight, color, stroke color, background color and opacity, etc., are configurable.

Line breaks within the string (\n) are respected, enabling multi-line strings. Each line is auto-aligned to the edge of the image according to the overlay position.

Layout

When using BasicStrategy, the lines of the string are applied verbatim and not wrapped or broken in any way. If a string doesn't fit entirely inside the image at its specified font size, the application will try to use the largest font size that fits, down to the configurable minimum allowed size. If none fit, the string won't be drawn.

ScriptStrategy can work the same way, but it also supports a word wrap mode, which can be enabled by returning a value of true for a word_wrap key in the hash returned from overlay(). When word wrap is enabled, the string is broken at word boundaries to fit inside the width of the image. If the resulting string's height exceeds the image's height, it won't be drawn.

To get a list of all available fonts, start the JVM with the -list-fonts command-line argument.

BasicStrategy Examples

Image with string overlay applied.
overlays.strategy = BasicStrategy
overlays.BasicStrategy.type = string
overlays.BasicStrategy.string = McKinley Marina\nMilwaukee, Wisconsin, USA
overlays.BasicStrategy.string.color = white
overlays.BasicStrategy.string.background.color = rgba(0,0,0,0.5)
overlays.BasicStrategy.position = bottom left
overlays.BasicStrategy.inset = 10

ScriptStrategy Examples

This example renders the image identifier onto the image.

class CustomDelegate
  MIN_SIZE_CUTOFF = 300

  def overlay(options = {})
    resulting_size = context['resulting_size']
    return nil if resulting_size['width'] < MIN_SIZE_CUTOFF or
        resulting_size['height'] < MIN_SIZE_CUTOFF
    {
      'string' => context['identifier'],
      'position' => 'bottom left',
      'inset' => 5,
      'font' => 'Helvetica',
      'font_size' => 20,
      'font_min_size' => 18,
      'font_weight' => 1.0,
      'color' => 'white',
      'background_color' => 'rgba(0, 0, 0, 0.6)'
      'glyph_spacing' => 0,
      'stroke_color' => 'black',
      'stroke_width' => 1,
      'word_wrap' => false
    }
  end
end

With Zooming Viewers

Zooming image viewers display a mosaic of cropped image tiles. There are a few techniques that can prevent each tile from receiving an overlay:

  • Set the overlays.BasicStrategy.output_width_threshold and overlays.BasicStrategy.output_height_threshold configuration options to values that are slightly larger than the tile size used by the image viewer. This will disable overlays for images the size of image viewer tiles, and enable them for anything larger. Be aware, though, that the tile size used by the viewer may differ depending on the source image.
  • Have the zooming viewer supply a URI query argument, and check for it in the overlay() delegate method. Of course, a user could figure this out and supply the same argument in other image requests.
  • Using ScriptStrategy, return nil if a crop operation is present in the operation list. Example:
    class CustomDelegate
      def overlay(options = {})
        return nil if context['operations'].find{ |op| op['class'].include?('Crop') }
        # ...
      end
    end