CAPS Universe documentation  1.0.4
All you need to know to be successful
Data Structures | Macros | Typedefs | Functions | Variables
halftone-processing.c File Reference

Collection of halftone routines. More...

Data Structures

union  ht_dot_vector
 Union to convert eight grey scale dots into monochrome. More...
 
struct  monochrome_converter
 Halftone processing buffer. More...
 
struct  drv_halftone_converter
 Monochrome halftone processing. More...
 

Macros

#define WHITE_DOT   255
 

Typedefs

typedef unsigned char htpix8
 Vector to define eight dots in the halftone buffer.
 

Functions

struct drv_halftone_converterht_get (enum halftone_type t, unsigned cnt)
 
void ht_put (struct drv_halftone_converter *ht)
 
static signed int ht_quantize_grey_dot (const struct drv_halftone_converter *ht, signed int v)
 
static signed int ht_quant_error_calc (signed int orig, signed int final)
 
static unsigned char diffuse (signed int raw, signed int err, signed int numerator, signed int denominator)
 
static void lp_average (struct drv_halftone_converter *ht)
 
static void lp_ordered (struct drv_halftone_converter *ht)
 
static void lp_flsdiffusion_lr (struct drv_halftone_converter *ht)
 
static void lp_flsdiffusion_rl (struct drv_halftone_converter *ht)
 
static void lp_jjndiffusion_lr (struct drv_halftone_converter *ht)
 
static void lp_jjndiffusion_rl (struct drv_halftone_converter *ht)
 
static void dither_line (struct drv_halftone_converter *ht)
 
int ht_grey_line_add (struct drv_halftone_converter *ht, unsigned cnt, const struct caps_dot_grey raw[cnt])
 
int ht_emptyline_add (struct drv_halftone_converter *ht)
 
int ht_drain (struct drv_halftone_converter *ht)
 
unsigned ht_monochrome_line_get (struct drv_halftone_converter *ht, enum caps_colour_format cf, unsigned cnt, struct caps_dot_monochrome ln[cnt])
 

Variables

static const signed char bayer_8x8_pattern [8][8]
 8x8 Bayer ordered dithering pattern
 

Detailed Description

Author
Jürgen Borleis
Warning
Use as experimental

Halftone routines for monochrome rasters.

Halftoning is done according to halftone_type and processing is done on unsigned char values, one per dot.

After processing a single dot in the top monochrome_converter::sliding_line buffer has a '0' for a black dot or a '255' (WHITE_DOT) for no dot (e.g. keep medium's colour).

Converting into monochrome data is done via ht_monochrome_line_get().

According to a famous paper about halftone algorithms:

‍It is critical with all of these algorithms that when error values are added to neighboring pixels, the resultant summed values must be truncated to fit- within the limits of hardware. Otherwise, an area of very intense color may cause streaks into an adjacent area of less intense color.

This truncation is known as "clipping," and is analogous to the audio world's concept of the same name. As in the case of an audio amplifier, clipping adds undesired noise to the data. Unlike the audio world, however, the visual clipping performed in error-diffusion halftoning is acceptable since it is not nearly so offensive as the color streaking that would occur otherwise. It is mainly for this reason that the larger filters work better – they split the errors up more finely and produce less clipping noise.

With all of these filters, it is also important to ensure that the sum of the distributed error values is equal to the original error value. This is most easily accomplished by subtracting each fraction, as it is calculated, from the whole error value, and using the final remainder as the last fraction.

Cited from "DHALF.txt".

Note
The halftone algorithms here don't ensure that the sum of the distributed error values is equal to the original error value. They only keep them small.
Todo:
The used algorithms are for square dots. If horizontal and vertical resolutions are equal, this is correct. But not, if both resolutions differ (1200 DPI x 600 DPI for example). Dealing with this use case is still a TODO.

Macro Definition Documentation

◆ WHITE_DOT

#define WHITE_DOT   255

Defines the signed short value for a white dot (e.g. no dot)

Typedef Documentation

◆ htpix8

typedef unsigned char htpix8

Function Documentation

◆ ht_get()

struct drv_halftone_converter * ht_get ( enum halftone_type  t,
unsigned  cnt 
)

Create and configure a halftone converter

Parameters
[in]tType of the halftone algorithm to configure
[in]cntPixel count in each line
Returns
A halftone converter configuration

Depending on the selected halftone algorithm, memory to process the input grey scale lines later on will be allocated.

Postcondition
Does not return in case of an unimplemented or unknown halftone method.

◆ ht_put()

void ht_put ( struct drv_halftone_converter ht)

Destroy a halftone converter configuration and free resources

Parameters
[in]htThe halftone converter configuration

◆ ht_quantize_grey_dot()

static signed int ht_quantize_grey_dot ( const struct drv_halftone_converter ht,
signed int  v 
)
static

Return a quantized value from a grey scale dot

Parameters
[in]htThe halftone converter configuration
[in]vThe intended grey value for a dot
Return values
0for a black dot
255For a white dot, e.g. WHITE_DOT
Note
Currently the threshold is at 128. Below is black, above white (WHITE_DOT).

◆ ht_quant_error_calc()

static signed int ht_quant_error_calc ( signed int  orig,
signed int  final 
)
static

Calculate the quantization error for a dot value

Parameters
[in]origThe original value (0 … 255)
[in]finalThe quantized value (0 or 255)
Return values
PositiveThe final value was too dark compared to orig
NegativeThe final value was too light compared to orig

Returns: orig - final

◆ diffuse()

static unsigned char diffuse ( signed int  raw,
signed int  err,
signed int  numerator,
signed int  denominator 
)
static

Diffuse a quantization error according to a fraction number

Parameters
[in]rawThe raw value (0 … 255)
[in]errThe quantization error (-128 … 0 … +127)
[in]numeratorThe numerator of the fraction
[in]denominatorThe denominator of the fraction
Returns
The new value (including the weighted quantization error and clipping)

Calculation done here is: result = raw + ((err * numerator) / denominator)

The multiplication is done first, to keep the error as small as possible.

And the result is clipped to be in the range of 0 … 255

◆ lp_average()

static void lp_average ( struct drv_halftone_converter ht)
static

Process a line according to the average method

Parameters
[in]htThe halftone converter configuration

For this method, there is no left/right serpentine processing.

Todo:
Can make use of vector processing as well.

◆ lp_ordered()

static void lp_ordered ( struct drv_halftone_converter ht)
static

Process a line according to the Bayer ordered dither.

Parameters
[in]htThe halftone converter configuration

For this method, there is no left/right serpentine processing.

◆ lp_flsdiffusion_lr()

static void lp_flsdiffusion_lr ( struct drv_halftone_converter ht)
static

Process a line according to the Floyd and Steinberg method, left to right processing direction

Parameters
[in]htThe halftone converter configuration

Processing description:

------------------> processing direction
|        *   7/16 | <-- cnf->sliding_line[0]
| 3/16  5/16 1/16 | <-- cnf->sliding_line[1]
Note
Left-to-right calculation variant of the serpentine processing.

◆ lp_flsdiffusion_rl()

static void lp_flsdiffusion_rl ( struct drv_halftone_converter ht)
static

Process a line according to the Floyd and Steinberg method, right to left processing direction

Parameters
[in]htThe halftone converter configuration

Processing description:

<------------------ processing direction
| 7/16   *        | <-- sliding_line[0]
| 1/16  5/16 3/16 | <-- sliding_line[1]
Note
Right-to-left calculation variant of the serpentine processing.

◆ lp_jjndiffusion_lr()

static void lp_jjndiffusion_lr ( struct drv_halftone_converter ht)
static

Process a line according to the Jarvis, Judice, and Ninke method, left to right processing direction

Parameters
[in]htThe halftone converter configuration

Processing description:

-------------------------------> processing direction
|              *    7/48  5/48 | <-- cnf->sliding_line[0]
| 3/48  5/48  7/48  5/48  3/48 | <-- cnf->sliding_line[1]
| 1/48  3/48  5/48  3/48  1/48 | <-- cnf->sliding_line[2]
Note
Left-to-right calculation variant of the serpentine processing.
Todo:
Distribute the skipped error at both borders.

◆ lp_jjndiffusion_rl()

static void lp_jjndiffusion_rl ( struct drv_halftone_converter ht)
static

Process a line according to the Jarvis, Judice, and Ninke method, right to left processing direction

Parameters
[in]htThe halftone converter configuration

Processing description:

<--------------------------------- processing direction
| 5/48  7/48   *               | <-- cnf->sliding_line[0]
| 3/48  5/48  7/48  5/48  3/48 | <-- cnf->sliding_line[1]
| 1/48  3/48  5/48  3/48  1/48 | <-- cnf->sliding_line[2]
Note
Right-to-left calculation variant of the serpentine processing.
Todo:
Distribute the skipped error at both borders.

◆ dither_line()

static void dither_line ( struct drv_halftone_converter ht)
static

Run the halftone algorithm on buffer's top line

Parameters
[in]htThe halftone converter configuration

◆ ht_grey_line_add()

int ht_grey_line_add ( struct drv_halftone_converter ht,
unsigned  cnt,
const struct caps_dot_grey  raw[cnt] 
)

Add a line with grey scale content to the halftone algorithm's processing buffer

Parameters
[in]htThe halftone converter configuration
[in]cntCount of grey scale dots in raw (can be '0')
[in]rawA raw line of grey scale dots (caps_dot_grey), can be NULL if cnt is '0' as well
Return values
-EAGAINAdd another line first to pre-load the halftone algorithm
0A dithered line awaits its read via ht_monochrome_line_get()

If the call returns with a 0, a line can be read with ht_monochrome_line_get()

Note
If cnt is '0', an empty line (e.g. all dot are white) is added to the halftone converter.

Silently:

  • longer lines are skipped at their right end.
  • shorter lines are filled at their right end.

◆ ht_emptyline_add()

int ht_emptyline_add ( struct drv_halftone_converter ht)

Add an empty line (e.g. all dots are white) to the halftone algorithm's processing buffer

Parameters
[in]htThe halftone converter configuration
Return values
-EAGAINAdd another line first to pre-load the halftone algorithm
0A dithered line awaits its read via ht_monochrome_line_get()
Note
This is just an optimization.

When the call returns with a 0, a line can be read with ht_monochrome_line_get()

◆ ht_drain()

int ht_drain ( struct drv_halftone_converter ht)

Drain the halftone algorithm's processing buffer by padding an empty line

Parameters
[in]htThe halftone converter configuration
Return values
0Finished
-EAGAINRead a dithered line via ht_monochrome_line_get() and call again

Let process the halftone algorithm the next line with a new empty line. Intended to be called at the bottom of the input raster to finish the halftone algorithm. Some algorithm needs more than one line in its buffer to process the current line. Call this routine as often as it returns -EAGAIN. After each call (and -EAGAIN) you can read the next processed line. If it returns 0, you are done.

If the call returns with a -EAGAIN, another line can be read with ht_monochrome_line_get()

Todo:
Better '0' for "read a dithered line…" and -ENODATA for "finished"?

◆ ht_monochrome_line_get()

unsigned ht_monochrome_line_get ( struct drv_halftone_converter ht,
enum caps_colour_format  cf,
unsigned  cnt,
struct caps_dot_monochrome  ln[cnt] 
)

Extract the monochrome data line from the halftone processing buffer

Parameters
[in]htThe halftone converter configuration
[in]cfThe target monochrome colour format (one of CAPS_CF_MONOCHROME0 or CAPS_CF_MONOCHROME1)
[in]cntThe amount of bytes in ln
[out]lnThe buffer for the monochrome data
Returns
Amount of dots in ln (not bytes!)

Converts the top line from the halftone converter into a line of monochrome bits in ln. In order to make this work, this top line must already be processed, e.g. the last call to ht_grey_line_add() and ht_emptyline_add() returned with '0' or a call to ht_drain() returned with -EAGAIN.

If the count of grey scale dots in the halftone converter's line isn't a multiple of eight, the remaining resulting monochrome bits are white, e.g. set to "no dot".

Note
There is an optimized variant (vectorized) of this function, tested on ARM32, ARM64 and X86-64.
And a non-optimized variant for general purpose.
Precondition
ln must be equal or greater than DOTS_TO_BYTES(drv_halftone_converter::cnt) bytes
An already halftoned buffer in monochrome_converter::sliding_line[0]

Variable Documentation

◆ bayer_8x8_pattern

const signed char bayer_8x8_pattern[8][8]
static
Initial value:
= {
{ 0, 32, 8, 40, 2, 34, 10, 42, },
{ 48, 16, 56, 24, 50, 18, 58, 26, },
{ 12, 44, 4, 36, 14, 46, 6, 38, },
{ 60, 28, 52, 20, 62, 30, 54, 22, },
{ 3, 35, 11, 43, 1, 33, 9, 41, },
{ 51, 19, 59, 27, 49, 17, 57, 25, },
{ 15, 47, 7, 39, 13, 45, 5, 37, },
{ 63, 31, 55, 23, 61, 29, 53, 21, },
}

Used by the lp_ordered() halftone funtion.