diff --git a/libs/gfx/include/psemek/gfx/dither.hpp b/libs/gfx/include/psemek/gfx/dither.hpp new file mode 100644 index 00000000..338c3719 --- /dev/null +++ b/libs/gfx/include/psemek/gfx/dither.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace psemek::gfx +{ + + basic_pixmap ordered_dither(std::size_t size); + +} diff --git a/libs/gfx/source/dither.cpp b/libs/gfx/source/dither.cpp new file mode 100644 index 00000000..30681233 --- /dev/null +++ b/libs/gfx/source/dither.cpp @@ -0,0 +1,40 @@ +#include + +namespace psemek::gfx +{ + + static std::uint8_t interleave_with_zeros(std::uint8_t input) { + std::uint8_t word = input; + word = (word ^ (word << 4 )) & 0x0f0f; + word = (word ^ (word << 2 )) & 0x3333; + word = (word ^ (word << 1 )) & 0x5555; + return word; + } + + static std::uint8_t bit_interleave(std::uint8_t a, std::uint8_t b) { + return interleave_with_zeros(a) | (interleave_with_zeros(b) << 1); + } + + static std::uint8_t bit_reverse(std::uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; + } + + basic_pixmap ordered_dither(std::size_t size) + { + if (size > 16) + throw std::runtime_error("8-bit dither map cannot be larger than 16x16"); + + basic_pixmap result({size, size}); + + for (auto idx : result.indices()) + { + result(idx) = bit_reverse(bit_interleave(idx[0] ^ idx[1], idx[0])); + } + + return result; + } + +}