mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2025-12-18 15:17:42 +03:00
175 lines
5.0 KiB
C++
175 lines
5.0 KiB
C++
#include "BmpReader.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
uint16_t BmpReader::readLE16(File& f) {
|
|
const int c0 = f.read();
|
|
const int c1 = f.read();
|
|
const uint8_t b0 = (uint8_t)(c0 < 0 ? 0 : c0);
|
|
const uint8_t b1 = (uint8_t)(c1 < 0 ? 0 : c1);
|
|
return (uint16_t)b0 | ((uint16_t)b1 << 8);
|
|
}
|
|
|
|
uint32_t BmpReader::readLE32(File& f) {
|
|
const int c0 = f.read();
|
|
const int c1 = f.read();
|
|
const int c2 = f.read();
|
|
const int c3 = f.read();
|
|
|
|
const uint8_t b0 = (uint8_t)(c0 < 0 ? 0 : c0);
|
|
const uint8_t b1 = (uint8_t)(c1 < 0 ? 0 : c1);
|
|
const uint8_t b2 = (uint8_t)(c2 < 0 ? 0 : c2);
|
|
const uint8_t b3 = (uint8_t)(c3 < 0 ? 0 : c3);
|
|
|
|
return (uint32_t)b0 | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 16) | ((uint32_t)b3 << 24);
|
|
}
|
|
|
|
void BmpReader::freeMonoBitmap(MonoBitmap& bmp) {
|
|
if (bmp.data) {
|
|
free(bmp.data);
|
|
bmp.data = nullptr;
|
|
}
|
|
bmp.width = 0;
|
|
bmp.height = 0;
|
|
bmp.len = 0;
|
|
}
|
|
|
|
const char* BmpReader::errorToString(BmpReaderError err) {
|
|
switch (err) {
|
|
case BmpReaderError::Ok:
|
|
return "Ok";
|
|
case BmpReaderError::FileInvalid:
|
|
return "FileInvalid";
|
|
case BmpReaderError::SeekStartFailed:
|
|
return "SeekStartFailed";
|
|
case BmpReaderError::NotBMP:
|
|
return "NotBMP (missing 'BM')";
|
|
case BmpReaderError::DIBTooSmall:
|
|
return "DIBTooSmall (<40 bytes)";
|
|
case BmpReaderError::BadPlanes:
|
|
return "BadPlanes (!= 1)";
|
|
case BmpReaderError::UnsupportedBpp:
|
|
return "UnsupportedBpp (expected 24)";
|
|
case BmpReaderError::UnsupportedCompression:
|
|
return "UnsupportedCompression (expected BI_RGB)";
|
|
case BmpReaderError::BadDimensions:
|
|
return "BadDimensions";
|
|
case BmpReaderError::SeekPixelDataFailed:
|
|
return "SeekPixelDataFailed";
|
|
case BmpReaderError::OomOutput:
|
|
return "OomOutput";
|
|
case BmpReaderError::OomRowBuffer:
|
|
return "OomRowBuffer";
|
|
case BmpReaderError::ShortReadRow:
|
|
return "ShortReadRow";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
BmpReaderError BmpReader::convert24BitRotate90CCW(File& file, MonoBitmap& out, uint8_t threshold) {
|
|
return convert24BitImpl(file, out, threshold, true);
|
|
}
|
|
|
|
BmpReaderError BmpReader::convert24BitImpl(File& f, MonoBitmap& out, uint8_t threshold, bool rotate90CCW) {
|
|
freeMonoBitmap(out);
|
|
|
|
if (!f) return BmpReaderError::FileInvalid;
|
|
if (!f.seek(0)) return BmpReaderError::SeekStartFailed;
|
|
|
|
// --- BMP FILE HEADER ---
|
|
const uint16_t bfType = readLE16(f);
|
|
if (bfType != 0x4D42) return BmpReaderError::NotBMP;
|
|
|
|
(void)readLE32(f);
|
|
(void)readLE16(f);
|
|
(void)readLE16(f);
|
|
const uint32_t bfOffBits = readLE32(f);
|
|
|
|
// --- DIB HEADER ---
|
|
const uint32_t biSize = readLE32(f);
|
|
if (biSize < 40) return BmpReaderError::DIBTooSmall;
|
|
|
|
const int32_t srcW = (int32_t)readLE32(f);
|
|
int32_t srcHRaw = (int32_t)readLE32(f);
|
|
const uint16_t planes = readLE16(f);
|
|
const uint16_t bpp = readLE16(f);
|
|
const uint32_t comp = readLE32(f);
|
|
|
|
if (planes != 1) return BmpReaderError::BadPlanes;
|
|
if (bpp != 24) return BmpReaderError::UnsupportedBpp;
|
|
if (comp != 0) return BmpReaderError::UnsupportedCompression;
|
|
|
|
(void)readLE32(f);
|
|
(void)readLE32(f);
|
|
(void)readLE32(f);
|
|
(void)readLE32(f);
|
|
(void)readLE32(f);
|
|
|
|
if (srcW <= 0) return BmpReaderError::BadDimensions;
|
|
|
|
const bool topDown = (srcHRaw < 0);
|
|
const int32_t srcH = topDown ? -srcHRaw : srcHRaw;
|
|
if (srcH <= 0) return BmpReaderError::BadDimensions;
|
|
|
|
// Output dimensions
|
|
out.width = rotate90CCW ? (int)srcH : (int)srcW;
|
|
out.height = rotate90CCW ? (int)srcW : (int)srcH;
|
|
|
|
const size_t outBytesPerRow = (size_t)(out.width + 7) / 8;
|
|
out.len = outBytesPerRow * (size_t)out.height;
|
|
|
|
out.data = (uint8_t*)malloc(out.len);
|
|
if (!out.data) return BmpReaderError::OomOutput;
|
|
memset(out.data, 0xFF, out.len);
|
|
|
|
// Source row stride (padded to 4 bytes)
|
|
const uint32_t srcBytesPerRow24 = (uint32_t)srcW * 3u;
|
|
const uint32_t srcRowStride = (srcBytesPerRow24 + 3u) & ~3u;
|
|
|
|
if (!f.seek(bfOffBits)) {
|
|
freeMonoBitmap(out);
|
|
return BmpReaderError::SeekPixelDataFailed;
|
|
}
|
|
|
|
uint8_t* rowBuf = (uint8_t*)malloc(srcRowStride);
|
|
if (!rowBuf) {
|
|
freeMonoBitmap(out);
|
|
return BmpReaderError::OomRowBuffer;
|
|
}
|
|
|
|
for (int fileRow = 0; fileRow < (int)srcH; fileRow++) {
|
|
if (f.read(rowBuf, srcRowStride) != (int)srcRowStride) {
|
|
free(rowBuf);
|
|
freeMonoBitmap(out);
|
|
return BmpReaderError::ShortReadRow;
|
|
}
|
|
|
|
const int srcY = topDown ? fileRow : ((int)srcH - 1 - fileRow);
|
|
|
|
for (int srcX = 0; srcX < (int)srcW; srcX++) {
|
|
const uint8_t b = rowBuf[srcX * 3 + 0];
|
|
const uint8_t g = rowBuf[srcX * 3 + 1];
|
|
const uint8_t r = rowBuf[srcX * 3 + 2];
|
|
|
|
const uint8_t lum = (uint8_t)((77u * r + 150u * g + 29u * b) >> 8);
|
|
bool isBlack = (lum < threshold);
|
|
|
|
int outX, outY;
|
|
if (!rotate90CCW) {
|
|
outX = srcX;
|
|
outY = srcY;
|
|
} else {
|
|
// 90° counter-clockwise: (x,y) -> (y, w-1-x)
|
|
outX = srcY;
|
|
outY = (int)srcW - 1 - srcX;
|
|
}
|
|
|
|
setMonoPixel(out.data, out.width, outX, outY, isBlack);
|
|
}
|
|
}
|
|
|
|
free(rowBuf);
|
|
return BmpReaderError::Ok;
|
|
}
|