CAPS Universe documentation  1.0.4
All you need to know to be successful
Data Structures | Macros | Functions
ql-colour.c File Reference

Creating the printer's wire data for bi-coloured print. More...

Data Structures

struct  qlhsv
 Coloured dot for bi-colour printing in HSV. More...
 
struct  colour_line_wire_data
 Printer command (one part of it) to print one bi-coloured line. More...
 
struct  colour_line_wrapper
 Wrapper to define the command for one colour line of printer data. More...
 

Macros

#define HIGH_ENERGY   0x01
 
#define LOW_ENERGY   0x02
 

Functions

static void overlapping_dots_remove (size_t cnt, uint8_t black_block[cnt], const uint8_t red_block[cnt])
 
static void colour_line_encode (struct colour_line_wrapper *cmd, size_t input_cnt, const conv_type inputb[input_cnt], const conv_type inputr[input_cnt])
 
static struct colour_line_wrappercolour_command_get (size_t bytes_per_line)
 
static void colour_command_put (void *cmd)
 
static int ql_colour_line_process (struct ql8_drv *t1, struct halftone_converter *cnv_black, struct halftone_converter *cnv_red)
 
static double fraction (double no)
 
static void rgb2hsv (struct qlhsv *hsv, const struct caps_dot_rgb *rgb_pixel)
 
static void red_dot_mark (signed int val, conv_type *black_pixel, conv_type *red_pixel)
 
static void black_dot_mark (signed int val, conv_type *black_pixel, conv_type *red_pixel)
 
static signed int to_grey (const struct caps_dot_rgb *pixel)
 
static signed int is_red (struct ql8_drv *t1, const struct caps_dot_rgb *pixel)
 
static int ql_next_colour_line_read (struct ql8_drv *t1, struct halftone_converter *cnv_black, struct halftone_converter *cnv_red)
 
int ql_colour_page_print (struct ql8_drv *t1)
 

Detailed Description

Author
Jürgen Borleis

The QL8xx familiy of printers is capable of printing red and black dots on a special medium (e.g. DK2251).

"Colour" here means: black and red on white labels.

In this mode every line must be sent twice. First for low energie to print red dots and second for high energie to print black dots.

The result isn't perfect, since low energy creates red dots, the high energy black dots all have red shadows around them (because the high energy cools down beside the main dot area, the medium tends to red colour). Refer bi_colour_limitation for details.

Macro Definition Documentation

◆ HIGH_ENERGY

#define HIGH_ENERGY   0x01

Value to force a black dot. Used in the printer's command to print the black dots, refer colour_line_wire_data for details

◆ LOW_ENERGY

#define LOW_ENERGY   0x02

Value to force a red dot. Used in the printer's command to print the red dots, refer colour_line_wire_data for details

Function Documentation

◆ overlapping_dots_remove()

static void overlapping_dots_remove ( size_t  cnt,
uint8_t  black_block[cnt],
const uint8_t  red_block[cnt] 
)
static

Remove dots in the black_block, if they are already set in the red_block

Parameters
[in]cntElement count in black_block and red_block
[in,out]black_blockMonochrome data for black dots
[in]red_blockMonochrome data for red dots

We should not send the command for low energy and high energy at the same dot position. So, remove the black dots (high energy) in favour of the red dots (low energy) if they overlap.

Precondition
Both blocks must have the same data size

◆ colour_line_encode()

static void colour_line_encode ( struct colour_line_wrapper cmd,
size_t  input_cnt,
const conv_type  inputb[input_cnt],
const conv_type  inputr[input_cnt] 
)
static

Create the command to print one dual colour line

Parameters
[out]cmdCommand data structure where to store the line data
[in]input_cntCount of signed shorts in inputb and inputr (max. 2040 elements, usual 720 elements)
[in]inputbBlack data to convert
[in]inputrRed data to convert

The printer wire colour data format is:

  | command | <bytes_per_line> | command | <bytes_per_line> |

e.g. according to the QL800 datasheet 186 bytes over all (two times 90 bytes per line each, plus overhead).

Precondition
Both input blocks must have the same data size

◆ colour_command_get()

static struct colour_line_wrapper * colour_command_get ( size_t  bytes_per_line)
static

Allocate the buffer for the colour print command

Parameters
[in]bytes_per_lineExpected bytes per line to be sent to the printer
Returns
colour_line_wrapper structure

◆ colour_command_put()

static void colour_command_put ( void *  cmd)
static

Free the buffer for the colour print command

Parameters
[in]cmdThe command buffer to be freed

◆ ql_colour_line_process()

static int ql_colour_line_process ( struct ql8_drv t1,
struct halftone_converter cnv_black,
struct halftone_converter cnv_red 
)
static

Process one line and send it to the printer

Parameters
[in]t1Full job description
[in]cnv_blackBlack data half-tone converter
[in]cnv_redRed data half-tone converter
Return values
0On success
Precondition
The half tone converters must already be filled with two lines
One of the three available half-tone algorithms must already be selected

◆ fraction()

static double fraction ( double  no)
static

Return the fractional part of a floating point

Parameters
[in]noThe full number
Returns
the fractional part of no
Todo:
can lrint() be used here instead?

◆ rgb2hsv()

static void rgb2hsv ( struct qlhsv hsv,
const struct caps_dot_rgb rgb_pixel 
)
static

Convert one pixel from RGB colour space to HSV colour space

Parameters
[out]hsvThe target HSV colour space
[in]rgb_pixelThe RGB pixel to convert
  • all three components equal to '0' will print a black dot.
  • all three components equal to '0xff' will print no dot.
Note
If the colour is on the vertical axis (e.g. a shade of grey), the qlhsv::hue component is returned as a negative number to mark it as invalid/undefined/do-not-use. But the qlhsv::value component is still valid in this case.

https://github.com/iamh2o/rgbw_colorspace_converter/
https://www.neltnerlabs.com/saikoled/how-to-convert-from-hsi-to-rgb-white

◆ red_dot_mark()

static void red_dot_mark ( signed int  val,
conv_type black_pixel,
conv_type red_pixel 
)
static

Mark a red dot value into both line bufffers

Parameters
[in]valThe brightness of the dot (-255 = full red brightness … 0 = no colour) (negative value!)
[out]black_pixelWhere to store the corresponding black value for no black dot at this position
[out]red_pixelWhere to store the corresponding red value

The input values for the red dots get inverted. A 0x00 input means "no red", so a 255 is stored to keep the paper colour. And a -255 input means a "full red", so a 0 is stored to print a red dot. At the same position, the black pixel is setup to keep the paper colour and to not disturb the red dot printing.

◆ black_dot_mark()

static void black_dot_mark ( signed int  val,
conv_type black_pixel,
conv_type red_pixel 
)
static

Mark a black dot value into both line bufffers

Parameters
[in]valThe brightness of the dot (-255 = full brightness … 0 = black) (negative value!)
[out]black_pixelWhere to store the corresponding black value for no black dot at this position
[out]red_pixelWhere to store the corresponding red value

The black dots scale from 0x00 (black dot!) to 0xff (paper colour!) Red settings depends on red_dot_mark() and its inversion, e.g. both colours scale from 0x00 (dot!) to 0xff (no dot!)

◆ to_grey()

static signed int to_grey ( const struct caps_dot_rgb pixel)
static

Convert an RGB pixel to grey scale

Parameters
[in]pixelThe pixel in RGB to convert (scale is 0…255 in each channel)
Returns
Grey value of the coloured RGB pixel (0…255)
Precondition
All colour component values are scaled from 0x00 … 0xff. Still expected scale here is: '0' no-colour, 0xff full colour (values for a screen, not printer)

https://duckduckgo.com/?q=color%20picker%20%23808080

◆ is_red()

static signed int is_red ( struct ql8_drv t1,
const struct caps_dot_rgb pixel 
)
static

Check, if the given pixel in RGB colour space defines a red pixel

Parameters
[in]t1Full job description
[in]pixelThe RGB pixel to check
Return values
PositiveBlack pixel brightness (grey scale value)
NegativeRed pixel brightness (hsv's value)

This is a simple and ugly red dot detection. Since we have an RGB colour space, a red colour is hard to detect. Converting it into an HSV colour space makes it simpler to detect red.
If the hue is below 30° or above 350° it is more or less a red. It then depends on its saturation and value if it is a visible red - or still a black.
All checked values are user defined and part of the ql8_drv structure.

◆ ql_next_colour_line_read()

static int ql_next_colour_line_read ( struct ql8_drv t1,
struct halftone_converter cnv_black,
struct halftone_converter cnv_red 
)
static

Read in the next line from an RGB raster right border aligned and separate black (e.g. shades of grey) and red (shades of)

Parameters
[in]t1Full job description
[in,out]cnv_blackThe black part of the line
[in,out]cnv_redThe red part of the line
Precondition
Input raster is expected as RGB 'chunked', 8 bit per channel (e.g. RGB RGB RGB …)
Todo:
The input raster can be smaller than the required line width. In this case the labels are smaller (for example) and the raster must be right aligned.
Note
Both colours gets scaled to COLOUR_VAL_DARK (dot) to COLOUR_VAL_BRIGHT (no dot) in both halftone converters

◆ ql_colour_page_print()

int ql_colour_page_print ( struct ql8_drv t1)

Convert the current page into the printer's wire data format

Parameters
[in]t1Full job description
Return values
0On success
-EINVALUnsupported raster input format
-ECANCELEDTermination request from outerspace

The routine loops through all or the remaining lines of the current page and converts them into the printer's colour wire format

Precondition
The format was already checked, e.g. it fits into the printer's media.