CUPS rastertoql filter  1.0.4
Filter for the Brother QL family of label printers
Data Structures | Macros | Functions | Variables
qlcolour.c File Reference

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

Data Structures

struct  colour_line_start
 

Macros

#define HIGH_ENERGY   0x01
 
#define LOW_ENERGY   0x02
 

Functions

static void remove_overlapping_dots (size_t cnt, unsigned char black_block[cnt], const unsigned char red_block[cnt])
 
static void colour_line_convert (struct line_command *cmd, size_t input_cnt, const signed short inputb[input_cnt], const signed short inputr[input_cnt])
 
static struct line_commandcolour_command_get (size_t bytes_per_line)
 
static void colour_command_put (void *cmd)
 
static int cups_ql_colour_line_process (struct qldriver *ql, struct halftone_converter *cnv_black, struct halftone_converter *cnv_red)
 
static double fraction (double no)
 
static void rgb2hsv (struct qlhsv *hsv, const struct qlrgb *rgb_pixel)
 
static void red_dot_mark (signed int val, signed short *black_pixel, signed short *red_pixel)
 
static void black_dot_mark (signed int val, signed short *black_pixel, signed short *red_pixel)
 
static signed int to_grey (const struct qlrgb *pixel)
 
static signed int is_red (struct qldriver *ql, const struct qlrgb *pixel)
 
static int cups_ql_next_colour_line_read (struct qldriver *ql, struct halftone_converter *cnv_black, struct halftone_converter *cnv_red)
 
int cups_ql_colour_page_print (struct qldriver *ql, int first_time)
 

Variables

int terminate
 

Detailed Description

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

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 Limitations in bi-colour print 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_start 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_start for details

Function Documentation

◆ black_dot_mark()

static void black_dot_mark ( signed int  val,
signed short *  black_pixel,
signed short *  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!)

◆ colour_command_get()

static struct line_command* 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
line_command structure
Todo:
shared component, because the monochrome usecase is only a special usecase of the colour usecase

◆ colour_command_put()

static void colour_command_put ( void *  cmd)
static

Free the buffer for the monochrome print command

Parameters
[in]cmdThe command buffer to be freed

◆ colour_line_convert()

static void colour_line_convert ( struct line_command cmd,
size_t  input_cnt,
const signed short  inputb[input_cnt],
const signed short  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 (90 bytes per line each).

◆ cups_ql_colour_line_process()

static int cups_ql_colour_line_process ( struct qldriver ql,
struct halftone_converter cnv_black,
struct halftone_converter cnv_red 
)
static

Process one line and send it to the printer

Parameters
[in]qlFull 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

◆ cups_ql_colour_page_print()

int cups_ql_colour_page_print ( struct qldriver ql,
int  first_time 
)

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

Parameters
[in]qlFull job description
[in]first_time'1' if called the first time of this job, '0' else
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.

◆ cups_ql_next_colour_line_read()

static int cups_ql_next_colour_line_read ( struct qldriver ql,
struct halftone_converter cnv_black,
struct halftone_converter cnv_red 
)
static

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

Parameters
[in]qlFull job description
[in,out]cnv_blackThe black part of the line
[in,out]cnv_redThe red part of the line
Precondition
Input Raster is 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 0x00 (dot) to 0xff (no dot) in both halftone converters

◆ 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?

◆ is_red()

static signed int is_red ( struct qldriver ql,
const struct qlrgb pixel 
)
static

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

Parameters
[in]qlFull 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 qldriver structure.

◆ red_dot_mark()

static void red_dot_mark ( signed int  val,
signed short *  black_pixel,
signed short *  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.

◆ remove_overlapping_dots()

static void remove_overlapping_dots ( size_t  cnt,
unsigned char  black_block[cnt],
const unsigned char  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

◆ rgb2hsv()

static void rgb2hsv ( struct qlhsv hsv,
const struct qlrgb 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

◆ to_grey()

static signed int to_grey ( const struct qlrgb 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

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

Variable Documentation

◆ terminate

int terminate

Change to '1' to signal the program should stop