Ghostscript: halftoning and dithering

Tried to convert an image using Ghostscript and not happy with the result?
Tried to print the document in Linux using CUPS, and the printout has weird horizontal lines in it?


Original image which we will dither and halftone for 1-bit printing


Algorithm

Ghostscript has several dithering and halftoning alrogithms. Auto selection depends on the following factors:

  • DPI (resolution) of the output device: DPI >= 150 uses halftoning instead of dithering
  • -dDITHERPPI=xxx option forces halftoning (despite the option name!) even for low DPI
Output DPI Alrogithm
< 150 8×8 ordered dithering
>= 150 Horn's cosine line halftoning


Dithering

By default, dithering is used only for low-DPI output devices, < 150 DPI.
Only a single dithering function is defined by default in the initialization script

Dithering is considered a low-resolution path, which toggles the following configuration:

  • Screen: dithering
  • Spot function: square 8×8 ordered dither (by Gregg Townsend)
  • Screen angle:
  • Screen frequency: DPI / 16 by default
  • Transfer function: none ("Identity" in PDF terms)
  • Stroke adjust: yes (pixel-perfect alignment, no sub-pixel processing)


Forcing 8×8 dithering for high DPI output

.setloresscreen function configures all the aforementioned options inside the initialization script, which is run after processing gs… -c option.
The easiest way to trigger auto-reconfiguration I found is to define /Install function inside Page Device.

.setloresscreen accepts DPI as an argument. The simple invocation for 300 DPI is as follows:

gs … -c '<< /Install { 300 .setloresscreen } >> setpagedevice' …

And with DPI auto-detection code, as seen in the initialization script:

gs … -c '<< /Install { 72 72 matrix defaultmatrix dtransform abs exch abs .min .setloresscreen } >> setpagedevice' …

8×8 dithering


Using ordered dithering with .genordered

Ghostscript also includes dithering + halftoning generator function based on the threshold array, called .genordered.

It performs ordered dither screening pattern with the selected DotShape pixel output.

  • Screen: dithering + halftoning
  • Spot function: CIRCLE, REDBOOK, INVERTED, RHOMBOID, LINE_X, LINE_Y, DIAMOND1, DIAMOND2, ROUNDSPOT, check gen_ordered.c
  • Screen angle: selectable
  • Screen frequency: selectable
  • Transfer function: selectable
  • Stroke adjust: selectable

Example:

gs … -c '<< /Frequency 150 /Angle 135 /DotShape 8 >> .genordered /Default exch /Halftone defineresource sethalftone { } settransfer 0.003 setsmoothness' …

In the example, { } settransfer removes the default { 0.8 exp } transfer function (which will make the image darker compared to the default), and also sets 0.003 setsmoothness which I'm not sure what it does, but that's what is used in gen_ordered_example.ps.

All .genordered parameters are optional, but you would probably want to set them anyway. Minimal example:

gs … -c '<< >> .genordered /Default exch /Halftone defineresource sethalftone { } settransfer 0.003 setsmoothness' …

Ordered dithering with .genordered, frequency=60, angle=135, dotshape=circle(0)


Halftoning

Halftoning by default is used only for high-DPI output devices, >= 150 DPI.
The default halftone function, Horn's cosine spot, does not correspond to any halftone function defined in PDF standard.

Dithering is considered a high-resolution path, which toggles the following default configuration:

  • Screen: rational tangent line screen
  • Spot function: Berthold K.P. Horn's cosine line halftoning
  • Screen angle: 45°
  • Screen frequency: lookup table based on DPI, 150 max, forced with -dDITHERPPI=xxx
  • Transfer function: { 0.8 exp } (prevent the image from being too dark)
  • Stroke adjust: no, not aligned to pixels
  • Fill adjust: 0.5 dup .setfilladjust2, default Adobe's any-part-of-pixel rule
DPI Frequency (LPI) Approx. gray levels
100-199 46 6-8
200-299 46 20-26
300-399 60 26-31
400-499 60 45-55
500-599 60 70-88
600-699 106 33-39
700-799 106 44-51
800-899 106 57-68
900-999 106 72-89
1000-1099 133 57-68
1100-1499 133 69-127
1500+ 150 101+

LPI (lines-per-inch) could be overridden with -dDITHERPPI=xxx, maximum value of which is limited by output device DPI / 4.01: min(DITHERPPI, dpi/4.01).
Note: it accepts accepts LPI, not DPI, despite the name (and it's not a dither option either!)

-dDITHERPPI= lpi
Forces all devices to be considered high-resolution, and forces use of a halftone screen or screens with lpi lines per inch, disregarding the actual device resolution. Reasonable values for lpi are N/5 to N/20, where N is the resolution in dots per inch.

The rendering options documentation additionally tells us the following:

On high-resolution devices (at least 150 dpi resolution, or -dDITHERPPI specified), -dCOLORSCREEN forces the use of separate halftone screens with different angles for CMYK or RGB if halftones are needed (this produces the best-quality output); -dCOLORSCREEN=0 uses separate screens with the same frequency and angle; -dCOLORSCREEN=false forces the use of a single binary screen. The default if COLORSCREEN is not specified is to use separate screens with different angles if the device has fewer than 5 bits per color, and a single binary screen (which is never actually used under normal circumstances) on all other devices.

Condition Angle
dpi < 150 (if specifically forced) 45°
dpi >= 150 and -dCOLORSCREEN=false/0 45°
dpi >= 150 and -dCOLORSCREEN=true (default) C=45° M=90° Y=15° K=75°

Stock Ghostscript conversion configuration on 203 DPI output. Looks weird!


Using halftone functions from PDF specification

PDF defines several common halftoning spot functions.
Check the Specification on pages 303-307.

You can use each of the spot functions as follows:

gs … -c '<< /HalftoneType 1 /Frequency 150 /Angle 45 /SpotFunction { dup mul exch dup mul add 1 exch sub } >> /Default exch /Halftone defineresource sethalftone' …

Where:

  • HalftoneType: 1 for single halftone screen function (same function for each color), check Table 129 of PDF Specification for more information
  • Frequency: LPI (lines-per-inch) value
  • Angle: screen angle in degrees
  • SpotFunction: function from the PDF specification (a copy of which could be found in pdf_gstate.c)

frequency=60, angle=135, function=SimpleDot


Using stochastic halftone

As the stocht.ps stochastic threshold-array halftoning script tells us:

% For printing technologies other than inkjet, Stochastic halftoning
% may not look better than standard screening. In particular, thermal
% transfer and direct thermal tend to be better with standard ordered
% screening methods. Some laser printers may produce "smoother"
% looking gray shades with Stochastic halftoning. Try it, and if
% you like it, use it.

This is a grainy halftoning algorithm with the output similar to classic dithering altorithms like Floyd-Steinberg, but it is not based on error diffusion.

Usage:

gs … -f stocht.ps …

Stochastic halftoning