Created
October 12, 2023 19:38
-
-
Save etscrivner/c0738c37907c05eefc642462a5ee4722 to your computer and use it in GitHub Desktop.
Dithering on a grayscale bitmap with 2x2, 4x4, and 8x8 Bayer matrices.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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