Bitmap auslesen mit C

Als Teilaufgabe einer Teilaufgabe in einem Projekt, musste ich in C (eigentlich in C++) schlicht ein Bitmap auslesen und in ein Array speichern. Da ich inzwischen beim Jonglieren von Bits relativ routiniert bin, dachte ich mir „Kein Problem!“, habe mir den Wikipedia-Eintrag zum Dateiformat aufgemacht (Link) und los gelegt. Eigentlich habe ich damit gerechnet ein paar Strukturen anzulegen und diese einfach mit fread(…) zu beladen usw. Einige nervenraubende Stunden später ist es mir dann gelungen das Ganze zum Laufen zu kriegen.

Vielleicht kann der Code dem ein oder anderen helfen:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

unsigned char getBit(unsigned char value, char offset);
unsigned getBits(unsigned char *data, unsigned offset, char length);
unsigned getBitmapWidth(const char *path);
unsigned getBitmapHeight(const char *path);
void readBitmap(const char *path, unsigned *data, unsigned w, unsigned h);

int main(int argc, char **argv)
{
	const char *path = "C:\\Users\\Martin\\Desktop\\face.bmp";
	unsigned width = getBitmapWidth(path);
	unsigned height = getBitmapHeight(path);
	unsigned *data = (unsigned *)malloc(sizeof(unsigned)*width*height);
	readBitmap(path, data, width, height);
	
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			for (int z = 0; z < 2; z++)
				printf("%c", data[x + y*width] == 0 ? 219 : ' ');
		}
		printf("\n");
	}
	printf("\n");
	
	free(data);

	return 0;
}

unsigned getBits(unsigned char *data, unsigned offset, char length)
{
	unsigned result = 0;
	int start = offset / 8;
	int inner = offset % 8;

	for (int i = 0; i < length; i++)
	{
		unsigned char bit = getBit(data[start], 7 - inner);
		result <<= 1;
		result |= bit;

		inner++;
		if (inner % 8 == 0)
		{
			start++;
			inner = 0;
		}
	}

	return result;
}

unsigned char getBit(unsigned char value, char offset)
{
	return (value & (1 << offset)) ? 1 : 0;
}

unsigned getBitmapWidth(const char *path)
{
	FILE *bitmap;

	unsigned width = 0;

	bitmap = fopen(path, "rb");
	if (bitmap != NULL)
	{
		int x, y, z;
		unsigned char header[14];
		unsigned char dibhed[16];

		fread(&header, sizeof(header), 1, bitmap);
		fseek(bitmap, 0x0e, SEEK_SET);
		fread(&dibhed, sizeof(dibhed), 1, bitmap);

		width = dibhed[7] << 24 | dibhed[6] << 16 | dibhed[5] << 8 | dibhed[4];

		fclose(bitmap);
	}

	return width;
}

unsigned getBitmapHeight(const char *path)
{
	FILE *bitmap;

	unsigned height = 0;

	bitmap = fopen(path, "rb");
	if (bitmap != NULL)
	{
		int x, y, z;
		unsigned char header[14];
		unsigned char dibhed[16];

		fread(&header, sizeof(header), 1, bitmap);
		fseek(bitmap, 0x0e, SEEK_SET);
		fread(&dibhed, sizeof(dibhed), 1, bitmap);

		height = dibhed[11]<<24 | dibhed[10]<<16 | dibhed[9]<<8 | dibhed[8];

		fclose(bitmap);
	}

	return height;
}

void readBitmap(const char *path, unsigned *data, unsigned w, unsigned h)
{
	FILE *bitmap;

	bitmap = fopen(path, "rb");
	if (bitmap != NULL)
	{
		int x, y, z;
		unsigned char header[14];
		unsigned char dibhed[16];

		fread(&header, sizeof(header), 1, bitmap);
		fseek(bitmap, 0x0e, SEEK_SET);
		fread(&dibhed, sizeof(dibhed), 1, bitmap);

		unsigned offset = header[13]<<24 | header[12]<<16 | header[11]<<8 | header[10];
		unsigned width = dibhed[7]<<24 | dibhed[6]<<16 | dibhed[5]<<8 | dibhed[4];
		unsigned height = dibhed[11]<<24 | dibhed[10]<<16 | dibhed[9]<<8 | dibhed[8];
		unsigned bitsPerPixel = dibhed[15]<<8 | dibhed[14];
		unsigned bytesPerRow = (unsigned)((floor((bitsPerPixel*width + 31) / 32.0)) * 4);

		fseek(bitmap, offset, SEEK_SET);
		unsigned char *raw = (unsigned char *)malloc(sizeof(unsigned char)*height*bytesPerRow);
		fread(raw, 1, bytesPerRow*height, bitmap);
		fclose(bitmap);

		if (width != w || height != h)
			return;
		unsigned dibsize = dibhed[3]<<24 | dibhed[2]<<16 | dibhed[1]<<8 | dibhed[0];

		unsigned o = 0;

		for (y = 0; y < height; y++)
		{
			for (x = 0; x < width; x++)
			{
				unsigned v = getBits(raw, o, (char)bitsPerPixel);
				data[x + (height-1-y)*width] = v;
				o += bitsPerPixel;
			}
			o += (bytesPerRow * 8) - (bitsPerPixel * width);
		}

		free(raw);
	}
}