feat: Add PNG cover image support for EPUB books (#827)
Cherry-pick upstream PR #827 with conflict resolution for mod/master: - Add PngToBmpConverter library for PNG cover → BMP conversion - Add PNG thumbnail generation in generateThumbBmp() - Fix generateCoverBmp() PNG block to use effectiveCoverImageHref (consistent with mod's fallback cover candidate probing) - Add .png to getCoverCandidates() extensions - Use LOG_ERR macro in ImageToFramebufferDecoder (mod standard) - Upstream image converter refinements (ImageBlock, PixelCache, JpegToFramebufferConverter, PngToFramebufferConverter) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <HalStorage.h>
|
||||
#include <JpegToBmpConverter.h>
|
||||
#include <Logging.h>
|
||||
#include <PngToBmpConverter.h>
|
||||
#include <ZipFile.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -518,12 +519,45 @@ bool Epub::generateCoverBmp(bool cropped) const {
|
||||
LOG_ERR("EBP", "Failed to generate BMP from cover image");
|
||||
Storage.remove(getCoverBmpPath(cropped).c_str());
|
||||
}
|
||||
LOG_DBG("EBP", "Generated BMP from cover image, success: %s", success ? "yes" : "no");
|
||||
LOG_DBG("EBP", "Generated BMP from JPG cover image, success: %s", success ? "yes" : "no");
|
||||
return success;
|
||||
} else {
|
||||
LOG_ERR("EBP", "Cover image is not a supported format, skipping");
|
||||
}
|
||||
|
||||
bool isPng = lowerHref.substr(lowerHref.length() - 4) == ".png";
|
||||
if (isPng) {
|
||||
LOG_DBG("EBP", "Generating BMP from PNG cover image (%s mode)", cropped ? "cropped" : "fit");
|
||||
const auto coverPngTempPath = getCachePath() + "/.cover.png";
|
||||
|
||||
FsFile coverPng;
|
||||
if (!Storage.openFileForWrite("EBP", coverPngTempPath, coverPng)) {
|
||||
return false;
|
||||
}
|
||||
readItemContentsToStream(effectiveCoverImageHref, coverPng, 1024);
|
||||
coverPng.close();
|
||||
|
||||
if (!Storage.openFileForRead("EBP", coverPngTempPath, coverPng)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FsFile coverBmp;
|
||||
if (!Storage.openFileForWrite("EBP", getCoverBmpPath(cropped), coverBmp)) {
|
||||
coverPng.close();
|
||||
return false;
|
||||
}
|
||||
const bool success = PngToBmpConverter::pngFileToBmpStream(coverPng, coverBmp, cropped);
|
||||
coverPng.close();
|
||||
coverBmp.close();
|
||||
Storage.remove(coverPngTempPath.c_str());
|
||||
|
||||
if (!success) {
|
||||
LOG_ERR("EBP", "Failed to generate BMP from PNG cover image");
|
||||
Storage.remove(getCoverBmpPath(cropped).c_str());
|
||||
}
|
||||
LOG_DBG("EBP", "Generated BMP from PNG cover image, success: %s", success ? "yes" : "no");
|
||||
return success;
|
||||
}
|
||||
|
||||
LOG_ERR("EBP", "Cover image is not a supported format, skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -611,9 +645,46 @@ bool Epub::generateThumbBmp(int height) const {
|
||||
}
|
||||
LOG_DBG("EBP", "Generated thumb BMP from JPG cover image, success: %s", success ? "yes" : "no");
|
||||
return success;
|
||||
} else {
|
||||
LOG_ERR("EBP", "Cover image is not a supported format, skipping thumbnail");
|
||||
}
|
||||
|
||||
bool isPng = lowerHref.substr(lowerHref.length() - 4) == ".png";
|
||||
if (isPng) {
|
||||
LOG_DBG("EBP", "Generating thumb BMP from PNG cover image");
|
||||
const auto coverPngTempPath = getCachePath() + "/.cover.png";
|
||||
|
||||
FsFile coverPng;
|
||||
if (!Storage.openFileForWrite("EBP", coverPngTempPath, coverPng)) {
|
||||
return false;
|
||||
}
|
||||
readItemContentsToStream(effectiveCoverImageHref, coverPng, 1024);
|
||||
coverPng.close();
|
||||
|
||||
if (!Storage.openFileForRead("EBP", coverPngTempPath, coverPng)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FsFile thumbBmp;
|
||||
if (!Storage.openFileForWrite("EBP", getThumbBmpPath(height), thumbBmp)) {
|
||||
coverPng.close();
|
||||
return false;
|
||||
}
|
||||
int THUMB_TARGET_WIDTH = height * 0.6;
|
||||
int THUMB_TARGET_HEIGHT = height;
|
||||
const bool success =
|
||||
PngToBmpConverter::pngFileTo1BitBmpStreamWithSize(coverPng, thumbBmp, THUMB_TARGET_WIDTH, THUMB_TARGET_HEIGHT);
|
||||
coverPng.close();
|
||||
thumbBmp.close();
|
||||
Storage.remove(coverPngTempPath.c_str());
|
||||
|
||||
if (!success) {
|
||||
LOG_ERR("EBP", "Failed to generate thumb BMP from PNG cover image");
|
||||
Storage.remove(getThumbBmpPath(height).c_str());
|
||||
}
|
||||
LOG_DBG("EBP", "Generated thumb BMP from PNG cover image, success: %s", success ? "yes" : "no");
|
||||
return success;
|
||||
}
|
||||
|
||||
LOG_ERR("EBP", "Cover image is not a supported format, skipping thumbnail");
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -970,7 +1041,7 @@ bool Epub::isValidThumbnailBmp(const std::string& bmpPath) {
|
||||
|
||||
std::vector<std::string> Epub::getCoverCandidates() const {
|
||||
std::vector<std::string> coverDirectories = {".", "images", "Images", "OEBPS", "OEBPS/images", "OEBPS/Images"};
|
||||
std::vector<std::string> coverExtensions = {".jpg", ".jpeg"}; // add ".png" when PNG cover support is implemented
|
||||
std::vector<std::string> coverExtensions = {".jpg", ".jpeg", ".png"};
|
||||
std::vector<std::string> coverCandidates;
|
||||
for (const auto& ext : coverExtensions) {
|
||||
for (const auto& dir : coverDirectories) {
|
||||
|
||||
Reference in New Issue
Block a user