ClapTrap - Very simple bitmap trapping

The problem to be solved.

During printing processes one common problem is that of misregistration between inks. Colours are not typically printed all at once, and successive passes can result in slight misalignments. This can have various different causes, depending on the exact process in use (paper movement, expansion due to ink wetness, misaligned nozzles etc).

This can have particularly bad effects when two regions, filled in different dark inks, abut one another. If one of the regions is offset from the other, a 'blank' region may show through in the newly created gap between them. The gap can be much lighter in appearance, and can be very noticeable.

The process of trapping is that of modifying the image to be printed to be more resistant to such errors.

Trapping can be done manually at the design phase, or can be done mechanistically as part of the rendering pipeline.

Some methods work by operating on the vector objects themselves prior to rasterisation, whereas others (such as this one) operate at the other end of the pipeline by modifying the pixels of the rendered image before they are printed.

The code

ClapTrap is a very simple set of routines for bitmap trapping.

It is designed to work on simple 8 bit chunky contone bitmap data (the results of a standard render), and to post process that data to improve the appearance of the image when printed. It could just as easily work on planar data.

Currently it has not been integrated with ghostscript, but that could fairly easily be done.

git repo

The source can be found on casper as /home/robin/private-repos/claptrap.git


The example utility reads a .pam (such as is produced by the pamcmyk32 device) and postprocesses it to produce another pam.

   gswin32c.exe -o out.pam -sDEVICE=pamcmyk32 -r100 examples/tiger.eps
   trappam out.pam trapped.pam

Library Usage

To init the library we call:

ClapTrap *ClapTrap_Init(int              width,
                        int              height,
                        int              num_comps,
                        const int       *comp_order,
                        int              max_x_offset,
                        int              max_y_offset,
                        ClapTrap_LineFn *get_line,
                        void            *get_line_arg)

This creates some program state, and returns it as a pointer to an opaque ClapTrap structure.

width is the width of the image data (in pixels). height is the width of the image data (in pixels). num_comps is the number of components in the image data. comp_order is the order in which we should process the components image data (from darkest to lightest) max_x_offset is the maximum x offset (in pixels) that any plane can be offset for as a result of printing errors. max_y_offset is the maximum y offset (in pixels) that any plane can be offset for as a result of printing errors. get_line is a function that ClapTrap will call to request a line of data. get_line_arg is an opaque function that will be passed through to get_line.

The user can then request a line of trapped data by simply calling:

void ClapTrap_GetLine(ClapTrap      * restrict ct,
                      unsigned char * restrict buffer)

Once height lines have been requested, ClapTrap can be closed down by calling:

void ClapTrap_Fin(ClapTrap *trapper)


The operation of the library is based on the key observation that a darker ink will (largely) obscure (or 'shadow') lighter inks at the same position.

This means that we can 'bleed' lighter inks into neighbouring darker areas without substantially affecting the results.

We operate by forming a 'process map' of pixels in the image. A pixel being set in the process map indicates that there is a danger that this pixel might be 'exposed' by the misregistration of a darker plane and that we should perform processing to alleviate the situation. The value in the map indicates the 'strength' of the shadowing that would have occurred from there. Initially therefore the map is empty (full of 0's).

We run through the inks from darkest to lightest, and we run through each pixel in each plane in turn.

For each pixel, of value V, we search the region around it, to find the minimum and maximum ink values, MIN_V and MAX_V respectively.

If the process map value for this pixel, P, is non-zero then we know that our pixel is in danger of being exposed by the misregistration of a darker ink. This can only cause a 'dropout' if there are are other pixels in the region that would have been darker. So we look at MAX_V, and compare it to V. If it differs noticeably (see trap_here) then we modify the output value at this point. We bump the ink level at the current point up to the minimum of MAX_V and P. This ensures that the misregistration of a small amount of, say, black will not cause vast difference in the amount of, say, magenta.

If the V is noticeably higher than the MIN_V, then there is a danger of a misregistration of this plane causing a 'drop out'. i.e. pixels of other (lighter) inks that would have been shadowed by this pixel might show through. See shadow_here in the code for the exact condition used. We mark such pixels in the process map (by storing P into the process_map).

-- Robin Watts - 2016-01-26


Edit | Attach | Watch | Print version | History: r2 < r1 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r2 - 2016-01-28 - RobinWatts
This site is powered by the TWiki collaboration platform Powered by PerlCopyright 2014 Artifex Software Inc