rotate fullscreen bmp CCW instead of CW

This commit is contained in:
Sam Davis 2025-12-14 19:50:05 +11:00
parent 602d3da3a2
commit d60378719c
4 changed files with 26 additions and 35 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
.pio .pio
.idea .idea
.DS_Store .DS_Store
.vscode .vscode

View File

@ -3,7 +3,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
uint16_t BmpToMono::readLE16(fs::File& f) { uint16_t BmpToMono::readLE16(File& f) {
const int c0 = f.read(); const int c0 = f.read();
const int c1 = f.read(); const int c1 = f.read();
const uint8_t b0 = (uint8_t)(c0 < 0 ? 0 : c0); const uint8_t b0 = (uint8_t)(c0 < 0 ? 0 : c0);
@ -11,7 +11,7 @@ uint16_t BmpToMono::readLE16(fs::File& f) {
return (uint16_t)b0 | ((uint16_t)b1 << 8); return (uint16_t)b0 | ((uint16_t)b1 << 8);
} }
uint32_t BmpToMono::readLE32(fs::File& f) { uint32_t BmpToMono::readLE32(File& f) {
const int c0 = f.read(); const int c0 = f.read();
const int c1 = f.read(); const int c1 = f.read();
const int c2 = f.read(); const int c2 = f.read();
@ -67,15 +67,11 @@ const char* BmpToMono::errorToString(BmpToMonoError err) {
return "Unknown"; return "Unknown";
} }
BmpToMonoError BmpToMono::convert24(fs::File& file, MonoBitmap& out, uint8_t threshold, bool invert) { BmpToMonoError BmpToMono::convert24BitRotate90CCW(File& file, MonoBitmap& out, uint8_t threshold) {
return convert24Impl(file, out, threshold, invert, false); return convert24BitImpl(file, out, threshold, true);
} }
BmpToMonoError BmpToMono::convert24Rotate90CW(fs::File& file, MonoBitmap& out, uint8_t threshold, bool invert) { BmpToMonoError BmpToMono::convert24BitImpl(File& f, MonoBitmap& out, uint8_t threshold, bool rotate90CCW) {
return convert24Impl(file, out, threshold, invert, true);
}
BmpToMonoError BmpToMono::convert24Impl(fs::File& f, MonoBitmap& out, uint8_t threshold, bool invert, bool rotate90CW) {
freeMonoBitmap(out); freeMonoBitmap(out);
if (!f) return BmpToMonoError::FileInvalid; if (!f) return BmpToMonoError::FileInvalid;
@ -117,8 +113,8 @@ BmpToMonoError BmpToMono::convert24Impl(fs::File& f, MonoBitmap& out, uint8_t th
if (srcH <= 0) return BmpToMonoError::BadDimensions; if (srcH <= 0) return BmpToMonoError::BadDimensions;
// Output dimensions // Output dimensions
out.width = rotate90CW ? (int)srcH : (int)srcW; out.width = rotate90CCW ? (int)srcH : (int)srcW;
out.height = rotate90CW ? (int)srcW : (int)srcH; out.height = rotate90CCW ? (int)srcW : (int)srcH;
const size_t outBytesPerRow = (size_t)(out.width + 7) / 8; const size_t outBytesPerRow = (size_t)(out.width + 7) / 8;
out.len = outBytesPerRow * (size_t)out.height; out.len = outBytesPerRow * (size_t)out.height;
@ -158,16 +154,15 @@ BmpToMonoError BmpToMono::convert24Impl(fs::File& f, MonoBitmap& out, uint8_t th
const uint8_t lum = (uint8_t)((77u * r + 150u * g + 29u * b) >> 8); const uint8_t lum = (uint8_t)((77u * r + 150u * g + 29u * b) >> 8);
bool isBlack = (lum < threshold); bool isBlack = (lum < threshold);
if (invert) isBlack = !isBlack;
int outX, outY; int outX, outY;
if (!rotate90CW) { if (!rotate90CCW) {
outX = srcX; outX = srcX;
outY = srcY; outY = srcY;
} else { } else {
// 90° clockwise: (x,y) -> (h-1-y, x) // 90° counter-clockwise: (x,y) -> (y, w-1-x)
outX = (int)srcH - 1 - srcY; outX = srcY;
outY = srcX; outY = (int)srcW - 1 - srcX;
} }
setMonoPixel(out.data, out.width, outX, outY, isBlack); setMonoPixel(out.data, out.width, outX, outY, isBlack);

View File

@ -32,20 +32,16 @@ enum class BmpToMonoError : uint8_t {
class BmpToMono { class BmpToMono {
public: public:
// No rotation: output size == BMP size // Rotate 90° counter-clockwise: (w,h) -> (h,w)
static BmpToMonoError convert24(fs::File& file, MonoBitmap& out, uint8_t threshold = 160, bool invert = false); // Used for converting portrait BMP (480x800) into landscape framebuffer (800x480)
static BmpToMonoError convert24BitRotate90CCW(File& file, MonoBitmap& out, uint8_t threshold = 160);
// Rotate 90° clockwise: (w,h) -> (h,w)
// Useful for converting portrait BMP (480x800) into landscape framebuffer (800x480).
static BmpToMonoError convert24Rotate90CW(fs::File& file, MonoBitmap& out, uint8_t threshold = 160,
bool invert = false);
static void freeMonoBitmap(MonoBitmap& bmp); static void freeMonoBitmap(MonoBitmap& bmp);
static const char* errorToString(BmpToMonoError err); static const char* errorToString(BmpToMonoError err);
private: private:
static uint16_t readLE16(fs::File& f); static uint16_t readLE16(File& f);
static uint32_t readLE32(fs::File& f); static uint32_t readLE32(File& f);
// Writes a single pixel into a row-aligned 1bpp buffer (MSB-first), 0=black, 1=white // Writes a single pixel into a row-aligned 1bpp buffer (MSB-first), 0=black, 1=white
static inline void setMonoPixel(uint8_t* buf, int w, int x, int y, bool isBlack) { static inline void setMonoPixel(uint8_t* buf, int w, int x, int y, bool isBlack) {
@ -58,5 +54,5 @@ class BmpToMono {
buf[idx] |= mask; buf[idx] |= mask;
} }
static BmpToMonoError convert24Impl(fs::File& file, MonoBitmap& out, uint8_t threshold, bool invert, bool rotate90CW); static BmpToMonoError convert24BitImpl(File& file, MonoBitmap& out, uint8_t threshold, bool rotate90CW);
}; };

View File

@ -129,8 +129,8 @@ bool GfxRenderer::drawFullScreenBmp(File& file) {
file.seek(0); // Ensure we're at the start of the file file.seek(0); // Ensure we're at the start of the file
MonoBitmap mono; MonoBitmap bmp;
auto err = BmpToMono::convert24Rotate90CW(file, mono, 160, false); auto err = BmpToMono::convert24BitRotate90CCW(file, bmp);
if (err != BmpToMonoError::Ok) { if (err != BmpToMonoError::Ok) {
Serial.printf("[%lu] [GFX] BMP convert failed: %s\n", millis(), BmpToMono::errorToString(err)); Serial.printf("[%lu] [GFX] BMP convert failed: %s\n", millis(), BmpToMono::errorToString(err));
@ -138,17 +138,17 @@ bool GfxRenderer::drawFullScreenBmp(File& file) {
} }
// Hard requirement: must match panel exactly // Hard requirement: must match panel exactly
if (mono.width != EInkDisplay::DISPLAY_WIDTH || mono.height != EInkDisplay::DISPLAY_HEIGHT) { if (bmp.width != EInkDisplay::DISPLAY_WIDTH || bmp.height != EInkDisplay::DISPLAY_HEIGHT) {
Serial.printf("[%lu] [GFX] drawFullScreenBmp: rotated BMP size %dx%d does not match panel %dx%d\n", millis(), Serial.printf("[%lu] [GFX] drawFullScreenBmp: rotated BMP size %dx%d does not match panel %dx%d\n", millis(),
mono.width, mono.height, EInkDisplay::DISPLAY_WIDTH, EInkDisplay::DISPLAY_HEIGHT); bmp.width, bmp.height, EInkDisplay::DISPLAY_WIDTH, EInkDisplay::DISPLAY_HEIGHT);
BmpToMono::freeMonoBitmap(mono); BmpToMono::freeMonoBitmap(bmp);
return false; return false;
} }
// Raw full-screen blit // Full-screen blit
einkDisplay.drawImage(mono.data, 0, 0, mono.width, mono.height); einkDisplay.drawImage(bmp.data, 0, 0, bmp.width, bmp.height);
BmpToMono::freeMonoBitmap(mono); BmpToMono::freeMonoBitmap(bmp);
return true; return true;
} }