Skip to content

Instantly share code, notes, and snippets.

@etscrivner
Created October 12, 2023 19:38
Show Gist options
  • Save etscrivner/c0738c37907c05eefc642462a5ee4722 to your computer and use it in GitHub Desktop.
Save etscrivner/c0738c37907c05eefc642462a5ee4722 to your computer and use it in GitHub Desktop.
Dithering on a grayscale bitmap with 2x2, 4x4, and 8x8 Bayer matrices.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "ext/stb_image_write.h"
uint8_t* GenerateGrayGradientBitmap(int width, int height) {
uint8_t* result = (uint8_t*)malloc(width * height);
for (int x = 0; x < width; ++x) {
float t = (float)x / (float)width;
uint8_t color = t * 255;
for (int y = 0; y < height; ++y) {
result[x + (y * width)] = color;
}
}
return(result);
}
void DitherBayer2x2(uint8_t* bitmap, int width, int height) {
uint8_t bayer_2x2[4] = {
0, 2,
3, 1
};
float scalar = 1.0f / 4.0f;
for (int i = 0; i < 4; ++i) {
bayer_2x2[i] = (uint8_t)(scalar * bayer_2x2[i] * 255);
}
uint8_t* pixel = bitmap;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x, ++pixel) {
int idx = ((x % 2) + ((y % 2) * 2));
uint8_t threshold = bayer_2x2[idx];
if (*pixel > threshold) {
*pixel = 0xff;
} else {
*pixel = 0x00;
}
}
}
}
void DitherBayer4x4(uint8_t* bitmap, int width, int height) {
uint8_t bayer_4x4[16] = {
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
float scalar = 1.0f / 16.0f;
for (int i = 0; i < 16; ++i) {
bayer_4x4[i] = roundf(scalar * bayer_4x4[i] * 255);
}
uint8_t* pixel = bitmap;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x, ++pixel) {
int idx = ((x % 4) + ((y % 4) * 4));
uint8_t threshold = bayer_4x4[idx];
if (*pixel > threshold) {
*pixel = 0xff;
} else {
*pixel = 0x00;
}
}
}
}
void DitherBayer8x8(uint8_t* bitmap, int width, int height) {
uint8_t bayer_8x8[64] = {
0, 32, 8, 40, 2, 34, 10, 42,
48, 16, 56, 24, 50, 18, 58, 26,
12, 44, 4, 36, 14, 46, 6, 38,
60, 28, 52, 20, 62, 30, 54, 22,
3, 35, 11, 43, 1, 33, 9, 41,
51, 19, 59, 27, 49, 17, 57, 25,
15, 47, 7, 39, 13, 45, 5, 37,
63, 31, 55, 23, 61, 29, 53, 21
};
float scalar = 1.0f / 64.0f;
for (int i = 0; i < 64; ++i) {
bayer_8x8[i] = roundf(scalar * bayer_8x8[i] * 255);
}
uint8_t* pixel = bitmap;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x, ++pixel) {
int idx = ((x % 8) + ((y % 8) * 8));
uint8_t threshold = bayer_8x8[idx];
if (*pixel > threshold){
*pixel = 0xff;
} else {
*pixel = 0x00;
}
}
}
}
int main() {
int row_height = 64;
int bitmap_w = 256, bitmap_h = row_height * 4;
uint8_t* bitmap = GenerateGrayGradientBitmap(bitmap_w, bitmap_h);
uint8_t* dither_row = bitmap + (bitmap_w * row_height);
DitherBayer2x2(dither_row, bitmap_w, row_height);
dither_row += bitmap_w * row_height;
DitherBayer4x4(dither_row, bitmap_w, row_height);
dither_row += bitmap_w * row_height;
DitherBayer8x8(dither_row, bitmap_w, row_height);
stbi_write_png("grad.png", bitmap_w, bitmap_h, 1, bitmap, bitmap_w);
free(bitmap);
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment