88 lines
2.9 KiB
C

#include "bmp_loader.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static uint16_t le16(const unsigned char* data) {
return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
}
static uint32_t le32(const unsigned char* data) {
return (uint32_t)data[0] | ((uint32_t)data[1] << 8) |
((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
}
int load_bmp_rgb(const char* path, unsigned char** output, int* width, int* height) {
if (!path || !output || !width || !height) return -1;
FILE* file = fopen(path, "rb");
if (!file) return -2;
unsigned char header[54];
if (fread(header, 1, sizeof(header), file) != sizeof(header) || header[0] != 'B' || header[1] != 'M') {
fclose(file);
return -3;
}
const uint32_t pixel_offset = le32(header + 10);
const int32_t source_width = (int32_t)le32(header + 18);
const int32_t signed_height = (int32_t)le32(header + 22);
const uint16_t planes = le16(header + 26);
const uint16_t bits = le16(header + 28);
const uint32_t compression = le32(header + 30);
if (source_width <= 0 || signed_height == 0 || planes != 1 ||
(bits != 24 && bits != 32) || compression != 0) {
fclose(file);
return -4;
}
const int source_height = signed_height < 0 ? -signed_height : signed_height;
const int top_down = signed_height < 0;
const size_t source_stride = (((size_t)source_width * bits + 31u) / 32u) * 4u;
const size_t source_size = source_stride * (size_t)source_height;
const size_t rgb_size = (size_t)source_width * (size_t)source_height * 3u;
if (source_size == 0 || rgb_size == 0 || source_size > 1024u * 1024u * 1024u) {
fclose(file);
return -5;
}
unsigned char* source = (unsigned char*)malloc(source_size);
unsigned char* rgb = (unsigned char*)malloc(rgb_size);
if (!source || !rgb) {
free(source);
free(rgb);
fclose(file);
return -6;
}
if (fseek(file, (long)pixel_offset, SEEK_SET) != 0 || fread(source, 1, source_size, file) != source_size) {
free(source);
free(rgb);
fclose(file);
return -7;
}
fclose(file);
const int bytes_per_pixel = bits / 8;
for (int y = 0; y < source_height; ++y) {
const int source_y = top_down ? y : source_height - 1 - y;
const unsigned char* row = source + (size_t)source_y * source_stride;
unsigned char* target = rgb + (size_t)y * (size_t)source_width * 3u;
for (int x = 0; x < source_width; ++x) {
const unsigned char* pixel = row + (size_t)x * (size_t)bytes_per_pixel;
target[x * 3] = pixel[2];
target[x * 3 + 1] = pixel[1];
target[x * 3 + 2] = pixel[0];
}
}
free(source);
*output = rgb;
*width = source_width;
*height = source_height;
return 0;
}
void free_bmp_rgb(unsigned char* pixels) {
free(pixels);
}