fix: #348 fit cover artifacts - merge crop parameter (#465)

Cherry-picked from upstream PR #465
Resolved conflicts: merged crop parameter with existing progressCallback,
kept local dimension calculation and edge luminance caching logic
This commit is contained in:
cottongin 2026-01-27 07:50:37 -05:00
parent 6ffd19a7e8
commit c90304f59b
No known key found for this signature in database
GPG Key ID: 0ECC91FE4655C262
3 changed files with 16 additions and 10 deletions

View File

@ -447,7 +447,7 @@ bool Epub::generateCoverBmp(bool cropped) const {
if (coverImageHref.substr(coverImageHref.length() - 4) == ".jpg" || if (coverImageHref.substr(coverImageHref.length() - 4) == ".jpg" ||
coverImageHref.substr(coverImageHref.length() - 5) == ".jpeg") { coverImageHref.substr(coverImageHref.length() - 5) == ".jpeg") {
Serial.printf("[%lu] [EBP] Generating BMP from JPG cover image\n", millis()); Serial.printf("[%lu] [EBP] Generating BMP from JPG cover image (%s mode)\n", millis(), cropped ? "cropped" : "fit");
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg"; const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
FsFile coverJpg; FsFile coverJpg;

View File

@ -200,7 +200,8 @@ unsigned char JpegToBmpConverter::jpegReadCallback(unsigned char* pBuf, const un
// Internal implementation with configurable target size and bit depth // Internal implementation with configurable target size and bit depth
bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bmpOut, int targetWidth, int targetHeight, bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bmpOut, int targetWidth, int targetHeight,
bool oneBit, const std::function<void(int)>& progressCallback) { bool oneBit, bool crop,
const std::function<void(int)>& progressCallback) {
Serial.printf("[%lu] [JPG] Converting JPEG to %s BMP (target: %dx%d)\n", millis(), oneBit ? "1-bit" : "2-bit", Serial.printf("[%lu] [JPG] Converting JPEG to %s BMP (target: %dx%d)\n", millis(), oneBit ? "1-bit" : "2-bit",
targetWidth, targetHeight); targetWidth, targetHeight);
@ -242,8 +243,12 @@ bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bm
const float scaleToFitWidth = static_cast<float>(targetWidth) / imageInfo.m_width; const float scaleToFitWidth = static_cast<float>(targetWidth) / imageInfo.m_width;
const float scaleToFitHeight = static_cast<float>(targetHeight) / imageInfo.m_height; const float scaleToFitHeight = static_cast<float>(targetHeight) / imageInfo.m_height;
// We scale to the smaller dimension, so we can potentially crop later. // We scale to the smaller dimension, so we can potentially crop later.
// TODO: ideally, we already crop here. float scale = 1.0;
const float scale = (scaleToFitWidth > scaleToFitHeight) ? scaleToFitWidth : scaleToFitHeight; if (crop) { // if we will crop, scale to the smaller dimension
scale = (scaleToFitWidth > scaleToFitHeight) ? scaleToFitWidth : scaleToFitHeight;
} else { // else, scale to the larger dimension to fit
scale = (scaleToFitWidth < scaleToFitHeight) ? scaleToFitWidth : scaleToFitHeight;
}
outWidth = static_cast<int>(imageInfo.m_width * scale); outWidth = static_cast<int>(imageInfo.m_width * scale);
outHeight = static_cast<int>(imageInfo.m_height * scale); outHeight = static_cast<int>(imageInfo.m_height * scale);
@ -556,22 +561,22 @@ bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bm
} }
// Core function: Convert JPEG file to 2-bit BMP (uses default target size) // Core function: Convert JPEG file to 2-bit BMP (uses default target size)
bool JpegToBmpConverter::jpegFileToBmpStream(FsFile& jpegFile, Print& bmpOut) { bool JpegToBmpConverter::jpegFileToBmpStream(FsFile& jpegFile, Print& bmpOut, bool crop) {
return jpegFileToBmpStreamInternal(jpegFile, bmpOut, TARGET_MAX_WIDTH, TARGET_MAX_HEIGHT, false, nullptr); return jpegFileToBmpStreamInternal(jpegFile, bmpOut, TARGET_MAX_WIDTH, TARGET_MAX_HEIGHT, false, crop, nullptr);
} }
// Convert with custom target size (for thumbnails, 2-bit) // Convert with custom target size (for thumbnails, 2-bit)
bool JpegToBmpConverter::jpegFileToBmpStreamWithSize(FsFile& jpegFile, Print& bmpOut, int targetMaxWidth, bool JpegToBmpConverter::jpegFileToBmpStreamWithSize(FsFile& jpegFile, Print& bmpOut, int targetMaxWidth,
int targetMaxHeight, int targetMaxHeight,
const std::function<void(int)>& progressCallback) { const std::function<void(int)>& progressCallback) {
return jpegFileToBmpStreamInternal(jpegFile, bmpOut, targetMaxWidth, targetMaxHeight, false, progressCallback); return jpegFileToBmpStreamInternal(jpegFile, bmpOut, targetMaxWidth, targetMaxHeight, false, true, progressCallback);
} }
// Convert to 1-bit BMP (black and white only, no grays) for fast home screen rendering // Convert to 1-bit BMP (black and white only, no grays) for fast home screen rendering
bool JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(FsFile& jpegFile, Print& bmpOut, int targetMaxWidth, bool JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(FsFile& jpegFile, Print& bmpOut, int targetMaxWidth,
int targetMaxHeight, int targetMaxHeight,
const std::function<void(int)>& progressCallback) { const std::function<void(int)>& progressCallback) {
return jpegFileToBmpStreamInternal(jpegFile, bmpOut, targetMaxWidth, targetMaxHeight, true, progressCallback); return jpegFileToBmpStreamInternal(jpegFile, bmpOut, targetMaxWidth, targetMaxHeight, true, true, progressCallback);
} }
// Get JPEG dimensions without full conversion // Get JPEG dimensions without full conversion

View File

@ -10,10 +10,11 @@ class JpegToBmpConverter {
static unsigned char jpegReadCallback(unsigned char* pBuf, unsigned char buf_size, static unsigned char jpegReadCallback(unsigned char* pBuf, unsigned char buf_size,
unsigned char* pBytes_actually_read, void* pCallback_data); unsigned char* pBytes_actually_read, void* pCallback_data);
static bool jpegFileToBmpStreamInternal(class FsFile& jpegFile, Print& bmpOut, int targetWidth, int targetHeight, static bool jpegFileToBmpStreamInternal(class FsFile& jpegFile, Print& bmpOut, int targetWidth, int targetHeight,
bool oneBit, const std::function<void(int)>& progressCallback = nullptr); bool oneBit, bool crop,
const std::function<void(int)>& progressCallback = nullptr);
public: public:
static bool jpegFileToBmpStream(FsFile& jpegFile, Print& bmpOut); static bool jpegFileToBmpStream(FsFile& jpegFile, Print& bmpOut, bool crop = true);
// Convert with custom target size (for thumbnails) // Convert with custom target size (for thumbnails)
static bool jpegFileToBmpStreamWithSize(FsFile& jpegFile, Print& bmpOut, int targetMaxWidth, int targetMaxHeight, static bool jpegFileToBmpStreamWithSize(FsFile& jpegFile, Print& bmpOut, int targetMaxWidth, int targetMaxHeight,
const std::function<void(int)>& progressCallback = nullptr); const std::function<void(int)>& progressCallback = nullptr);