Created
August 4, 2020 21:08
-
-
Save qookei/e3d0c71656b0ed10bc2ecc22f7de5e7c to your computer and use it in GitHub Desktop.
Incomplete single header C BMP loader
This file contains hidden or 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
#ifndef BMPLDR_H | |
#define BMPLDR_H | |
#include <stdint.h> | |
#include <stddef.h> | |
#include <stdbool.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
// verifies if the bmp file is correct | |
bool bmpldr_verify(void *bmp); | |
// returns the bitmap's size and bit depth | |
void bmpldr_get_size(void *bmp, int *w, int *h, int *bpp); | |
// loads the bitmap pixel data into dst, always in RGBA | |
// dst must be of size (width * height * 4) | |
void bmpldr_load(void *bmp, void *dst); | |
#ifdef BMPLDR_IMPLEMENTATION | |
#define READ_OFFSET(x, y, offset) *((x *)(&(y[offset]))) | |
#define ABS(x) ((x) > 0 ? (x) : -(x)) | |
#define COUNT_LEADING(x,y) {while(!((x>>y) & 1))y++;} | |
bool bmpldr_verify(void *bmp) { | |
uint8_t *data = (uint8_t *)bmp; | |
if (data[0] != 0x42 || data[1] != 0x4D) | |
return false; // header doesn't match | |
if (READ_OFFSET(uint32_t, data, 0x0E) < 40) | |
return false; // not BITMAPINFOHEADER or later | |
if (READ_OFFSET(uint32_t, data, 0x1E) != 0 && | |
READ_OFFSET(uint32_t, data, 0x1E) != 3) | |
return false; // not RGB/RGBA, it's compressed or whatever | |
return true; | |
} | |
void bmpldr_get_size(void *bmp, int *w, int *h, int *bpp) { | |
if (!bmpldr_verify(bmp)) | |
return; | |
uint8_t *data = (uint8_t *)bmp; | |
if (w) | |
*w = READ_OFFSET(int, data, 0x12); | |
if (h) | |
*h = ABS(READ_OFFSET(int, data, 0x16)); | |
if (bpp) | |
*bpp = READ_OFFSET(uint16_t, data, 0x1C); | |
} | |
void bmpldr_load(void *bmp, void *dst) { | |
if (!bmpldr_verify(bmp)) | |
return; | |
uint8_t *data = (uint8_t *)bmp; | |
int w = READ_OFFSET(int, data, 0x12); | |
int h = READ_OFFSET(int, data, 0x16); | |
uint32_t bpp = READ_OFFSET(uint16_t, data, 0x1C); | |
uint8_t *bmp_pixels = data + READ_OFFSET(uint32_t, data, 0x0A); | |
uint8_t *dst_pixels = (uint8_t *)dst; | |
uint32_t row_size = (bpp * w + 31) / 32 * 4; | |
int abs_h = ABS(h); | |
uint32_t rmask = READ_OFFSET(uint32_t, data, 0x36); | |
uint32_t gmask = READ_OFFSET(uint32_t, data, 0x3A); | |
uint32_t bmask = READ_OFFSET(uint32_t, data, 0x3E); | |
uint32_t amask = READ_OFFSET(uint32_t, data, 0x42); | |
bool mask = READ_OFFSET(uint32_t, data, 0x1E) == 3; | |
for (int y = 0; y < abs_h; y++) { | |
for (int x = 0; x < w; x++) { | |
uint32_t dst_off = (y * w + x) * 4; | |
uint32_t bmp_off; | |
if (h > 0) | |
bmp_off = row_size * (abs_h - 1 - y) + x * bpp / 8; | |
else | |
bmp_off = row_size * y + x * bpp / 8; | |
uint32_t bmp_r = bmp_pixels[bmp_off + 2]; | |
uint32_t bmp_g = bmp_pixels[bmp_off + 1]; | |
uint32_t bmp_b = bmp_pixels[bmp_off + 0]; | |
uint32_t bmp_a = bmp_pixels[bmp_off + 3]; | |
if (mask) { | |
uint32_t dword_px = *(uint32_t *)(&(bmp_pixels[bmp_off])); | |
uint32_t r_zero = 0, g_zero = 0, | |
b_zero = 0, a_zero = 0; | |
COUNT_LEADING(rmask, r_zero); | |
COUNT_LEADING(gmask, g_zero); | |
COUNT_LEADING(bmask, b_zero); | |
COUNT_LEADING(amask, a_zero); | |
bmp_r = (dword_px & rmask) >> r_zero; | |
bmp_g = (dword_px & gmask) >> g_zero; | |
bmp_b = (dword_px & bmask) >> b_zero; | |
bmp_a = (dword_px & amask) >> a_zero; | |
} | |
dst_pixels[dst_off + 0] = bmp_r; // R | |
dst_pixels[dst_off + 1] = bmp_g; // G | |
dst_pixels[dst_off + 2] = bmp_b; // B | |
if (bpp == 32) | |
dst_pixels[dst_off + 3] = bmp_a; // A | |
else | |
dst_pixels[dst_off + 3] = 0xFF; // opaque | |
} | |
} | |
} | |
#endif | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif // BMPLDR_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment