Harlequin RIP SDK
A set of multi-level dispersed screens

The htm8ml example of the modular screening API implements a multi-level screening technique which produces a configurable number of levels, and where the transition between levels is dispersed using a stochastic screen cell. More...

Files

file  htm8ml.h
 HTM Interface to a simple example screening module which implements a set of multi-level dispersed screens.
 
file  htm8ml.c
 A screening module for multi-level dispersed screening.
 

Functions

sw_htm_apihtm8ml_getInstance (void)
 Return the singleton instance of the sw_htm_api object containing details of the example multi-level screening module. More...
 
static HqBool ml8Init (sw_htm_api *implementation, const sw_htm_init_params *params)
 Implementation of sw_htm_api::init(). More...
 
static sw_htm_result ml8HalftoneSelect (sw_htm_instance *instance, const sw_htm_select_info *info, sw_htm_instance **matches)
 Implementation of sw_htm_api::HalftoneSelect(). More...
 
static void ml8HalftoneRelease (sw_htm_instance *instance)
 Implementation of sw_htm_api::HalftoneRelease(). More...
 
static sw_htm_result ml8RenderInit (sw_htm_api *implementation, const sw_htm_render_info *render_info)
 Implementation of sw_htm_api::RenderInitiation(). More...
 
static HqBool ml8DoHalftone (sw_htm_instance *instance, const sw_htm_dohalftone_request *request)
 Implementation of sw_htm_api::DoHalftone(). More...
 
static void ml8AbortHalftone (sw_htm_instance *instance, const sw_htm_dohalftone_request *request)
 Implementation of sw_htm_api::AbortHalftone(). More...
 

Detailed Description

The htm8ml example of the modular screening API implements a multi-level screening technique which produces a configurable number of levels, and where the transition between levels is dispersed using a stochastic screen cell.

Based on the principle that it can theoretically be configured to produce any desired number of different output pixel values ('levels'), between 2 and 255, it currently supports destination raster depths of 1, 2, 4 and 8 bits per pixel (although not all RIP implementations support screened output to all those bit depths).

The destination bit depth is determined by the RIP, of course, from the /ValuesPerComponent page device key, whereas the desired number of 'levels' is configured via the module's halftone dictionary using the /Levels key.

It is the responsibility of the creator of the relevant halftone dictionary to ensure that the number of 'levels' being produced is appropriate for the number of bits per pixel in the output raster.

Beware, though, that although the desired number of levels will be distributed as smoothly as possible across the output bit depth, some combinations will exhibit distinct non-linearity across the values. For example, you can't really distribute 10 levels evenly across the 16 possible pixel values of 4-bit per pixel output.

It's relatively easy, though, to use code in the RIP back end to re-distribute the pixel values generated by the halftone module as you see fit, for example using a simple look up table.

Different final device bit depths could, of course, be obtained by configuring the RIP to use a larger number of bits per pixel, and then using a similar look-up technique in the RIP back end to re-map the pixel values to ones suitable for fewer bits. For example producing 8 levels with a 4-bit destination raster converted to 3 bits per pixel in the back end before transmission to the device.

(However, note that RIPs which do not support 8-bit halftoned destination raster are consequently limited to a maximum of 4-bits per pixel final output depth.)

Internally, the module uses a form of dispersed stochastic screen tile, similar to Harlequin Dispersed Screening, to distribute the individual pixel values across the area being rendered.

It has four such tiles, thus allowing a basic set of four separate 'separations' to be screened.

Which tile to use is determined by the /Screen key in the halftone dictionary - a number between 0 and 3.

Furthermore the tiles are constructed in such a way that they can also be processed in the 'inverse' order enabling different colorants to 'share' the same screen without overlapping as such. This 'inverse' option is very useful, for example, when utilising more than one shade of ink, such as Light Cyan and Dark Cyan - the light ink can use one tile while the dark screen uses its inverse.

The use of the inverse screen is triggered by setting the /Inverse key of the halftone dictionary to true.

An example of a valid halftone dictionary for this module might be:

<<
  /HalftoneType 100
  /HalftoneModule /htm8ml
  /Screen 0
  /Levels 4
>>

which will generate 4 levels (suitable for 2 bits per pixel output) using internal tile number 0 to distribute the pixel values.

A pair of colorants might be declared sharing a tile like this:

/LightCyan <<
  /HalftoneType 100
  /HalftoneModule /htm8ml
  /Screen 0
  /Levels 4
>>

/DarkCyan <<
  /HalftoneType 100
  /HalftoneModule /htm8ml
  /Screen 0
  /Levels 4
  /Inverse true
>>

You can see some complete examples of declaring halftone dictionaries for this module in the RIP's data directory, in SW/Sys/ExtraStart/HtmEg.

To demonstrate the htm8ml screen in action, you can use the configuration in SW/TestConfig/HTM/CMYKCompHtm8mlSeps300dpi3bit with the "clrip" application. This will produce 4-bit-per-pixel output rasters screened using just 8 different levels (values) for each pixel.

This example module is registered by calling SwRegisterHTM() with the singleton API instance returned by htm8ml_getInstance(). The module implements the modular screening API methods ml8Init(), ml8HalftoneSelect(), ml8HalftoneRelease(), ml8RenderInit(), ml8DoHalftone(), and ml8AbortHalftone().

Function Documentation

◆ htm8ml_getInstance()

sw_htm_api* htm8ml_getInstance ( void  )

Return the singleton instance of the sw_htm_api object containing details of the example multi-level screening module.

Returns
A single instance of the screening module.
See also
SwRegisterHTM()

If compiled normally, the "clrip" application layer registers this module during RIP startup. Halftone module examples may be excluded from "clrip" by building with NO_HTMEG defined.

◆ ml8AbortHalftone()

static void ml8AbortHalftone ( sw_htm_instance instance,
const sw_htm_dohalftone_request request 
)
static

Implementation of sw_htm_api::AbortHalftone().

The RIP calls ml8AbortHalftone() when it wants the module to stop rendering an outstanding request issued by ml8DoHalftone() with the same arguments.

Note
This function is not implemented yet.

The module must stop writing to its output buffers and then invoke sw_htm_dohalftone_request::DoneHalftone() before returning (it is permissible to invoke it from another thread). Because the calls can't be synchronized, it may happen that the module has already called sw_htm_dohalftone_request::DoneHalftone() between the RIP deciding to abort and actually invoking AbortHalftone(). This is acceptable, but the module must be so constructed that any such sw_htm_dohalftone_request::DoneHalftone() has, in fact, returned before AbortHalftone() returns. The RIP will check if the request has, in fact, terminated, and it is an error if it hasn't.

Parameters
[in]instancePointer to a halftone instance selected by HalftoneSelect().
[in]requestPointer to a structure holding details of the RIP's halftone request.

◆ ml8DoHalftone()

static HqBool ml8DoHalftone ( sw_htm_instance instance,
const sw_htm_dohalftone_request request 
)
static

Implementation of sw_htm_api::DoHalftone().

This is the most important function in the module.

The RIP calls ml8DoHalftone() when it wants the module to render a halftone instance into a channel within the RIP's band buffers.

In the case of this example, the halftoning is so simple that even if the RIP were capable of asynchronous calls, we would still opt to complete ours synchronously.

Parameters
[in]instancePointer to a halftone instance selected by HalftoneSelect().
[in]requestPointer to a structure holding details of the RIP's halftone request.
Return values
FALSEThe module is unable to accept the request. In this case, the module must not touch any arguments passed to it, and the RIP will deem this an unrecoverable failure.
TRUEThe module accepts the request and will call sw_htm_dohalftone_request::DoneHalftone() when done. The success or failure of the overall request is indicated by the result parameter of sw_htm_dohalftone_request::DoneHalftone().

The module must not block on something that depends on another DoHalftone() invocation being made. Even if sw_htm_render_info::max_render_threads > 1, there's no guarantee that another invocation can be made concurrently.

The module must call sw_htm_dohalftone_request::DoneHalftone() using exactly the same request pointer passed in to it.

◆ ml8HalftoneRelease()

static void ml8HalftoneRelease ( sw_htm_instance instance)
static

Implementation of sw_htm_api::HalftoneRelease().

ml8HalftoneRelease() will be called once for each previously successful ml8HalftoneSelect() when the RIP no longer needs the halftone instance.

In this example, our function doneWithSolution() handles everything that ml8HalftoneRelease() requires, so all that's needed is to call that.

HalftoneRelease() will be called once for each previously successful HalftoneSelect(), with the instance pointer that is not required. The implementation should clean up any resources associated with the instance.

Parameters
[in]instanceThe halftone instance value originally constructed by HalftoneSelect().

◆ ml8HalftoneSelect()

static sw_htm_result ml8HalftoneSelect ( sw_htm_instance instance,
const sw_htm_select_info info,
sw_htm_instance **  matches 
)
static

Implementation of sw_htm_api::HalftoneSelect().

ml8HalftoneSelect() is called when the page description language (PDL) does the equivalent of a PostScript sethalftone.

The RIP calls us individually for each halftone needed, so in the case of multi-colorants, such as a PostScript type 5 halftone dictionary, the RIP will call us for each colorant sub-dictionary therein.

We only have to worry, therefore, about a single halftone instance in any one call.

The key names this module understands from the halftone dictionary are:

Levels - Integer - Mandatory.
The number of tone levels, or shades of gray. Must be in the range 2 to 256. At the moment, the requested number of levels are spread evenly over the range 0 to 255, so three levels would result in 8-bit pixel values of 0, 127 and 255, while six levels would produce 0, 51, 102, 153, 204 and 255. This means that, for example, an output device requiring three-bit pixels having values in the range 0 to 5 cannot simply use the most-significant three bits of the eight-bit pixel - the device driver would currently need to re-map the pixel values appropriately.
Screen - Integer - Mandatory.
Which halftone cell to use. Must be in the range 0 to 3.
Inverse - Boolean - Optional.
Whether to light pixels in inverse order. Default is false.

This example is relatively simple, so after validating our parameters, we call our prepareSolution() function to calculate the distribution solution for the requested number of levels and initialise one of our ml8_htinstance structures for the combination of levels, screen and inverse-setting.

Parameters
[in,out]instanceAn incomplete instance of the sw_htm_instance structure to complete. The RIP will allocate a structure of the size presented in the implementation's sw_htm_api::info.instance_size field, fill in the implementation and callback API instance pointers, and then pass it to this routine. The HalftoneSelect() method is expected to fill in the remaining fields. The implementation may sub-class the instance to allocate private workspace by initialising the sw_htm_api::info.instance_size larger than the size of the sw_htm_instance structure, then downcasting the instance pointer in method calls.
[in]infoHalftone selection information from the RIP. This contains things like the halftone phase and a pointer to an information structure describing the destination raster and colorants etc. It also contains a data access API, a datum representing the colorant name for which this halftone is being selected and a datum representing the halftone dictionary. Any data accessed through this pointer MUST be copied if the halftone instance wished to refer to it after the HalftoneSelect() method has returned.
[out]matchesIf the halftone module currently has an instance selected that maps onto the same screen, it should store the existing instance in this pointer and return SW_HTM_SUCCESS. In this case, the instance under construction will be destroyed immediately, and there will be no call to HalftoneRelease(). This will ensure that equivalent screen definitions share the same DoHalftone() calls, and will improve rendering efficiency. It is an error to store the instance under construction through this pointer.
Returns
SW_HTM_SUCCESS, or one of the sw_htm_error error constants.

◆ ml8Init()

static HqBool ml8Init ( sw_htm_api implementation,
const sw_htm_init_params params 
)
static

Implementation of sw_htm_api::init().

ml8Init() is called once when the RIP boots up, before all other methods in this interface. It is used here to initialize implementation class-specific data.

This method may be used to initialise any implementation-specific data. This method is optional.

Parameters
implementationThe registered modular halftone implementation to be initialised.
[in]paramsA structure containing callback APIs and parameters valid for the lifetime of the module. Any parameters that the implementation needs access to should be copied out of this structure into private storage for the registered implementation.
Return values
TRUESuccess, indicating that the implementation is fully initialised.
FALSEFailure to initialise the implementation. If this is returned, the implementation will not be finalised.

◆ ml8RenderInit()

static sw_htm_result ml8RenderInit ( sw_htm_api implementation,
const sw_htm_render_info render_info 
)
static

Implementation of sw_htm_api::RenderInitiation().

The RIP calls ml8RenderInit() when it prepares to render a page.

This primitive example won't benefit from more memory, so we don't really have much to do in this function. We just check that the source (contone) and destination (halftone) raster bit depths are appropriate, and let the RIP know our rendering needs.

RenderInitiation() is a class function. It will be called once for each halftone module that may have halftones used on a page.

In the case of color-separated output, RenderInitiation() takes place once, before the RIP renders all the separations of a page.

Note that when the RenderInitiation() is for a partial paint, very little memory will be available, so any mandatory buffers should be preallocated during HalftoneSelect().

This method is optional.

Note
In preparation for a partial paint, the RIP may choose to reduce the band size used for rendering. This may involve several repeated calls to RenderInitiation() until such time as the RIP reaches a good compromise. When two consecutive RenderInitiation() calls occur, without an intervening RenderCompletion(), the later RenderInitiation() supersedes the earlier one. In such cases RenderCompletion() will only be called once, because the superseded RenderInitiation() calls are deemed never to have taken place.
Parameters
[in]implementationA pointer to the implementation registered with the RIP.
[in]render_infoPointer to a structure containing general information about the upcoming render as a whole.
Returns
SW_HTM_SUCCESS, or one of the sw_htm_error error constants.