88 lines
2.9 KiB
C
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);
|
|
}
|