feat: Extend high contrast mode to entire UI
- Add global high contrast mode flag in BitmapHelpers - Modify quantization thresholds for high contrast rendering - Update ditherer classes (Atkinson, Floyd-Steinberg) for contrast mode - Add displayContrast setting with persistence - Add "High Contrast" toggle in display settings - Apply high contrast mode on startup from saved settings
This commit is contained in:
parent
1496ce68a6
commit
158caacfe0
@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Global high contrast mode flag
|
||||||
|
static bool g_highContrastMode = false;
|
||||||
|
|
||||||
|
void setHighContrastMode(bool enabled) {
|
||||||
|
g_highContrastMode = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHighContrastMode() {
|
||||||
|
return g_highContrastMode;
|
||||||
|
}
|
||||||
|
|
||||||
// Brightness/Contrast adjustments:
|
// Brightness/Contrast adjustments:
|
||||||
constexpr bool USE_BRIGHTNESS = false; // true: apply brightness/gamma adjustments
|
constexpr bool USE_BRIGHTNESS = false; // true: apply brightness/gamma adjustments
|
||||||
constexpr int BRIGHTNESS_BOOST = 10; // Brightness offset (0-50)
|
constexpr int BRIGHTNESS_BOOST = 10; // Brightness offset (0-50)
|
||||||
@ -52,14 +63,28 @@ int adjustPixel(int gray) {
|
|||||||
// Simple quantization without dithering - divide into 4 levels
|
// Simple quantization without dithering - divide into 4 levels
|
||||||
// The thresholds are fine-tuned to the X4 display
|
// The thresholds are fine-tuned to the X4 display
|
||||||
uint8_t quantizeSimple(int gray) {
|
uint8_t quantizeSimple(int gray) {
|
||||||
if (gray < 45) {
|
if (g_highContrastMode) {
|
||||||
return 0;
|
// High contrast: push mid-grays toward black/white
|
||||||
} else if (gray < 70) {
|
if (gray < 60) {
|
||||||
return 1;
|
return 0;
|
||||||
} else if (gray < 140) {
|
} else if (gray < 100) {
|
||||||
return 2;
|
return 1;
|
||||||
|
} else if (gray < 180) {
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return 3;
|
// Normal thresholds
|
||||||
|
if (gray < 45) {
|
||||||
|
return 0;
|
||||||
|
} else if (gray < 70) {
|
||||||
|
return 1;
|
||||||
|
} else if (gray < 140) {
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
// Global high contrast mode control
|
||||||
|
// When enabled, quantization thresholds are adjusted to push mid-grays toward black/white
|
||||||
|
void setHighContrastMode(bool enabled);
|
||||||
|
bool isHighContrastMode();
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
uint8_t quantize(int gray, int x, int y);
|
uint8_t quantize(int gray, int x, int y);
|
||||||
uint8_t quantizeSimple(int gray);
|
uint8_t quantizeSimple(int gray);
|
||||||
@ -122,21 +127,23 @@ class AtkinsonDitherer {
|
|||||||
// Quantize to 4 levels
|
// Quantize to 4 levels
|
||||||
uint8_t quantized;
|
uint8_t quantized;
|
||||||
int quantizedValue;
|
int quantizedValue;
|
||||||
if (false) { // original thresholds
|
if (isHighContrastMode()) {
|
||||||
if (adjusted < 43) {
|
// High contrast thresholds - push mid-grays toward extremes
|
||||||
|
if (adjusted < 50) {
|
||||||
quantized = 0;
|
quantized = 0;
|
||||||
quantizedValue = 0;
|
quantizedValue = 15;
|
||||||
} else if (adjusted < 128) {
|
} else if (adjusted < 90) {
|
||||||
quantized = 1;
|
quantized = 1;
|
||||||
quantizedValue = 85;
|
quantizedValue = 50;
|
||||||
} else if (adjusted < 213) {
|
} else if (adjusted < 170) {
|
||||||
quantized = 2;
|
quantized = 2;
|
||||||
quantizedValue = 170;
|
quantizedValue = 130;
|
||||||
} else {
|
} else {
|
||||||
quantized = 3;
|
quantized = 3;
|
||||||
quantizedValue = 255;
|
quantizedValue = 230;
|
||||||
}
|
}
|
||||||
} else { // fine-tuned to X4 eink display
|
} else {
|
||||||
|
// Normal thresholds - fine-tuned to X4 eink display
|
||||||
if (adjusted < 30) {
|
if (adjusted < 30) {
|
||||||
quantized = 0;
|
quantized = 0;
|
||||||
quantizedValue = 15;
|
quantizedValue = 15;
|
||||||
@ -223,24 +230,26 @@ class FloydSteinbergDitherer {
|
|||||||
if (adjusted < 0) adjusted = 0;
|
if (adjusted < 0) adjusted = 0;
|
||||||
if (adjusted > 255) adjusted = 255;
|
if (adjusted > 255) adjusted = 255;
|
||||||
|
|
||||||
// Quantize to 4 levels (0, 85, 170, 255)
|
// Quantize to 4 levels
|
||||||
uint8_t quantized;
|
uint8_t quantized;
|
||||||
int quantizedValue;
|
int quantizedValue;
|
||||||
if (false) { // original thresholds
|
if (isHighContrastMode()) {
|
||||||
if (adjusted < 43) {
|
// High contrast thresholds - push mid-grays toward extremes
|
||||||
|
if (adjusted < 50) {
|
||||||
quantized = 0;
|
quantized = 0;
|
||||||
quantizedValue = 0;
|
quantizedValue = 15;
|
||||||
} else if (adjusted < 128) {
|
} else if (adjusted < 90) {
|
||||||
quantized = 1;
|
quantized = 1;
|
||||||
quantizedValue = 85;
|
quantizedValue = 50;
|
||||||
} else if (adjusted < 213) {
|
} else if (adjusted < 170) {
|
||||||
quantized = 2;
|
quantized = 2;
|
||||||
quantizedValue = 170;
|
quantizedValue = 130;
|
||||||
} else {
|
} else {
|
||||||
quantized = 3;
|
quantized = 3;
|
||||||
quantizedValue = 255;
|
quantizedValue = 230;
|
||||||
}
|
}
|
||||||
} else { // fine-tuned to X4 eink display
|
} else {
|
||||||
|
// Normal thresholds - fine-tuned to X4 eink display
|
||||||
if (adjusted < 30) {
|
if (adjusted < 30) {
|
||||||
quantized = 0;
|
quantized = 0;
|
||||||
quantizedValue = 15;
|
quantizedValue = 15;
|
||||||
|
|||||||
@ -23,7 +23,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 26; // 25 + sleepScreenCoverFilter
|
constexpr uint8_t SETTINGS_COUNT = 27; // 26 + displayContrast
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -66,6 +66,8 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writeString(outputFile, std::string(opdsPassword));
|
serialization::writeString(outputFile, std::string(opdsPassword));
|
||||||
// PR #476: B&W filter for cover images
|
// PR #476: B&W filter for cover images
|
||||||
serialization::writePod(outputFile, sleepScreenCoverFilter);
|
serialization::writePod(outputFile, sleepScreenCoverFilter);
|
||||||
|
// System-wide display contrast
|
||||||
|
serialization::writePod(outputFile, displayContrast);
|
||||||
// New fields added at end for backward compatibility
|
// New fields added at end for backward compatibility
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
@ -167,6 +169,9 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
// PR #476: B&W filter for cover images
|
// PR #476: B&W filter for cover images
|
||||||
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
|
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
// System-wide display contrast (0 = normal, 1 = high)
|
||||||
|
serialization::readPod(inputFile, displayContrast);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
// New fields added at end for backward compatibility
|
// New fields added at end for backward compatibility
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
|
|||||||
@ -133,6 +133,8 @@ class CrossPointSettings {
|
|||||||
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
||||||
// Long-press chapter skip on side buttons
|
// Long-press chapter skip on side buttons
|
||||||
uint8_t longPressChapterSkip = 1;
|
uint8_t longPressChapterSkip = 1;
|
||||||
|
// System-wide display contrast (0 = normal, 1 = high)
|
||||||
|
uint8_t displayContrast = 0;
|
||||||
|
|
||||||
// Pinned list name (empty = none pinned)
|
// Pinned list name (empty = none pinned)
|
||||||
char pinnedListName[64] = "";
|
char pinnedListName[64] = "";
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "CategorySettingsActivity.h"
|
#include "CategorySettingsActivity.h"
|
||||||
|
|
||||||
|
#include <BitmapHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
@ -115,6 +116,11 @@ void CategorySettingsActivity::toggleCurrentSetting() {
|
|||||||
// Toggle the boolean value using the member pointer
|
// Toggle the boolean value using the member pointer
|
||||||
const bool currentValue = SETTINGS.*(setting.valuePtr);
|
const bool currentValue = SETTINGS.*(setting.valuePtr);
|
||||||
SETTINGS.*(setting.valuePtr) = !currentValue;
|
SETTINGS.*(setting.valuePtr) = !currentValue;
|
||||||
|
|
||||||
|
// Handle side effects for specific settings
|
||||||
|
if (setting.valuePtr == &CrossPointSettings::displayContrast) {
|
||||||
|
setHighContrastMode(SETTINGS.displayContrast == 1);
|
||||||
|
}
|
||||||
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
} else if (setting.type == SettingType::ENUM && setting.valuePtr != nullptr) {
|
||||||
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
const uint8_t currentValue = SETTINGS.*(setting.valuePtr);
|
||||||
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
SETTINGS.*(setting.valuePtr) = (currentValue + 1) % static_cast<uint8_t>(setting.enumValues.size());
|
||||||
|
|||||||
@ -19,6 +19,7 @@ const SettingInfo displaySettings[displaySettingsCount] = {
|
|||||||
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop", "Actual"}),
|
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop", "Actual"}),
|
||||||
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
|
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
|
||||||
{"None", "Contrast", "Inverted"}),
|
{"None", "Contrast", "Inverted"}),
|
||||||
|
SettingInfo::Toggle("High Contrast", &CrossPointSettings::displayContrast),
|
||||||
SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar,
|
SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar,
|
||||||
{"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}),
|
{"None", "No Progress", "Full w/ Percentage", "Full w/ Progress Bar", "Progress Bar"}),
|
||||||
SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}),
|
SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}),
|
||||||
|
|||||||
@ -6,9 +6,12 @@
|
|||||||
#include <SDCardManager.h>
|
#include <SDCardManager.h>
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <builtinFonts/all.h>
|
#include <builtinFonts/all.h>
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <BitmapHelpers.h>
|
||||||
|
|
||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
#include "BookListStore.h"
|
#include "BookListStore.h"
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
@ -470,6 +473,8 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SETTINGS.loadFromFile();
|
SETTINGS.loadFromFile();
|
||||||
|
// Apply high contrast mode from settings
|
||||||
|
setHighContrastMode(SETTINGS.displayContrast == 1);
|
||||||
|
|
||||||
if (isWakeupByPowerButton()) {
|
if (isWakeupByPowerButton()) {
|
||||||
// For normal wakeups, verify power button press duration
|
// For normal wakeups, verify power button press duration
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user