Book cover sleep screen (#89)
## Summary * Fix issue with 2-bit bmp rendering * Add support generate book cover BMP from JPG and use as sleep screen ## Additional Context * It does not support other image formats beyond JPG at this point * Something is cooked with my JpegToBmpConverter logic, it generates weird interlaced looking images for some JPGs | Book 1 | Book 2| | --- | --- | |  |  |
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "Epub.h"
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <JpegToBmpConverter.h>
|
||||
#include <SD.h>
|
||||
#include <ZipFile.h>
|
||||
|
||||
@@ -218,7 +219,45 @@ const std::string& Epub::getPath() const { return filepath; }
|
||||
|
||||
const std::string& Epub::getTitle() const { return title; }
|
||||
|
||||
const std::string& Epub::getCoverImageItem() const { return coverImageItem; }
|
||||
std::string Epub::getCoverBmpPath() const { return cachePath + "/cover.bmp"; }
|
||||
|
||||
bool Epub::generateCoverBmp() const {
|
||||
// Already generated, return true
|
||||
if (SD.exists(getCoverBmpPath().c_str())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (coverImageItem.empty()) {
|
||||
Serial.printf("[%lu] [EBP] No known cover image\n", millis());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (coverImageItem.substr(coverImageItem.length() - 4) == ".jpg" ||
|
||||
coverImageItem.substr(coverImageItem.length() - 5) == ".jpeg") {
|
||||
Serial.printf("[%lu] [EBP] Generating BMP from JPG cover image\n", millis());
|
||||
File coverJpg = SD.open((getCachePath() + "/.cover.jpg").c_str(), FILE_WRITE, true);
|
||||
readItemContentsToStream(coverImageItem, coverJpg, 1024);
|
||||
coverJpg.close();
|
||||
|
||||
coverJpg = SD.open((getCachePath() + "/.cover.jpg").c_str(), FILE_READ);
|
||||
File coverBmp = SD.open(getCoverBmpPath().c_str(), FILE_WRITE, true);
|
||||
const bool success = JpegToBmpConverter::jpegFileToBmpStream(coverJpg, coverBmp);
|
||||
coverJpg.close();
|
||||
coverBmp.close();
|
||||
SD.remove((getCachePath() + "/.cover.jpg").c_str());
|
||||
|
||||
if (!success) {
|
||||
Serial.printf("[%lu] [EBP] Failed to generate BMP from JPG cover image\n", millis());
|
||||
SD.remove(getCoverBmpPath().c_str());
|
||||
}
|
||||
Serial.printf("[%lu] [EBP] Generated BMP from JPG cover image, success: %s\n", millis(), success ? "yes" : "no");
|
||||
return success;
|
||||
} else {
|
||||
Serial.printf("[%lu] [EBP] Cover image is not a JPG, skipping\n", millis());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string normalisePath(const std::string& path) {
|
||||
std::vector<std::string> components;
|
||||
|
||||
@@ -48,7 +48,8 @@ class Epub {
|
||||
const std::string& getCachePath() const;
|
||||
const std::string& getPath() const;
|
||||
const std::string& getTitle() const;
|
||||
const std::string& getCoverImageItem() const;
|
||||
std::string getCoverBmpPath() const;
|
||||
bool generateCoverBmp() const;
|
||||
uint8_t* readItemContentsToBytes(const std::string& itemHref, size_t* size = nullptr,
|
||||
bool trailingNullByte = false) const;
|
||||
bool readItemContentsToStream(const std::string& itemHref, Print& out, size_t chunkSize) const;
|
||||
|
||||
@@ -128,7 +128,7 @@ BmpReaderError Bitmap::readRow(uint8_t* data, uint8_t* rowBuffer) const {
|
||||
int bitShift = 6;
|
||||
|
||||
// Helper lambda to pack 2bpp color into the output stream
|
||||
auto packPixel = [&](uint8_t lum) {
|
||||
auto packPixel = [&](const uint8_t lum) {
|
||||
uint8_t color = (lum >> 6); // Simple 2-bit reduction: 0-255 -> 0-3
|
||||
currentOutByte |= (color << bitShift);
|
||||
if (bitShift == 0) {
|
||||
@@ -140,38 +140,49 @@ BmpReaderError Bitmap::readRow(uint8_t* data, uint8_t* rowBuffer) const {
|
||||
}
|
||||
};
|
||||
|
||||
uint8_t lum;
|
||||
|
||||
switch (bpp) {
|
||||
case 8: {
|
||||
case 32: {
|
||||
const uint8_t* p = rowBuffer;
|
||||
for (int x = 0; x < width; x++) {
|
||||
packPixel(paletteLum[rowBuffer[x]]);
|
||||
lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8;
|
||||
packPixel(lum);
|
||||
p += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
const uint8_t* p = rowBuffer;
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8;
|
||||
lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8;
|
||||
packPixel(lum);
|
||||
p += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
for (int x = 0; x < width; x++) {
|
||||
packPixel(paletteLum[rowBuffer[x]]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
for (int x = 0; x < width; x++) {
|
||||
lum = paletteLum[(rowBuffer[x >> 2] >> (6 - ((x & 3) * 2))) & 0x03];
|
||||
packPixel(lum);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint8_t lum = (rowBuffer[x >> 3] & (0x80 >> (x & 7))) ? 0xFF : 0x00;
|
||||
lum = (rowBuffer[x >> 3] & (0x80 >> (x & 7))) ? 0xFF : 0x00;
|
||||
packPixel(lum);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
const uint8_t* p = rowBuffer;
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8;
|
||||
packPixel(lum);
|
||||
p += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return BmpReaderError::UnsupportedBpp;
|
||||
}
|
||||
|
||||
// Flush remaining bits if width is not a multiple of 4
|
||||
|
||||
Reference in New Issue
Block a user