diff --git a/lib/Epub/Epub.cpp b/lib/Epub/Epub.cpp index 7559e3b3..91439f32 100644 --- a/lib/Epub/Epub.cpp +++ b/lib/Epub/Epub.cpp @@ -468,10 +468,47 @@ bool Epub::generateThumbBmp() const { coverJpg.close(); return false; } - // Use smaller target size for Continue Reading card (half of screen: 240x400) - // Generate 1-bit BMP for fast home screen rendering (no gray passes needed) - constexpr int THUMB_TARGET_WIDTH = 240; - constexpr int THUMB_TARGET_HEIGHT = 400; + + // Generate 1-bit BMP maintaining aspect ratio with fixed height of 400px + // This ensures the card width adapts to the actual cover image aspect ratio + const int THUMB_TARGET_HEIGHT = 400; + + // Read a small portion of the JPG file to determine its dimensions + uint8_t headerBuffer[200]; + size_t bytesRead = coverJpg.read(headerBuffer, sizeof(headerBuffer)); + coverJpg.seek(0); // Reset file position + + int imgWidth = 240, imgHeight = 400; // Default fallback + + // Simple header parsing to get image dimensions (works for most JPGs) + if (bytesRead > 0) { + // Look for SOF (Start of Frame) markers + for (size_t i = 0; i < bytesRead - 10; i++) { + if (headerBuffer[i] == 0xFF && headerBuffer[i + 1] == 0xC0) { + // Found SOF0 marker + if (i + 7 < bytesRead) { + imgHeight = (headerBuffer[i + 5] << 8) | headerBuffer[i + 6]; + imgWidth = (headerBuffer[i + 7] << 8) | headerBuffer[i + 8]; + break; + } + } else if (headerBuffer[i] == 0xFF && headerBuffer[i + 1] == 0xC2) { + // Found SOF2 marker + if (i + 7 < bytesRead) { + imgHeight = (headerBuffer[i + 5] << 8) | headerBuffer[i + 6]; + imgWidth = (headerBuffer[i + 7] << 8) | headerBuffer[i + 8]; + break; + } + } + } + } + + // Calculate target width maintaining aspect ratio + const float aspectRatio = static_cast(imgWidth) / static_cast(imgHeight); + const int THUMB_TARGET_WIDTH = static_cast(THUMB_TARGET_HEIGHT * aspectRatio); + + Serial.printf("[%lu] [EBP] Original JPG: %dx%d, Target: %dx%d\n", millis(), imgWidth, imgHeight, THUMB_TARGET_WIDTH, + THUMB_TARGET_HEIGHT); + const bool success = JpegToBmpConverter::jpegFileTo1BitBmpStreamWithSize(coverJpg, thumbBmp, THUMB_TARGET_WIDTH, THUMB_TARGET_HEIGHT); coverJpg.close(); diff --git a/lib/JpegToBmpConverter/JpegToBmpConverter.cpp b/lib/JpegToBmpConverter/JpegToBmpConverter.cpp index 84ac1d58..837bfd27 100644 --- a/lib/JpegToBmpConverter/JpegToBmpConverter.cpp +++ b/lib/JpegToBmpConverter/JpegToBmpConverter.cpp @@ -567,5 +567,5 @@ bool JpegToBmpConverter::jpegFileToBmpStreamWithSize(FsFile& jpegFile, Print& bm // 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, int targetMaxHeight) { - return jpegFileToBmpStreamInternal(jpegFile, bmpOut, targetMaxWidth, targetMaxHeight, true); + return jpegFileToBmpStreamInternal(jpegFile, bmpOut, targetMaxWidth, targetMaxHeight, true, false); } diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 678af7cb..8a9e2625 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -224,9 +224,50 @@ void HomeActivity::render() { constexpr int bottomMargin = 60; // --- Top "book" card for the current title (selectorIndex == 0) --- - const int bookWidth = pageWidth / 2; - const int bookHeight = pageHeight / 2; - const int bookX = (pageWidth - bookWidth) / 2; + // When there's no cover image, use fixed size (half screen) + // When there's cover image, adapt width to image aspect ratio, keep height fixed at 400px + const int baseHeight = 400; // Fixed height for both scenarios + + int bookWidth, bookX; + if (hasCoverImage) { + // When there's cover, calculate width based on image aspect ratio + // Use default width ratio as fallback if we can't get image dimensions + const float defaultWidthRatio = 0.5f; // Same as half screen + bookWidth = static_cast(pageWidth * defaultWidthRatio); + + // If we have cover bitmap path, try to get actual image dimensions + if (!coverBmpPath.empty()) { + FsFile file; + if (SdMan.openFileForRead("HOME", coverBmpPath, file)) { + Bitmap bitmap(file); + if (bitmap.parseHeaders() == BmpReaderError::Ok) { + const int imgWidth = bitmap.getWidth(); + const int imgHeight = bitmap.getHeight(); + + // Calculate width based on aspect ratio, maintaining 400px height + if (imgHeight > 0) { + const float aspectRatio = static_cast(imgWidth) / static_cast(imgHeight); + bookWidth = static_cast(baseHeight * aspectRatio); + + // Ensure width doesn't exceed reasonable limits (max 90% of screen width) + const int maxWidth = static_cast(pageWidth * 0.9f); + if (bookWidth > maxWidth) { + bookWidth = maxWidth; + } + } + } + file.close(); + } + } + + bookX = (pageWidth - bookWidth) / 2; + } else { + // No cover: use half screen size + bookWidth = pageWidth / 2; + bookX = (pageWidth - bookWidth) / 2; + } + + const int bookHeight = baseHeight; constexpr int bookY = 30; const bool bookSelected = hasContinueReading && selectorIndex == 0;