blob: 5494c4ecee45cdc0dc4d947c745bd931ec9d8ce1 [file]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DITHERING_GLSL_
#define DITHERING_GLSL_
#include <impeller/types.glsl>
/// The dithering rate, which is 1.0 / 64.0, or 0.015625.
const float kDitherRate = 1.0 / 64.0;
/// Returns the closest color to the input color using 8x8 ordered dithering.
///
/// Ordered dithering divides the output into a grid of cells, and then assigns
/// a different threshold to each cell. The threshold is used to determine if
/// the color should be rounded up (white) or down (black).
//
/// This technique was chosen mostly because Skia also uses it:
/// https://github.com/google/skia/blob/f9de059517a6f58951510fc7af0cba21e13dd1a8/src/opts/SkRasterPipeline_opts.h#L1717
///
/// See also:
/// - https://en.wikipedia.org/wiki/Ordered_dithering
/// - https://surma.dev/things/ditherpunk/
/// - https://shader-tutorial.dev/advanced/color-banding-dithering/
vec4 IPOrderedDither8x8(vec4 color, vec2 dest) {
// Get the x and y coordinates of the pixel in the 8x8 grid.
uint x = uint(dest.x) % 8;
uint y = uint(dest.y);
y ^= x;
// Get the dither value from the matrix.
uint m = (y & 1) << 5 | //
(x & 1) << 4 | //
(y & 2) << 2 | //
(x & 2) << 1 | //
(y & 4) >> 1 | //
(x & 4) >> 2; //
// Scale that dither to [0,1), then (-0.5,+0.5), here using 63/128 = 0.4921875
// as 0.5-epsilon. We want to make sure our dither is less than 0.5 in either
// direction to keep exact values like 0 and 1 unchanged after rounding.
float dither = float(m) * (2.0 / 128.0) - (63.0 / 128.0);
// Apply the dither to the color.
color.rgb += dither * kDitherRate;
return color;
}
#endif