mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2025-12-16 22:27:42 +03:00
add support for 1, 8 and 32 bit bmps
This commit is contained in:
parent
9f51a31677
commit
dc2a6ca2f8
@ -50,9 +50,9 @@ const char* BmpReader::errorToString(BmpReaderError err) {
|
||||
case BmpReaderError::BadPlanes:
|
||||
return "BadPlanes (!= 1)";
|
||||
case BmpReaderError::UnsupportedBpp:
|
||||
return "UnsupportedBpp (expected 24)";
|
||||
return "UnsupportedBpp (expected 24, 32 or 1)";
|
||||
case BmpReaderError::UnsupportedCompression:
|
||||
return "UnsupportedCompression (expected BI_RGB)";
|
||||
return "UnsupportedCompression (expected BI_RGB or BI_BITFIELDS for 32bpp)";
|
||||
case BmpReaderError::BadDimensions:
|
||||
return "BadDimensions";
|
||||
case BmpReaderError::SeekPixelDataFailed:
|
||||
@ -67,44 +67,45 @@ const char* BmpReader::errorToString(BmpReaderError err) {
|
||||
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) {
|
||||
BmpReaderError BmpReader::read(File& file, MonoBitmap& out, uint8_t threshold) {
|
||||
freeMonoBitmap(out);
|
||||
|
||||
if (!f) return BmpReaderError::FileInvalid;
|
||||
if (!f.seek(0)) return BmpReaderError::SeekStartFailed;
|
||||
if (!file) return BmpReaderError::FileInvalid;
|
||||
if (!file.seek(0)) return BmpReaderError::SeekStartFailed;
|
||||
|
||||
// --- BMP FILE HEADER ---
|
||||
const uint16_t bfType = readLE16(f);
|
||||
const uint16_t bfType = readLE16(file);
|
||||
if (bfType != 0x4D42) return BmpReaderError::NotBMP;
|
||||
|
||||
(void)readLE32(f);
|
||||
(void)readLE16(f);
|
||||
(void)readLE16(f);
|
||||
const uint32_t bfOffBits = readLE32(f);
|
||||
(void)readLE32(file);
|
||||
(void)readLE16(file);
|
||||
(void)readLE16(file);
|
||||
const uint32_t bfOffBits = readLE32(file);
|
||||
|
||||
// --- DIB HEADER ---
|
||||
const uint32_t biSize = readLE32(f);
|
||||
const uint32_t biSize = readLE32(file);
|
||||
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);
|
||||
const int32_t srcW = (int32_t)readLE32(file);
|
||||
int32_t srcHRaw = (int32_t)readLE32(file);
|
||||
const uint16_t planes = readLE16(file);
|
||||
const uint16_t bpp = readLE16(file);
|
||||
const uint32_t comp = readLE32(file);
|
||||
const bool is24Bit = (bpp == 24);
|
||||
const bool is32Bit = (bpp == 32);
|
||||
const bool is8Bit = (bpp == 8);
|
||||
const bool is1Bit = (bpp == 1);
|
||||
|
||||
if (planes != 1) return BmpReaderError::BadPlanes;
|
||||
if (bpp != 24) return BmpReaderError::UnsupportedBpp;
|
||||
if (comp != 0) return BmpReaderError::UnsupportedCompression;
|
||||
if (!is24Bit && !is32Bit && !is8Bit && !is1Bit) return BmpReaderError::UnsupportedBpp;
|
||||
// Allow BI_RGB (0) for all, and BI_BITFIELDS (3) for 32bpp which is common for BGRA masks.
|
||||
if (!(comp == 0 || (is32Bit && comp == 3))) return BmpReaderError::UnsupportedCompression;
|
||||
|
||||
(void)readLE32(f);
|
||||
(void)readLE32(f);
|
||||
(void)readLE32(f);
|
||||
(void)readLE32(f);
|
||||
(void)readLE32(f);
|
||||
(void)readLE32(file); // biSizeImage
|
||||
(void)readLE32(file); // biXPelsPerMeter
|
||||
(void)readLE32(file); // biYPelsPerMeter
|
||||
const uint32_t clrUsed = readLE32(file);
|
||||
(void)readLE32(file); // biClrImportant
|
||||
|
||||
if (srcW <= 0) return BmpReaderError::BadDimensions;
|
||||
|
||||
@ -112,9 +113,9 @@ BmpReaderError BmpReader::convert24BitImpl(File& f, MonoBitmap& out, uint8_t thr
|
||||
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;
|
||||
// Output dimensions after 90° CCW rotation
|
||||
out.width = (int)srcH;
|
||||
out.height = (int)srcW;
|
||||
|
||||
const size_t outBytesPerRow = (size_t)(out.width + 7) / 8;
|
||||
out.len = outBytesPerRow * (size_t)out.height;
|
||||
@ -123,11 +124,38 @@ BmpReaderError BmpReader::convert24BitImpl(File& f, MonoBitmap& out, uint8_t thr
|
||||
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;
|
||||
// Palette for 8-bit indexed images
|
||||
uint8_t paletteLum[256];
|
||||
if (is8Bit) {
|
||||
for (int i = 0; i < 256; i++) paletteLum[i] = (uint8_t)i; // default grayscale ramp
|
||||
uint32_t paletteCount = (clrUsed == 0) ? 256u : clrUsed;
|
||||
if (paletteCount > 256u) paletteCount = 256u;
|
||||
for (uint32_t i = 0; i < paletteCount; i++) {
|
||||
const int b = file.read();
|
||||
const int g = file.read();
|
||||
const int r = file.read();
|
||||
(void)file.read(); // reserved
|
||||
const uint8_t bb = (uint8_t)(b < 0 ? 0 : b);
|
||||
const uint8_t gg = (uint8_t)(g < 0 ? 0 : g);
|
||||
const uint8_t rr = (uint8_t)(r < 0 ? 0 : r);
|
||||
paletteLum[i] = (uint8_t)((77u * rr + 150u * gg + 29u * bb) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (!f.seek(bfOffBits)) {
|
||||
// Source row stride (padded to 4 bytes)
|
||||
uint32_t bytesPerPixel = 0u;
|
||||
if (is8Bit) {
|
||||
bytesPerPixel = 1u;
|
||||
} else if (is32Bit) {
|
||||
bytesPerPixel = 4u;
|
||||
} else if (is24Bit) {
|
||||
bytesPerPixel = 3u;
|
||||
}
|
||||
const uint32_t srcBytesPerRow =
|
||||
is1Bit ? ((uint32_t)srcW + 7u) / 8u : (uint32_t)srcW * bytesPerPixel; // bpp==1 ignores bytesPerPixel
|
||||
const uint32_t srcRowStride = (srcBytesPerRow + 3u) & ~3u;
|
||||
|
||||
if (!file.seek(bfOffBits)) {
|
||||
freeMonoBitmap(out);
|
||||
return BmpReaderError::SeekPixelDataFailed;
|
||||
}
|
||||
@ -139,7 +167,7 @@ BmpReaderError BmpReader::convert24BitImpl(File& f, MonoBitmap& out, uint8_t thr
|
||||
}
|
||||
|
||||
for (int fileRow = 0; fileRow < (int)srcH; fileRow++) {
|
||||
if (f.read(rowBuf, srcRowStride) != (int)srcRowStride) {
|
||||
if (file.read(rowBuf, srcRowStride) != (int)srcRowStride) {
|
||||
free(rowBuf);
|
||||
freeMonoBitmap(out);
|
||||
return BmpReaderError::ShortReadRow;
|
||||
@ -148,23 +176,31 @@ BmpReaderError BmpReader::convert24BitImpl(File& f, MonoBitmap& out, uint8_t thr
|
||||
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;
|
||||
bool isBlack;
|
||||
if (is1Bit) {
|
||||
const uint8_t byte = rowBuf[srcX >> 3];
|
||||
const uint8_t mask = (uint8_t)(0x80u >> (srcX & 7));
|
||||
const bool bitSet = (byte & mask) != 0;
|
||||
// In 1bpp BMPs, palette index 0 is conventionally black and index 1 is white.
|
||||
isBlack = !bitSet;
|
||||
} else if (is8Bit) {
|
||||
const uint8_t idx = rowBuf[srcX];
|
||||
const uint8_t lum = paletteLum[idx];
|
||||
isBlack = (lum < threshold);
|
||||
} else {
|
||||
// 90° counter-clockwise: (x,y) -> (y, w-1-x)
|
||||
outX = srcY;
|
||||
outY = (int)srcW - 1 - srcX;
|
||||
const uint8_t* px = &rowBuf[srcX * bytesPerPixel];
|
||||
const uint8_t b = px[0];
|
||||
const uint8_t g = px[1];
|
||||
const uint8_t r = px[2];
|
||||
|
||||
const uint8_t lum = (uint8_t)((77u * r + 150u * g + 29u * b) >> 8);
|
||||
isBlack = (lum < threshold);
|
||||
}
|
||||
|
||||
// 90° counter-clockwise: (x,y) -> (y, w-1-x)
|
||||
const int outX = srcY;
|
||||
const int outY = (int)srcW - 1 - srcX;
|
||||
|
||||
setMonoPixel(out.data, out.width, outX, outY, isBlack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,8 @@ class BmpReader {
|
||||
public:
|
||||
// Rotate 90° counter-clockwise: (w,h) -> (h,w)
|
||||
// Used for converting portrait BMP (480x800) into landscape framebuffer (800x480)
|
||||
static BmpReaderError convert24BitRotate90CCW(File& file, MonoBitmap& out, uint8_t threshold = 160);
|
||||
// Supports 8-bit, 24-bit, 32-bit color and 1-bit monochrome BMPs.
|
||||
static BmpReaderError read(File& file, MonoBitmap& out, uint8_t threshold = 160);
|
||||
|
||||
static void freeMonoBitmap(MonoBitmap& bmp);
|
||||
static const char* errorToString(BmpReaderError err);
|
||||
@ -53,6 +54,4 @@ class BmpReader {
|
||||
else
|
||||
buf[idx] |= mask;
|
||||
}
|
||||
|
||||
static BmpReaderError convert24BitImpl(File& file, MonoBitmap& out, uint8_t threshold, bool rotate90CCW);
|
||||
};
|
||||
|
||||
@ -130,7 +130,7 @@ bool GfxRenderer::drawFullScreenBmp(File& file) {
|
||||
file.seek(0); // Ensure we're at the start of the file
|
||||
|
||||
MonoBitmap bmp;
|
||||
auto err = BmpReader::convert24BitRotate90CCW(file, bmp);
|
||||
auto err = BmpReader::read(file, bmp);
|
||||
|
||||
if (err != BmpReaderError::Ok) {
|
||||
Serial.printf("[%lu] [GFX] BMP convert failed: %s\n", millis(), BmpReader::errorToString(err));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user