diff --git a/USER_GUIDE.md b/USER_GUIDE.md index bdc0f03..06973c9 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -105,6 +105,10 @@ The Settings screen allows you to configure the device's behavior. There are a f - **Sleep Screen Cover Mode**: How to display the book cover when "Cover" sleep screen is selected: - "Fit" (default) - Scale the image down to fit centered on the screen, padding with white borders as necessary - "Crop" - Scale the image down and crop as necessary to try to to fill the screen (Note: this is experimental and may not work as expected) +- **Sleep Screen Cover Filter**: What filter will be applied to the book cover when "Cover" sleep screen is selected + - "None" (default) - The cover image will be converted to a grayscale image and displayed as it is + - "Contrast" - The image will be displayed as a black & white image without grayscale conversion + - "Inverted" - The image will be inverted as in white&black and will be displayed without grayscale conversion - **Status Bar**: Configure the status bar displayed while reading: - "None" - No status bar - "No Progress" - Show status bar without reading progress diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index 7287299..dafccb5 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -23,7 +23,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) { namespace { constexpr uint8_t SETTINGS_FILE_VERSION = 1; // Increment this when adding new persisted settings fields -constexpr uint8_t SETTINGS_COUNT = 25; // User's 23 + OPDS auth (2) +constexpr uint8_t SETTINGS_COUNT = 26; // 25 + sleepScreenCoverFilter constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin"; } // namespace @@ -64,6 +64,9 @@ bool CrossPointSettings::saveToFile() const { // OPDS auth fields added for PR #404 serialization::writeString(outputFile, std::string(opdsUsername)); serialization::writeString(outputFile, std::string(opdsPassword)); + // PR #476: B&W filter for cover images + serialization::writePod(outputFile, sleepScreenCoverFilter); + // New fields added at end for backward compatibility outputFile.close(); Serial.printf("[%lu] [CPS] Settings saved to file\n", millis()); @@ -161,6 +164,10 @@ bool CrossPointSettings::loadFromFile() { opdsPassword[sizeof(opdsPassword) - 1] = '\0'; } if (++settingsRead >= fileSettingsCount) break; + // PR #476: B&W filter for cover images + readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT); + if (++settingsRead >= fileSettingsCount) break; + // New fields added at end for backward compatibility } while (false); inputFile.close(); diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 21a6d16..79a42d8 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -18,6 +18,12 @@ class CrossPointSettings { // Should match with SettingsActivity text enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, BLANK = 4, SLEEP_SCREEN_MODE_COUNT }; enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, ACTUAL = 2, SLEEP_SCREEN_COVER_MODE_COUNT }; + enum SLEEP_SCREEN_COVER_FILTER { + NO_FILTER = 0, + BLACK_AND_WHITE = 1, + INVERTED_BLACK_AND_WHITE = 2, + SLEEP_SCREEN_COVER_FILTER_COUNT + }; // Status bar display type enum enum STATUS_BAR_MODE { NONE = 0, NO_PROGRESS = 1, FULL = 2, FULL_WITH_PROGRESS_BAR = 3, ONLY_PROGRESS_BAR = 4, STATUS_BAR_MODE_COUNT }; @@ -89,6 +95,8 @@ class CrossPointSettings { uint8_t sleepScreen = DARK; // Sleep screen cover mode settings uint8_t sleepScreenCoverMode = FIT; + // Sleep screen cover filter + uint8_t sleepScreenCoverFilter = NO_FILTER; // Status bar settings uint8_t statusBar = FULL; // Text rendering settings diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index 10ae5a9..bee3815 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -233,6 +233,10 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap, const std::str millis(), edges.top, edges.bottom, edges.left, edges.right, topGray, bottomGray, leftGray, rightGray); + // Check if greyscale pass should be used (PR #476: skip if filter is applied) + const bool hasGreyscale = bitmap.hasGreyscale() && + SETTINGS.sleepScreenCoverFilter == CrossPointSettings::SLEEP_SCREEN_COVER_FILTER::NO_FILTER; + // Clear screen to white first (default background) renderer.clearScreen(0xFF); @@ -256,9 +260,15 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap, const std::str Serial.printf("[%lu] [SLP] drawing bitmap at %d, %d\n", millis(), x, y); renderer.drawBitmap(bitmap, x, y, drawWidth, drawHeight, cropX, cropY); + + // PR #476: Apply inverted B&W filter if selected + if (SETTINGS.sleepScreenCoverFilter == CrossPointSettings::SLEEP_SCREEN_COVER_FILTER::INVERTED_BLACK_AND_WHITE) { + renderer.invertScreen(); + } + renderer.displayBuffer(EInkDisplay::HALF_REFRESH); - if (bitmap.hasGreyscale()) { + if (hasGreyscale) { // Grayscale LSB pass bitmap.rewindToData(); renderer.clearScreen(0x00); diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 15b3cef..ddfd2d7 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -12,11 +12,13 @@ const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"}; namespace { -constexpr int displaySettingsCount = 5; +constexpr int displaySettingsCount = 7; const SettingInfo displaySettings[displaySettingsCount] = { // Should match with SLEEP_SCREEN_MODE SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}), SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop", "Actual"}), + SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter, + {"None", "Contrast", "Inverted"}), SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, {"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}), SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}),