98 lines
3.4 KiB
C++
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;
|
|
}
|
|
}
|