|
|
|
|
.\" Automatically generated by Pandoc 3.8.2
|
|
|
|
|
.\"
|
|
|
|
|
.TH "DIDDER" "1" "October 20, 2025" "didder v1.3.0-15-g566375b" "User Manual"
|
|
|
|
|
.SH NAME
|
|
|
|
|
didder \(em dither images
|
|
|
|
|
.SH SYNOPSIS
|
|
|
|
|
\f[B]didder\f[R] [global options] command [command options]
|
|
|
|
|
[arguments\&...]
|
|
|
|
|
.SH DESCRIPTION
|
|
|
|
|
Dither images with a variety of algorithms and processing options.
|
|
|
|
|
.PP
|
|
|
|
|
Images with transparency are supported, and their alpha channel is kept
|
|
|
|
|
the way it was to begin with.
|
|
|
|
|
.PP
|
|
|
|
|
Mandatory global flags are \f[B]\-\-palette\f[R], \f[B]\-\-in\f[R], and
|
|
|
|
|
\f[B]\-\-out\f[R], all others are optional.
|
|
|
|
|
Each command applies a dithering algorithm or set of algorithms to the
|
|
|
|
|
input image(s).
|
|
|
|
|
.PP
|
|
|
|
|
The most important parts of this manual are highlighted in the
|
|
|
|
|
\f[B]TIPS\f[R] section, make sure you check it out!
|
|
|
|
|
.PP
|
|
|
|
|
Homepage: \c
|
|
|
|
|
.UR https://github.com/makeworld-the-better-one/didder
|
|
|
|
|
.UE \c
|
|
|
|
|
.SH OPTIONS
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-i\f[R], \f[B]\-\-in\f[R] \f[I]PATH\f[R]
|
|
|
|
|
Set the input file.
|
|
|
|
|
This flag can be used multiple times to dither multiple images with the
|
|
|
|
|
same palette and method.
|
|
|
|
|
A \f[I]PATH\f[R] of \(aq\f[B]\-\f[R]\(cq stands for standard input.
|
|
|
|
|
.RS
|
|
|
|
|
The input file path can also be parsed as a glob.
|
|
|
|
|
This will only happen if the path contains an asterisk.
|
|
|
|
|
For example \f[B]\-i \(aq*.jpg\(cq\f[R] will select all the .jpg files
|
|
|
|
|
in the current directory as input.
|
|
|
|
|
See this page for more info on glob pattern matching: \c
|
|
|
|
|
.UR https://golang.org/pkg/path/filepath/#Match
|
|
|
|
|
.UE \c
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-o\f[R], \f[B]\-\-out\f[R] \f[I]PATH\f[R]
|
|
|
|
|
Set the output file or directory.
|
|
|
|
|
A \f[I]PATH\f[R] of \(aq\f[B]\-\f[R]\(cq stands for standard output.
|
|
|
|
|
.RS
|
|
|
|
|
If \f[I]PATH\f[R] is an existing directory, then for each image input,
|
|
|
|
|
an output file with the same name (but possibly different extension)
|
|
|
|
|
will be created in that directory.
|
|
|
|
|
If \f[I]PATH\f[R] is a file, that ends in .gif (or \f[B]\-\-format
|
|
|
|
|
gif\f[R] is set) then multiple input files will be combined into an
|
|
|
|
|
animated GIF.
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-p\f[R], \f[B]\-\-palette\f[R] \f[I]COLORS\f[R]
|
|
|
|
|
Set the color palette used for dithering.
|
|
|
|
|
Colors are entered as a single quoted argument, with each color
|
|
|
|
|
separated by a space.
|
|
|
|
|
Colors can be formatted as RGB tuples (comma separated), hex codes
|
|
|
|
|
(case\-insensitive, with or without the `#'), a single number from
|
|
|
|
|
0\-255 for grayscale, or a color name from the SVG 1.1 spec (aka the
|
|
|
|
|
HTML or W3C color names).
|
|
|
|
|
All colors are interpreted in the sRGB colorspace.
|
|
|
|
|
.RS
|
|
|
|
|
A list of all color names is available at \c
|
|
|
|
|
.UR https://www.w3.org/TR/SVG11/types.html#ColorKeywords
|
|
|
|
|
.UE \c
|
|
|
|
|
Images are converted to grayscale automatically if the palette is
|
|
|
|
|
grayscale.
|
|
|
|
|
This produces more correct results.
|
|
|
|
|
Here\(cqs an example of all color formats being used: \f[B]\-\-palette
|
|
|
|
|
\(aq23,230,100 D24242 135 forestGreen\(cq\f[R]
|
|
|
|
|
Alternative, you can write mmcq:N in lieu of color names, where N is a
|
|
|
|
|
power of two.
|
|
|
|
|
This will use the median cut algorithm to pick the top N colors in the
|
|
|
|
|
image, and set those as the palette.
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-r\f[R], \f[B]\-\-recolor\f[R] \f[I]COLORS\f[R]
|
|
|
|
|
Set the color palette used for replacing the dithered color palette
|
|
|
|
|
after dithering.
|
|
|
|
|
The argument syntax is the same as \f[B]\-\-palette\f[R], with one
|
|
|
|
|
exception.
|
|
|
|
|
It also supports RGB\f[I]A\f[R] tuples, so 4 values.
|
|
|
|
|
This means you can also choose to change the opacity of a palette color
|
|
|
|
|
after dithering.
|
|
|
|
|
The values are not premultiplied, so set the RGB to the color you want
|
|
|
|
|
as you\(cqd expect.
|
|
|
|
|
.RS
|
|
|
|
|
The \f[B]\-\-recolor\f[R] flag exists because when palettes that are
|
|
|
|
|
severely limited in terms of RGB spread are used, accurately
|
|
|
|
|
representing the image colors with the desired palette is impossible.
|
|
|
|
|
Instead of accuracy of color, the new goal is accuracy of luminance, or
|
|
|
|
|
even just accuracy of contrast.
|
|
|
|
|
For example, the original Nintendo Game Boy used a solely green palette:
|
|
|
|
|
\c
|
|
|
|
|
.UR https://en.wikipedia.org/wiki/List_of_video_game_console_palettes#Game_Boy
|
|
|
|
|
.UE \c
|
|
|
|
|
\&.
|
|
|
|
|
By setting \f[B]\-\-palette\f[R] to shades of gray and then
|
|
|
|
|
\f[B]\-\-recolor\f[R]\-ing to the desired shades of green, input images
|
|
|
|
|
will be converted to grayscale automatically and then dithered in one
|
|
|
|
|
dimension (gray), rather than trying to dither a color image (three
|
|
|
|
|
dimensions, RGB) into a one dimensional green palette.
|
|
|
|
|
This is similar to \(lqhue shifting\(rq or \(lqcolorizing\(rq an image
|
|
|
|
|
in image editing software.
|
|
|
|
|
For these situations, \f[B]\-\-recolor\f[R] should usually be a palette
|
|
|
|
|
made up of one hue, and \f[B]\-\-palette\f[R] should be the grayscale
|
|
|
|
|
version of that palette.
|
|
|
|
|
The \f[B]\-\-palette\f[R] could also be just equally spread grayscale
|
|
|
|
|
values, which would increase the contrast but make the luminance
|
|
|
|
|
inaccurate.
|
|
|
|
|
Recoloring can also be useful for increasing contrast on a strange
|
|
|
|
|
palette, like: \f[B]\-\-palette \(aqblack white\(cq \-\-recolor
|
|
|
|
|
\(aqindigo LimeGreen\(cq\f[R].
|
|
|
|
|
Setting just \f[B]\-\-palette \(aqindigo LimeGreen\(cq\f[R] would give
|
|
|
|
|
bad (low contrast) results because that palette is not that far apart in
|
|
|
|
|
RGB space.
|
|
|
|
|
These \(lqbad results\(rq are much more pronounced when the input image
|
|
|
|
|
is in color, because three dimensions are being reduced.
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-s\f[R], \f[B]\-\-strength\f[R] \f[I]DECIMAL/PERCENT\f[R]
|
|
|
|
|
Set the strength of dithering.
|
|
|
|
|
This will affect every command except \f[B]random\f[R].
|
|
|
|
|
Decimal format is \-1.0 to 1.0, and percentage format is \-100% or 100%.
|
|
|
|
|
The range is not limited.
|
|
|
|
|
A zero value will be ignored.
|
|
|
|
|
Defaults to 100%, meaning that the dithering is applied at full
|
|
|
|
|
strength.
|
|
|
|
|
.RS
|
|
|
|
|
Reducing the strength is often visibly similar to reducing contrast.
|
|
|
|
|
With the \f[B]edm\f[R] command, \f[B]\-\-strength\f[R] can be used to
|
|
|
|
|
reduce noise, when set to a value around 80%.
|
|
|
|
|
When using the \f[B]bayer\f[R] command with a grayscale palette, usually
|
|
|
|
|
100% is fine, but for 4x4 matrices or smaller, you may need to reduce
|
|
|
|
|
the strength.
|
|
|
|
|
For \f[B]bayer\f[R] (and by extension \f[B]odm\f[R]) color palette
|
|
|
|
|
images, several sites recommend 64% strength (written as 256/4).
|
|
|
|
|
This is often a good default for \f[B]bayer\f[R]/\f[B]odm\f[R] dithering
|
|
|
|
|
color images, as 100% will distort colors too much.
|
|
|
|
|
Do not use the default of 100% for Bayer dithering color images.
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-j\f[R], \f[B]\-\-threads\f[R] \f[I]NUM\f[R]
|
|
|
|
|
Set the number of threads used.
|
|
|
|
|
By default a thread will be created for each CPU.
|
|
|
|
|
As dithering is a CPU\-bound operation, going above this will not
|
|
|
|
|
improve performance.
|
|
|
|
|
This flag does not affect \f[B]edm\f[R], as error diffusion dithering
|
|
|
|
|
cannot be parallelized.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-g\f[R], \f[B]\-\-grayscale\f[R]
|
|
|
|
|
Make input image(s) grayscale before dithering.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-\-saturation\f[R] \f[I]DECIMAL/PERCENT\f[R]
|
|
|
|
|
Change input image(s) saturation before dithering.
|
|
|
|
|
Decimal range is \-1.0 to 1.0, percentage range is \-100% or 100%.
|
|
|
|
|
Values that exceed the range will be rounded down.
|
|
|
|
|
\-1.0 or \-100% saturation is equivalent to \f[B]\-\-grayscale\f[R].
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-\-brightness\f[R] \f[I]DECIMAL/PERCENT\f[R]
|
|
|
|
|
Change input image(s) saturation before dithering.
|
|
|
|
|
Decimal range is \-1.0 to 1.0, percentage range is \-100% or 100%.
|
|
|
|
|
Values that exceed the range will be rounded down.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-\-contrast\f[R] \f[I]DECIMAL/PERCENT\f[R]
|
|
|
|
|
Change input image(s) saturation before dithering.
|
|
|
|
|
Decimal range is \-1.0 to 1.0, percentage range is \-100% or 100%.
|
|
|
|
|
Values that exceed the range will be rounded down.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-\-no\-exif\-rotation\f[R]
|
|
|
|
|
Disable using the EXIF rotation flag in image metadata to rotate the
|
|
|
|
|
image before processing.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-f\f[R], \f[B]\-\-format\f[R] \f[I]FORMAT\f[R]
|
|
|
|
|
Set the output file format.
|
|
|
|
|
Valid options are \(aqpng\(cq and \(aqgif\(cq.
|
|
|
|
|
It will auto detect from filename when possible, so usually this does
|
|
|
|
|
not need to be set.
|
|
|
|
|
If \f[B]\-o\f[R] is \(aq\f[B]\-\f[R]\(cq or a directory, then PNG files
|
|
|
|
|
will be outputted by default.
|
|
|
|
|
So this flag can be used to force GIF output instead.
|
|
|
|
|
If your output file has an extension that is not .png or .gif the format
|
|
|
|
|
will need to be specified.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-\-no\-overwrite\f[R]
|
|
|
|
|
Setting this flag means the program will stop before overwriting an
|
|
|
|
|
existing file.
|
|
|
|
|
Any files written before that one was encountered will stay in place.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-c\f[R], \f[B]\-\-compression\f[R] \f[I]TYPE\f[R]
|
|
|
|
|
Set the type of PNG compression.
|
|
|
|
|
Options are \(aqdefault\(cq, \(aqno\(cq, \(aqspeed\(cq, and
|
|
|
|
|
\(aqsize\(cq.
|
|
|
|
|
This flag is ignored for non\-PNG output.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-\-fps\f[R] \f[I]DECIMAL\f[R]
|
|
|
|
|
Set frames per second for animated GIF output.
|
|
|
|
|
Note that not all FPS values can be represented by the GIF format, and
|
|
|
|
|
so the closest possible one will be chosen.
|
|
|
|
|
This flag has no default, and is required when animated GIFs are being
|
|
|
|
|
outputted.
|
|
|
|
|
This flag is ignored for non animated GIF output.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-l\f[R], \f[B]\-\-loop\f[R] \f[I]NUM\f[R]
|
|
|
|
|
Set the number of times animated GIF output should loop.
|
|
|
|
|
0 is the default, and will loop infinitely.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-x\f[R], \f[B]\-\-width\f[R] \f[I]NUM\f[R]
|
|
|
|
|
Set the width the input image(s) will be resized to, before dithering.
|
|
|
|
|
Aspect ratio will be maintained if \f[B]\-\-height\f[R] is not specified
|
|
|
|
|
as well.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-y\f[R], \f[B]\-\-height\f[R] \f[I]NUM\f[R]
|
|
|
|
|
Set the height the input image(s) will be resized to, before dithering.
|
|
|
|
|
Aspect ratio will be maintained if \f[B]\-\-width\f[R] is not specified
|
|
|
|
|
as well.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-u\f[R], \f[B]\-\-upscale\f[R] \f[I]NUM\f[R]
|
|
|
|
|
Scale image up after dithering.
|
|
|
|
|
So \(aq2\(cq will make the output two times as big as the input (after
|
|
|
|
|
\f[B]\-x\f[R] and/or \f[B]\-y\f[R]).
|
|
|
|
|
Only integers are allowed, as scaling up by a non\-integer amount would
|
|
|
|
|
distort the dithering pattern and introduce artifacts.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-v\f[R], \f[B]\-\-version\f[R]
|
|
|
|
|
Get version information.
|
|
|
|
|
.SH COMMANDS
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]random\f[R] \f[I]MIN MAX\f[R] or \f[I]RED_MIN RED_MAX GREEN_MIN GREEN_MAX BLUE_MIN BLUE_MAX\f[R]
|
|
|
|
|
Grayscale and RGB random dithering
|
|
|
|
|
.RS
|
|
|
|
|
Accepts two arguments (min and max) for RGB or grayscale, or six
|
|
|
|
|
(min/max for each channel) to control each RGB channel.
|
|
|
|
|
Arguments can be separated by commas or spaces.
|
|
|
|
|
Random dithering adds random noise to the image.
|
|
|
|
|
The min and max numbers limit the range of the random noise.
|
|
|
|
|
A good default is \-0.5,0.5, which means that a middle gray pixel is 50%
|
|
|
|
|
likely to become black and 50% likely to become white, assuming a black
|
|
|
|
|
and white palette.
|
|
|
|
|
So \-0.2,0.2 will reduce the noise (20%), while \-0.7,0.7 will increase
|
|
|
|
|
it (70%).
|
|
|
|
|
Values like \-0.5,0.7 will bias the noise to one end of the channel(s).
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-s\f[R], \f[B]\-\-seed\f[R] \f[I]DECIMAL\f[R]
|
|
|
|
|
Set the seed for randomization.
|
|
|
|
|
This will also only use one thread, to keep output deterministic.
|
|
|
|
|
By default a different seed is chosen each time and multiple threads are
|
|
|
|
|
used.
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]bayer\f[R] \f[I]X\f[R] \f[I]Y\f[R]
|
|
|
|
|
Bayer matrix ordered dithering
|
|
|
|
|
.RS
|
|
|
|
|
Requires two arguments, for the X and Y dimension of the matrix.
|
|
|
|
|
They can be separated by a space, comma, or \(aqx\(cq.
|
|
|
|
|
Both arguments must be a power of two, with the exception of: 3x5, 5x3,
|
|
|
|
|
and 3x3.
|
|
|
|
|
.RE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]odm\f[R] \f[I]NAME/JSON/FILE\f[R]
|
|
|
|
|
Ordered Dithering Matrix
|
|
|
|
|
.RS
|
|
|
|
|
Select or provide an ordered dithering matrix.
|
|
|
|
|
This only takes one argument, but there a few types available:
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
A preprogrammed matrix name
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Inline JSON of a custom matrix
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Or a path to JSON for your custom matrix.
|
|
|
|
|
\(aq\f[B]\-\f[R]\(cq means standard input.
|
|
|
|
|
Here are all the built\-in ordered dithering matrices.
|
|
|
|
|
You can find details on these matrices here: \c
|
|
|
|
|
.UR https://github.com/makeworld-the-better-one/dither/blob/v2.0.0/ordered_ditherers.go
|
|
|
|
|
.UE \c
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDot4x4
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotDiagonal8x8
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Vertical5x3
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Horizontal3x5
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotDiagonal6x6
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotDiagonal8x8_2
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotDiagonal16x16
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDot6x6
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotSpiral5x5
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotHorizontalLine
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotVerticalLine
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDot8x8
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDot6x6_2
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDot6x6_3
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
ClusteredDotDiagonal8x8_3
|
|
|
|
|
Their names are case\-insensitive, and hyphens and underscores are
|
|
|
|
|
treated the same.
|
|
|
|
|
The JSON format (whether inline or in a file) looks like the below.
|
|
|
|
|
The matrix must be \(lqrectangular\(rq, meaning each array must have the
|
|
|
|
|
same length.
|
|
|
|
|
More information how to use a custom matrix can be found here: \c
|
|
|
|
|
.UR https://pkg.go.dev/github.com/makeworld-the-better-one/dither/v2#OrderedDitherMatrix
|
|
|
|
|
.UE \c
|
|
|
|
|
.RE
|
|
|
|
|
.IP
|
|
|
|
|
.EX
|
|
|
|
|
{
|
|
|
|
|
\(dqmatrix\(dq: [
|
|
|
|
|
[12, 5, 6, 13],
|
|
|
|
|
[4, 0, 1, 7],
|
|
|
|
|
[11, 3, 2, 8],
|
|
|
|
|
[15, 10, 9, 14]
|
|
|
|
|
],
|
|
|
|
|
\(dqmax\(dq: 16
|
|
|
|
|
}
|
|
|
|
|
.EE
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]edm\f[R] \f[I]NAME/JSON/FILE\f[R]
|
|
|
|
|
Error Diffusion Matrix
|
|
|
|
|
.RS
|
|
|
|
|
Select or provide an error diffusion matrix.
|
|
|
|
|
This only takes one argument, but there a few types available:
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
A preprogrammed matrix name
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Inline JSON of a custom matrix
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Or a path to JSON for your custom matrix.
|
|
|
|
|
\(aq\f[B]\-\f[R]\(cq means stdin.
|
|
|
|
|
Here are all the built\-in error diffusion matrices.
|
|
|
|
|
You can find details on these matrices here: \c
|
|
|
|
|
.UR https://github.com/makeworld-the-better-one/dither/blob/v2.0.0/error_diffusers.go
|
|
|
|
|
.UE \c
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Simple2D
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
FloydSteinberg
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
FalseFloydSteinberg
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
JarvisJudiceNinke
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Atkinson
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Stucki
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Burkes
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
Sierra (or Sierra3)
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
TwoRowSierra (or Sierra2)
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
SierraLite (or Sierra2_4A)
|
|
|
|
|
.PD 0
|
|
|
|
|
.P
|
|
|
|
|
.PD
|
|
|
|
|
.IP \(bu 2
|
|
|
|
|
StevenPigeon
|
|
|
|
|
Their names are case\-insensitive, and hyphens and underscores are
|
|
|
|
|
treated the same.
|
|
|
|
|
The JSON format (whether inline or in a file) for a custom matrix is
|
|
|
|
|
very simple, just a 2D array.
|
|
|
|
|
The matrix must be \(lqrectangular\(rq, meaning each array must have the
|
|
|
|
|
same length.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]\-s\f[R], \f[B]\-\-serpentine\f[R]
|
|
|
|
|
Enable serpentine dithering, which \(lqsnakes\(rq back and forth when
|
|
|
|
|
moving down the image, instead of going left\-to\-right each time.
|
|
|
|
|
This can reduce artifacts or patterns in the noise.
|
|
|
|
|
.RE
|
|
|
|
|
.SH TIPS
|
|
|
|
|
Read about \f[B]\-\-strength\f[R] if you haven\(cqt already.
|
|
|
|
|
.PP
|
|
|
|
|
Read about \f[B]\-\-recolor\f[R] if you haven\(cqt already.
|
|
|
|
|
.PP
|
|
|
|
|
It\(cqs easy to mess up a dithered image by scaling it manually.
|
|
|
|
|
It\(cqs best to scale the image to the size you want before dithering
|
|
|
|
|
(externally, or with \f[B]\-\-width\f[R] and/or \f[B]\-\-height\f[R]),
|
|
|
|
|
and then leave it.
|
|
|
|
|
.PP
|
|
|
|
|
If you need to scale it up afterward, use \f[B]\-\-upscale\f[R], rather
|
|
|
|
|
than another tool.
|
|
|
|
|
This will prevent image artifacts and blurring.
|
|
|
|
|
.PP
|
|
|
|
|
Be wary of environments where you can\(cqt make sure an image will be
|
|
|
|
|
displayed at 100% size, pixel for pixel.
|
|
|
|
|
Make sure to at least use nearest\-neighbor scaling, do your best to
|
|
|
|
|
preserve sharp pixel edges.
|
|
|
|
|
.PP
|
|
|
|
|
Dithered images must only be encoded in a lossless image format.
|
|
|
|
|
This is why the tool only outputs PNG and GIF.
|
|
|
|
|
.PP
|
|
|
|
|
To increase the dithering artifacts for aesthetic effect, you can
|
|
|
|
|
downscale the image before dithering and upscale after.
|
|
|
|
|
Like if the image is 1000 pixels tall, your command can look like
|
|
|
|
|
\f[B]didder \(enheight 500 \(enupscale 2 [\&...]\f[R].
|
|
|
|
|
Depending on the input image size and what final size you want, you can
|
|
|
|
|
of course just upscale as well.
|
|
|
|
|
.PP
|
|
|
|
|
If your palette (original or recolor) is low\-spread \(em meaning it
|
|
|
|
|
doesn\(cqt span much of the available shades of a single hue or the
|
|
|
|
|
entire RGB space \(em you can use flags like \f[B]\-\-brightness\f[R],
|
|
|
|
|
\f[B]\-\-contrast\f[R], and \f[B]\-\-saturation\f[R] to improve the way
|
|
|
|
|
dithered images turn out.
|
|
|
|
|
For example, if your palette is dark, you can turn up the brightness.
|
|
|
|
|
As mentioned above, these flags apply their transformations to the
|
|
|
|
|
original image and will not adjust your selected palette colors.
|
|
|
|
|
.SH EXAMPLES
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]didder \-\-palette \(dqblack white\(rq \-i input.jpg \-o test.png bayer 16x16\f[R]
|
|
|
|
|
This command dithers \f[CR]input.jpg\f[R] using only black and white
|
|
|
|
|
(implicitly converting the image to grayscale first), using a 16x16
|
|
|
|
|
Bayer matrix.
|
|
|
|
|
The result is written to \f[CR]test.png\f[R].
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]didder \-\-palette \(dqblack white\(rq \-i input.jpg \-o test.png odm ClusteredDot4x4\f[R]
|
|
|
|
|
Same command as above, but dithering with the preprogrammed ordered
|
|
|
|
|
dithering matrix called ClusteredDot4x4.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]didder \-i david.png \-o david_dithered.png \-\-palette \(dqblack white\(rq \-\-recolor \(dqblack F273FF\(rq \-\-upscale 2 bayer 4x4\f[R]
|
|
|
|
|
This is the command used for the README.
|
|
|
|
|
It dithers using a 4x4 Bayer matrix, initially to black and white, which
|
|
|
|
|
is then recolored to black and purple.
|
|
|
|
|
Dithering to black and purple directly would produce much lower contrast
|
|
|
|
|
results.
|
|
|
|
|
The dithered image is upscaled to be two times larger, so that the Bayer
|
|
|
|
|
dithering artifacts can be seen more clearly.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]didder \-i input.png \-o output.png \-p \(dq1E1E1E CDCDCD EDEDED FFFFFF\(rq \-r \(dq11161e 116bcd 63b3ed e1efff\(rq \-\-strength 64% \-\-brightness 20% bayer 32x32\f[R]
|
|
|
|
|
This command uses a blue recolor palette, one that is biased to being
|
|
|
|
|
darker.
|
|
|
|
|
The palette can be viewed at \c
|
|
|
|
|
.UR https://colorpeek.com/#11161e,116bcd,63b3ed,e1efff
|
|
|
|
|
.UE \c
|
|
|
|
|
\&.
|
|
|
|
|
The dithering palette is the grayscale version of those colors, to keep
|
|
|
|
|
luminance accurate.
|
|
|
|
|
Strength is set to 64%, which although usually recommended for Bayer
|
|
|
|
|
dithering of color images, works well here.
|
|
|
|
|
Alternatively, one could try and increase \f[B]\-\-contrast\f[R].
|
|
|
|
|
Finally, the brightness is increased to compensate for the dark palette.
|
|
|
|
|
.TP
|
|
|
|
|
\f[B]didder \-p \(dqblack white\(rq \-\-recolor \(dqdarkgreen white\(rq \-i frame_01.png \-i frame_02.png \-o output.gif \-\-fps 1 random \-0.5,0.5\f[R]
|
|
|
|
|
This command takes two input images and creates an animated GIF,
|
|
|
|
|
dithering and recoloring them along the way.
|
|
|
|
|
The GIF moves at 1 frame per second, and by default loops infinitely.
|
|
|
|
|
Random dithering is used, with recommended default of \-0.5,0.5.
|
|
|
|
|
.SH REPORTING BUGS
|
|
|
|
|
Any bugs can be reported by creating an issue on GitHub: \c
|
|
|
|
|
.UR https://github.com/makeworld-the-better-one/didder
|
|
|
|
|
.UE \c
|