dosgame1/util/Bmp.cpp

98 lines
3.4 KiB
C++

#include "Bmp.h"
#include <cstdio>
#include "Files.h"
namespace Bmp {
Bitmap loadFromMemory(void *buf, size_t len) {
if (len < sizeof (BmpFileHeader) + sizeof (BmpInfoHeader)) return {};
auto fileHeader = reinterpret_cast<BmpFileHeader *>(buf);
auto infoHeader = reinterpret_cast<BmpInfoHeader *>(fileHeader + 1);
if (fileHeader->signature != 0x4d42) {
printf("Invalid signature\n");
return {};
}
if (len < fileHeader->fileSize) {
printf("File too small\n");
return {};
}
if (infoHeader->headerSize != 40) {
printf("Invalid header size\n");
return {};
}
if (infoHeader->width < 0 || infoHeader->height < 0 || infoHeader->planes != 1) {
printf("Invalid width/height/bit planes\n");
return {};
}
if (infoHeader->bitsPerPixel != 1 && infoHeader->bitsPerPixel != 4 && infoHeader->bitsPerPixel != 8) {
printf("Unsupported bpp %u\n", infoHeader->bitsPerPixel);
return {};
}
if (infoHeader->compression != BmpInfoHeader::CompressionType::Rgb) {
printf("Unsupported compression\n");
return {};
}
// TODO check imageSize
Bitmap bitmap(infoHeader->width, infoHeader->height);
// Read color table
auto colorTable = reinterpret_cast<uint8_t *>(infoHeader + 1);
for (auto i = 0; i < infoHeader->colorsUsed; i++) {
Video::PaletteEntry paletteEntry;
paletteEntry.b = colorTable[4 * i + 0];
paletteEntry.g = colorTable[4 * i + 1];
paletteEntry.r = colorTable[4 * i + 2];
bitmap.SetPaletteEntry(i, paletteEntry);
}
// Read pixel data
auto data = static_cast<uint8_t *>(buf) + fileHeader->offset;
auto rowLength = infoHeader->width;
if (infoHeader->bitsPerPixel == 1) rowLength = (rowLength + 7) >> 3;
else if (infoHeader->bitsPerPixel == 4) rowLength = (rowLength + 1) >> 1;
auto stride = (rowLength + 3) >> 2 << 2;
for (auto y = 0u; y < infoHeader->height; y++) {
for (auto x = 0u; x < infoHeader->width; x++) {
uint8_t pv = 0;
switch (infoHeader->bitsPerPixel) {
case 1: {
const auto byte = data[(infoHeader->height - y - 1) * stride + (x >> 3)];
auto pixelOffset = x & 7;
pv = (byte >> (7 - pixelOffset)) & 1;
break;
}
case 4: {
const auto byte = data[(infoHeader->height - y - 1) * stride + (x >> 1)];
pv = x & 1 ? byte & 0xf : byte >> 4;
break;
}
case 8: {
pv = data[(infoHeader->height - y - 1) * stride + x];
break;
}
}
bitmap.SetPixel(x, y, pv);
}
}
return bitmap;
}
Bitmap loadFromFile(const char* path) {
void *data;
auto len = Files::allocateBufferAndLoadFromFile(path, &data);
auto bitmap = loadFromMemory(data, len);
Files::deleteBuffer(data);
return bitmap;
}
}