Skip to content

Instantly share code, notes, and snippets.

@qookei
Created August 4, 2020 21:08
Show Gist options
  • Save qookei/e3d0c71656b0ed10bc2ecc22f7de5e7c to your computer and use it in GitHub Desktop.
Save qookei/e3d0c71656b0ed10bc2ecc22f7de5e7c to your computer and use it in GitHub Desktop.
Incomplete single header C BMP loader
#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