
com.github.tommyettinger.anim8.Dithered Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of anim8-gdx Show documentation
Show all versions of anim8-gdx Show documentation
Support for writing animated GIF and PNG files, plus PNG8, to libGDX.
The newest version!
/*
* Copyright (c) 2022 Tommy Ettinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
*/
package com.github.tommyettinger.anim8;
/**
* A renderer and/or writer that allows selecting a {@link DitherAlgorithm} for its output.
*
* Created by Tommy Ettinger on 6/6/2020.
*/
public interface Dithered {
/**
* Gets the PaletteReducer this uses to lower the color count in an image. If the PaletteReducer is null, this
* should try to assign itself a PaletteReducer when given a new image.
* @return the PaletteReducer this uses; may be null
*/
PaletteReducer getPalette();
/**
* Sets the PaletteReducer this uses to bring a high-color or different-palette image down to a smaller palette
* size. If {@code palette} is null, this should try to assign itself a PaletteReducer when given a new image.
* @param palette a PaletteReducer that is often pre-configured with a specific palette; null is usually allowed
*/
void setPalette(PaletteReducer palette);
/**
* Gets the {@link DitherAlgorithm} this is currently using.
* @return which dithering algorithm this currently uses.
*/
DitherAlgorithm getDitherAlgorithm();
/**
* Sets the dither algorithm (or disables it) using an enum constant from {@link DitherAlgorithm}. If this is given
* null, it instead does nothing.
* @param ditherAlgorithm which {@link DitherAlgorithm} to use for upcoming output
*/
void setDitherAlgorithm(DitherAlgorithm ditherAlgorithm);
/**
* Represents a choice of dithering algorithm to apply when writing a high-color image with a color-limited format.
* Options are NONE (just using solid blocks of the closest color), GRADIENT_NOISE (using an edit on Jorge Jimenez'
* Gradient Interleaved Noise, a kind of ordered dither that adds some visual noise to break up patterns), PATTERN
* (Thomas Knoll's Pattern Dithering, with some gamma correction applied), DIFFUSION (an error-diffusing dither
* using Floyd-Steinberg, which isn't optimal for animations but is very good for still images), BLUE_NOISE (an
* ordered dither that corrects mismatched colors by checking a blue noise texture with no noticeable large
* patterns, and also using a quasi-random pattern to further break up artifacts), CHAOTIC_NOISE (which is like
* BLUE_NOISE but makes each frame of an animation dither differently, which can look busy but also trick the eye
* into seeing details over several frames), and SCATTER (which is similar to DIFFUSION but uses blue noise to
* scatter overly-regular patterns around). While NONE, GRADIENT_NOISE, BLUE_NOISE, DIFFUSION, CHAOTIC_NOISE, and
* SCATTER maintain the approximate lightness balance of the original image, PATTERN may slightly lighten mid-tones
* to make the gradient smoother. All of these algorithms except DIFFUSION are suitable for animations; using
* error-diffusion makes tiny changes in some frames disproportionately affect other pixels in those frames, which
* is compounded by how DIFFUSION can have large sections of minor artifacts that become very noticeable when they
* suddenly change between frames. Using SCATTER may be a good alternative to DIFFUSION for animations. NONE is
* fastest, and PATTERN is slowest. GRADIENT_NOISE, BLUE_NOISE, DIFFUSION, CHAOTIC_NOISE, and SCATTER are
* in-between.
*
* Created by Tommy Ettinger on 6/6/2020.
*/
enum DitherAlgorithm {
/**
* Doesn't dither at all; this generally looks bad unless the palette matches the colors in the image very
* closely or exactly.
*/
NONE("None"),
/**
* Jorge Jimenez' Gradient Interleaved Noise, modified slightly to use as an ordered dither here; this can have
* subtle repetitive artifacts, but doesn't have different amounts of noise on different frames or different
* parts of an image (which is a potential problem for {@link #DIFFUSION} and the other error-diffusion
* dithers). There's a sometimes-noticeable diagonal line pattern in the results this produces, and in
* animations, this pattern appears in the same place on every frame, which can be either desirable (small
* changes won't snowball into big ones) or undesirable (it makes the pattern appear to be part of the image).
* Although {@link #BLUE_NOISE} is mostly similar, it has a "spongy" artifact instead of the diagonal line
* artifact this can have. {@link #BLUE_NOISE} does have less noticeable small-scale patterns, though, for many
* input images. This handles gradients quite well. For pixel art, you may want to reduce the dither strength to
* 0.5 or so.
*/
GRADIENT_NOISE("GradientNoise"),
/**
* Thomas Knoll's Pattern Dither (with a 4x4 matrix), as originally described by Joel Yliluoma in
* this dithering article. Pattern Dither was
* patented until late 2019, so Yliluoma had to use an 8x8 matrix instead of the 4x4 used here; the 4x4 is much
* faster to compute and doesn't have as many artifacts with large enough palettes. It's an ordered dither, like
* {@link #GRADIENT_NOISE}, but isn't nearly as noisy (though it isn't noisy, it instead has regular
* square-shaped artifacts, which are mostly noticeable with small palettes). Earlier versions of Pattern Dither
* here had issues with lightness changing strangely based on dither strength, but these are mostly fixed now.
* {@link #OVERBOARD} is the current default; it does a better job at obscuring artifacts from dither, it
* maintains lightness well, and it handles gradients without banding. Setting the dither strength with
* {@link PaletteReducer#setDitherStrength(float)} can really change how strongly artifacts appear here, but
* artifacts may be very hard to spot with a full 255-color palette.
*/
PATTERN("Pattern"),
/**
* Floyd-Steinberg error-diffusion dithering; this is a good option for still images, and it's an OK option
* for some animated images. It doesn't lighten the image like {@link #PATTERN}, while still preserving most
* details on shapes, but small changes in one part of an animation will affect different frames very
* differently (which makes this less well-suited for animations). It may look better even in an animation than
* {@link #GRADIENT_NOISE}, depending on the animation, but this isn't often. Setting the dither strength with
* {@link PaletteReducer#setDitherStrength(float)} can improve the results with DIFFUSION tremendously, but the
* dither strength shouldn't go above about 1.5 or maybe 2.0 (this shows artifacts at higher strength).
* {@link #SCATTER}, {@link #NEUE}, {@link #WOVEN}, and {@link #DODGY} are based on this, and are generally able
* to break up visible artifacts that Floyd-Steinberg can have; all of those are now recommended over Diffusion.
*/
DIFFUSION("Diffusion"),
/**
* This is an ordered dither that modifies any error in a pixel's color by using 3 blue-noise patterns for each
* channel separately. The separate channels have their resulting positive or negative values added to the
* pixel's channels. This uses triangular-mapped blue noise patterns, which means most of its values are in the
* middle of its range and very few are at the extremely bright or dark. This yields closer results to
* {@link #NONE} than other ordered dithers like {@link #GRADIENT_NOISE}; it preserves soft gradients reasonably
* well, and it keeps lightness moderately-well, but it can look "noisier" than the other ordered dithers. For
* reference, the blue noise texture this uses looks like
* this small image;
* it looks different from a purely-random white noise texture because blue noise has no low frequencies in any
* direction, while white noise has all frequencies in equal measure. This has been optimized for quality on
* animations more so than on still images. Where error-diffusion dithers like {@link #NEUE} and
* {@link #DIFFUSION} can have "swimming" artifacts where the dither moves around in-place, ordered dithers are
* essentially immune to that type of artifact. Setting the dither strength with
* {@link PaletteReducer#setDitherStrength(float)} has significant effect (it didn't do much in previous
* versions), and raising it can improve depth and the appearance of some images when banding occurs.
*/
BLUE_NOISE("BlueNoise"),
/**
* Very similar to {@link #BLUE_NOISE} for a still frame, albeit less orderly, but in an animation this will
* change wildly from frame to frame, taking an ordered dither (one which uses the same blue noise texture that
* {@link #BLUE_NOISE} does) and incorporating one of the qualities of an error-diffusion dither to make each
* frame dither differently. This can look very good for moving images, but it is, true to its name, both
* chaotic and noisy. It can make many images look "rough" and "scratchy." Setting the dither strength with
* {@link PaletteReducer#setDitherStrength(float)} won't do much here, but the result will have more of a
* regular blue-noise pattern when dither strength is very low, and small changes will be introduced as dither
* strength approaches 1.
*/
CHAOTIC_NOISE("ChaoticNoise"),
/**
* This tries to subtly alter the more rigidly-defined error-diffusion dither of {@link #DIFFUSION} with a small
* amount of triangular-distributed blue noise, and unlike {@link #CHAOTIC_NOISE}, it doesn't introduce white
* noise. This offers an excellent mix of shape preservation, color preservation, animation-compatibility, and
* speed, and it was the default for a long time. Setting the dither strength to a low value makes this more
* bold, with higher contrast, while setting the strength too high (above 1.5, or sometimes higher) can
* introduce artifacts. This is only-just-okay at smooth gradient handling; {@link #NEUE}, {@link #DODGY}, and
* {@link #OVERBOARD} are much better at that and otherwise similar.
*/
SCATTER("Scatter"),
/**
* An error diffusion dither that mixes in ordered noise from a triangular-mapped blue noise texture; this is
* one of the best-behaving dithers here when it comes to smooth gradients. The approach to blue noise here is
* to add it to the pixel channels before calculating error diffusion for that pixel. This is different from
* {@link #SCATTER} in only a few ways, but a main one is that Scatter multiplies the current error by a blue
* noise value, where this adds in blue noise regardless of current error. The exact reason isn't clear, but
* this is drastically better when dithering smooth gradients, and can avoid banding except for the very
* smallest palettes. While {@link #BLUE_NOISE} is similarly good with smooth gradients, it has a hard time
* preserving fine color information (lightness is kept by Blue_Noise, but hue and saturation aren't very well);
* Neue preserves both. {@link #DODGY} is a potential successor to NEUE, and acts much like it except that it
* changes each RGB component separately, using three different blue noise textures. DODGY is, however, more
* chaotic-looking sometimes. There's always the current default dither, {@link #OVERBOARD}, which was inspired
* by NEUE, DODGY, and {@link #WOVEN} to get a generally-good compromise.
*/
NEUE("Neue"),
/**
* An ordered dither built around the lightness-dispersing R2 point sequence, by Martin Roberts. This is
* similar to {@link #GRADIENT_NOISE}; both add or subtract from the values at each pixel, but usually add a
* very different value to each pixel than to any of its neighbors. A major difference between GRADIENT_NOISE
* and this would be that instead of changing all RGB components at once, ROBERTS changes each component
* separately, using shifted versions of the R2 sequence. Compared to GRADIENT_NOISE, this has better (more
* faithful) reproduction of many colors, but may show some colors less easily. This is an ordered dither,
* so it won't change what artifacts it shows across different frames of an animation (the behavior here is
* usually desirable, but not always).
*/
ROBERTS("Roberts"),
/**
* An error-diffusion dither much like {@link #NEUE}, except that it adds or subtracts a different error value
* from each RGB channel, and that it uses translated copies of the R2 dither used by {@link #ROBERTS}, instead
* of using blue noise in any way. Modifying each channel separately can help color quality a lot, especially if
* dither strength is high. R2 dither tends to have very fine-grained artifacts that are hard to notice, but
* using a different translation for red, green, and blue means that sometimes the artifacts align for two or
* more channels repeatedly, forming somewhat-noticeable light and dark patterns that look like scales or dots.
* The artifacts here are usually less obvious than the ones in {@link #NEUE} at the same dither strength. This
* is an excellent choice for still images, especially those with small, varied palettes. It is not expected to
* be as good for pixel-art animations as an ordered dither.
*/
WOVEN("Woven"),
/**
* An error-diffusion dither that, like {@link #NEUE}, starts with {@link #DIFFUSION Floyd-Steinberg} dither and
* adds in blue noise values to break up patterns. Unlike NEUE, but like {@link #WOVEN}, this adds different
* noise values to the red, green, and blue channels. This last step significantly improves color accuracy, even
* on small palettes, while avoiding repetitive artifacts like WOVEN has. This is probably not a great pick for
* pixel-art animations, but can be good for some other GIFs in specific cases; when GIFs are recompressed, and
* they use ordered dithers, the artifacts can worsen, but an error-diffusion dither can move around artifacts
* in things like videos converted to GIF such that any artifact lasts only one frame.
*/
DODGY("Dodgy"),
/**
* An intentionally-low-fidelity ordered dither with obvious repeating 2x2 patterns on a regular grid.
* This is meant for dithering higher-color-count pixel art to produce lower color counts, without using any
* techniques that are too complex to be used effectively in hand-made pixel art. This may actually look simpler
* than it would have to be to look hand-made, especially at low dither strength.
*
* This is probably closest to {@link #PATTERN} in appearance, just because they both use a square grid, but
* this is much faster to run and looks less intricate. They also use different grids.
*/
LOAF("Loaf"),
/**
* An error-diffusion dither (like {@link #DIFFUSION}, but using Burkes instead of Floyd-Steinberg) that uses
* offset versions of the R2 sequence (like {@link #WOVEN}) and different blue noise textures (like
* {@link #DODGY}). This is a very good dither in many cases, and performs especially well on any-sized
* mid-to-low-saturation palettes. It tends to be able to preserve both hue and lightness well without showing
* repetitive structural artifacts (like WOVEN does). The main down-side to this is that in some cases, you may
* need to reduce or slightly increase dither strength to either avoid horizontal-zig-zag-line artifacts, or to
* improve hue or lightness fidelity. These cases aren't especially common, and working around this is as easy
* as calling {@link PaletteReducer#setDitherStrength(float)} (or its counterpart for a Gif or PNG class). These
* artifacts have gotten less frequent with some changes to the algorithm just after it was introduced.
*/
WREN("Wren"),
/**
* An error-diffusion dither (like {@link #DIFFUSION}, but using Burkes instead of Floyd-Steinberg) that uses
* an assortment of patterns to add error to diffuse, selecting which patterns to use in a way that mimics a
* simple ordered dither. This looks a lot like {@link #WREN} in practice, but tends to be smoother, and avoids
* some serious artifacts if the dither strength is higher than 1. Unlike {@link #WREN} and {@link #WOVEN}, this
* won't usually add a "rough" or "canvas-like" appearance to parts of an image that are mostly flat in color,
* making it usually more faithful at keeping fine details. The main disadvantage of this dithering algorithm is
* that it is more complex than most of the others here, so copying or editing it would be more challenging. It
* doesn't appear to be much slower than {@link #WREN}, if it is slower at all.
*
* Like {@link #WREN}, but unlike {@link #NEUE}, this adds extra error differently to different RGB channels.
* It doesn't go quite as far as {@link #WREN} at allowing really tremendous changes in color, which does mean
* it isn't always as capable of producing high-quality dithers with very small palettes. However, this tradeoff
* also means it doesn't pick up low-quality artifacts when dither strength is high.
*
* This dither is based on Burkes dither, but you could also just use {@link #BURKES} to avoid adding in any
* extra noise. You could also use {@link #OCEANIC} to incorporate just a little noise, softly. Relative to
* those two (newer) dithers, OVERBOARD has a harder time with "curt" gradients, that is, those that change
* smoothly but very quickly, and quickly stop changing. It does do well with larger, more-free-form gradients.
*
* This is currently the default dither.
*/
OVERBOARD("Overboard"),
/**
* An error-diffusion dither like {@link #DIFFUSION}, but using Burkes dither instead of Floyd-Steinberg. This
* can often look better than Floyd-Steinberg, at least how it is implemented here. This remains the case even
* with fairly high dither strength. If you set the strength unreasonably high, this will slowly approach an
* upper limit for how strong it can get, but will never reach it.
*
* If you encounter issues with BURKES dither, you can try the very similar {@link #OCEANIC} dither instead. It
* has almost the same code as BURKES, except that makes a small adjustment to every amount of error that gets
* diffused, using blue noise to make the adjustment deterministic but quasi-random.
*
* The source for this is very similar to the other error-diffusion algorithms in use here, and probably was
* informed by this blog post
* by Tanner Helland.
*/
BURKES("Burkes"),
/**
* An error-diffusion dither based closely on {@link #BURKES}, but that modifies how much error gets diffused
* using a per-pixel multiplier obtained from blue noise. Using noise to (usually slightly) adjust the error
* makes some unpleasant artifacts in BURKES dither essentially disappear here, replaced with fuzzy sections.
* This does well on soft lightness gradients, much like how {@link #NEUE} does, and is significantly better
* than BURKES at this task. It adds some noise, but not nearly as much as {@link #NEUE}, {@link #DODGY},
* {@link #SCATTER}, {@link #WREN}, or {@link #OVERBOARD}, while avoiding the repetitive artifacts in
* {@link #ROBERTS}, {@link #WOVEN}, and {@link #PATTERN}.
*/
OCEANIC("Oceanic"),
/**
* A close relative of {@link #OCEANIC}, this also incorporates noise into {@link #BURKES} to change how each
* pixel diffuses error. Unlike OCEANIC, the noise is different for each channel, which can improve how well
* this approximates colors with small palettes. This is the same technique used by {@link #DODGY} to improve
* upon {@link #NEUE}, and various other newer dithering algorithms here also use it.
*/
SEASIDE("Seaside"),
/**
* A relative of {@link #LOAF}, this is another ordered dither, with comparable speed to and higher quality than
* LOAF (but less of a "hand-drawn" feeling), and higher speed and comparable quality to {@link #PATTERN}.
* This will have some grid-based artifacts, but because it uses a somewhat large 8x8 grid (as opposed to 2x2
* for LOAF), their appearance isn't always as obvious. Like LOAF and PATTERN, this should look good for
* animations, since it doesn't have the error-diffusion issues where diffused error can zigzag over a moving
* object during an animation.
*/
GOURD("Gourd");
/**
* Used by {@link #toString()} to store a more human-readable name that isn't ALWAYS_YELLING.
*/
public final String legibleName;
/**
* A cached array of the result of {@link #values()}, to avoid repeatedly allocating new
* {@code DitherAlgorithm[]} arrays on each call to values().
*/
// currently (in version 0.4.5), this is:
// NONE, GRADIENT_NOISE, PATTERN, DIFFUSION, BLUE_NOISE, CHAOTIC_NOISE, SCATTER, NEUE, ROBERTS, WOVEN, DODGY, LOAF, WREN, OVERBOARD, BURKES, OCEANIC
// if alphabetized:
// BLUE_NOISE, BURKES, CHAOTIC_NOISE, DIFFUSION, DODGY, GRADIENT_NOISE, LOAF, NEUE, NONE, OCEANIC, OVERBOARD, PATTERN, ROBERTS, SCATTER, WOVEN, WREN
public static final DitherAlgorithm[] ALL = values();
DitherAlgorithm(String name){
this.legibleName = name;
}
@Override
public String toString() {
return legibleName;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy