Add dithering algorithms
This commit is contained in:
@@ -137,6 +137,12 @@ class CrossPointSettings {
|
||||
|
||||
// Image rendering in EPUB reader
|
||||
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
|
||||
enum IMAGE_DITHERING {
|
||||
IMAGE_DITHER_BAYER = 0,
|
||||
IMAGE_DITHER_ATKINSON = 1,
|
||||
IMAGE_DITHER_DIFFUSED_BAYER = 2,
|
||||
IMAGE_DITHERING_COUNT
|
||||
};
|
||||
|
||||
// Timezone options (POSIX TZ rules for DST support)
|
||||
enum TIMEZONE {
|
||||
@@ -223,6 +229,8 @@ class CrossPointSettings {
|
||||
uint8_t showHiddenFiles = 0;
|
||||
// Image rendering mode in EPUB reader
|
||||
uint8_t imageRendering = IMAGES_DISPLAY;
|
||||
// Dithering mode for decoded images (EPUB/JPG/PNG)
|
||||
uint8_t imageDithering = IMAGE_DITHER_BAYER;
|
||||
// Enable synthetic TOC fallback for malformed/sparse TOC books (1 = enabled, 0 = disabled)
|
||||
uint8_t syntheticTocFallback = 1;
|
||||
// Show clock in the reader status bar
|
||||
|
||||
@@ -69,6 +69,10 @@ inline const std::vector<SettingInfo>& getSettingsList() {
|
||||
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
|
||||
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
|
||||
"imageRendering", StrId::STR_CAT_READER),
|
||||
SettingInfo::Enum(
|
||||
StrId::STR_IMAGE_DITHERING, &CrossPointSettings::imageDithering,
|
||||
{StrId::STR_IMAGE_DITHER_BAYER, StrId::STR_IMAGE_DITHER_ATKINSON, StrId::STR_IMAGE_DITHER_DIFFUSED_BAYER},
|
||||
"imageDithering", StrId::STR_CAT_READER),
|
||||
SettingInfo::Toggle(StrId::STR_CREATE_FALLBACK_FOR_INVALID_TOC, &CrossPointSettings::syntheticTocFallback,
|
||||
"syntheticTocFallback", StrId::STR_CAT_READER),
|
||||
// --- Controls ---
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
namespace {
|
||||
constexpr const char* SLEEP_BMP_PATH = "/sleep.bmp";
|
||||
|
||||
uint8_t normalizeImageDitherModeValue(uint8_t mode) { return static_cast<uint8_t>(imageDitherModeFromSetting(mode)); }
|
||||
|
||||
bool isBmpFile(const std::string& path) { return FsHelpers::hasBmpExtension(path); }
|
||||
|
||||
bool isSupportedImageFile(const std::string& path) {
|
||||
@@ -53,7 +55,13 @@ void computeCenteredImagePlacement(const int imageWidth, const int imageHeight,
|
||||
} // namespace
|
||||
|
||||
BmpViewerActivity::BmpViewerActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string path)
|
||||
: Activity("BmpViewer", renderer, mappedInput), filePath(std::move(path)) {}
|
||||
: Activity("BmpViewer", renderer, mappedInput),
|
||||
filePath(std::move(path)),
|
||||
imageDitherMode(normalizeImageDitherModeValue(SETTINGS.imageDithering)) {}
|
||||
|
||||
bool BmpViewerActivity::renderCurrentImage(const bool showControls) {
|
||||
return isBmpFile(filePath) ? renderBmpImage(showControls) : renderDecodedImage(showControls);
|
||||
}
|
||||
|
||||
void BmpViewerActivity::onEnter() {
|
||||
Activity::onEnter();
|
||||
@@ -62,7 +70,7 @@ void BmpViewerActivity::onEnter() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool rendered = isBmpFile(filePath) ? renderBmpImage() : renderDecodedImage();
|
||||
const bool rendered = renderCurrentImage();
|
||||
if (!rendered) {
|
||||
renderError("Could not render image");
|
||||
}
|
||||
@@ -100,7 +108,8 @@ bool BmpViewerActivity::renderBmpImage(const bool showControls) {
|
||||
renderer.clearScreen();
|
||||
renderer.drawBitmap(bitmap, x, y, pageWidth, pageHeight, 0, 0);
|
||||
if (showControls) {
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", tr(STR_SET_SLEEP_SCREEN));
|
||||
const auto labels =
|
||||
mappedInput.mapLabels(tr(STR_BACK), "", I18N.get(getCurrentDitherModeLabel()), tr(STR_SET_SLEEP_SCREEN));
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
}
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
@@ -139,19 +148,44 @@ bool BmpViewerActivity::renderDecodedImage(const bool showControls) {
|
||||
config.useExactDimensions = true;
|
||||
config.useGrayscale = true;
|
||||
config.useDithering = true;
|
||||
config.ditherMode = imageDitherModeFromSetting(imageDitherMode);
|
||||
|
||||
if (!decoder->decodeToFramebuffer(filePath, renderer, config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (showControls) {
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", tr(STR_SET_SLEEP_SCREEN));
|
||||
const auto labels =
|
||||
mappedInput.mapLabels(tr(STR_BACK), "", I18N.get(getCurrentDitherModeLabel()), tr(STR_SET_SLEEP_SCREEN));
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
}
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
return true;
|
||||
}
|
||||
|
||||
StrId BmpViewerActivity::getCurrentDitherModeLabel() const {
|
||||
switch (imageDitherModeFromSetting(imageDitherMode)) {
|
||||
case ImageDitherMode::Atkinson:
|
||||
return StrId::STR_IMAGE_DITHER_ATKINSON;
|
||||
case ImageDitherMode::DiffusedBayer:
|
||||
return StrId::STR_IMAGE_DITHER_DIFFUSED_BAYER;
|
||||
case ImageDitherMode::Bayer:
|
||||
case ImageDitherMode::COUNT:
|
||||
default:
|
||||
return StrId::STR_IMAGE_DITHER_BAYER;
|
||||
}
|
||||
}
|
||||
|
||||
void BmpViewerActivity::cycleDitherMode() {
|
||||
imageDitherMode = (imageDitherMode + 1) % CrossPointSettings::IMAGE_DITHERING_COUNT;
|
||||
SETTINGS.imageDithering = imageDitherMode;
|
||||
SETTINGS.saveToFile();
|
||||
|
||||
if (!renderCurrentImage()) {
|
||||
renderError("Could not render image");
|
||||
}
|
||||
}
|
||||
|
||||
void BmpViewerActivity::renderError(const char* message) {
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
renderer.clearScreen();
|
||||
@@ -210,6 +244,11 @@ void BmpViewerActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Left)) {
|
||||
cycleDitherMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// Next/Right button: set this image as the sleep screen
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Right)) {
|
||||
setAsSleepScreen();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Epub/converters/ImageToFramebufferDecoder.h>
|
||||
#include <I18nKeys.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
@@ -16,8 +19,12 @@ class BmpViewerActivity final : public Activity {
|
||||
|
||||
private:
|
||||
std::string filePath;
|
||||
uint8_t imageDitherMode;
|
||||
bool renderCurrentImage(bool showControls = true);
|
||||
bool renderBmpImage(bool showControls = true);
|
||||
bool renderDecodedImage(bool showControls = true);
|
||||
void cycleDitherMode();
|
||||
StrId getCurrentDitherModeLabel() const;
|
||||
void renderError(const char* message);
|
||||
void setAsSleepScreen();
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user