mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 22:57:50 +03:00
Greyscale UI methods
This commit is contained in:
parent
06ce25f0cd
commit
5ecc277824
@ -250,6 +250,55 @@ void GfxRenderer::fillRect(const int x, const int y, const int width, const int
|
||||
}
|
||||
}
|
||||
|
||||
// Use Bayer matrix 4x4 dithering to fill the rectangle with a grey level - 0 white to 15 black
|
||||
void GfxRenderer::fillRectGrey(const int x, const int y, const int width, const int height, const int greyLevel) const {
|
||||
static constexpr uint8_t bayer4x4[4][4] = {
|
||||
{0, 8, 2, 10},
|
||||
{12, 4, 14, 6},
|
||||
{3, 11, 1, 9},
|
||||
{15, 7, 13, 5},
|
||||
};
|
||||
static constexpr int matrixSize = 4;
|
||||
static constexpr int matrixLevels = matrixSize * matrixSize;
|
||||
|
||||
const int normalizedGrey = (greyLevel * 255) / (matrixLevels - 1);
|
||||
const int clampedGrey = std::max(0, std::min(normalizedGrey, 255));
|
||||
const int threshold = (clampedGrey * (matrixLevels + 1)) / 256;
|
||||
|
||||
for (int dy = 0; dy < height; ++dy) {
|
||||
const int screenY = y + dy;
|
||||
const int matrixY = screenY & (matrixSize - 1);
|
||||
for (int dx = 0; dx < width; ++dx) {
|
||||
const int screenX = x + dx;
|
||||
const int matrixX = screenX & (matrixSize - 1);
|
||||
const uint8_t patternValue = bayer4x4[matrixY][matrixX];
|
||||
const bool black = patternValue < threshold;
|
||||
drawPixel(screenX, screenY, black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Color -1 white, 0 clear, 1 black
|
||||
void GfxRenderer::fillArc(const int maxRadius, const int cx, const int cy, const int xDir, const int yDir, const int insideColor, const int outsideColor) const {
|
||||
const int radiusSq = maxRadius * maxRadius;
|
||||
for (int dy = 0; dy <= maxRadius; ++dy) {
|
||||
for (int dx = 0; dx <= maxRadius; ++dx) {
|
||||
const int distSq = dx * dx + dy * dy;
|
||||
const int px = cx + xDir * dx;
|
||||
const int py = cy + yDir * dy;
|
||||
if (distSq > radiusSq) {
|
||||
if (outsideColor != 0) {
|
||||
drawPixel(px, py, outsideColor == 1);
|
||||
}
|
||||
} else {
|
||||
if (insideColor != 0) {
|
||||
drawPixel(px, py, insideColor == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const {
|
||||
// Flip X and Y for portrait mode
|
||||
einkDisplay.drawImage(bitmap, y, x, height, width);
|
||||
|
||||
@ -51,6 +51,8 @@ class GfxRenderer {
|
||||
void drawArc(int maxRadius, int cx, int cy, int xDir, int yDir, int lineWidth, bool state) const;
|
||||
void drawRoundedRect(int x, int y, int width, int height, int lineWidth, int cornerRadius, bool state) const;
|
||||
void fillRect(int x, int y, int width, int height, bool state = true) const;
|
||||
void fillRectGrey(int x, int y, int width, int height, int greyLevel) const;
|
||||
void fillArc(int maxRadius, int cx, int cy, int xDir, int yDir, int insideColor, int outsideColor) const;
|
||||
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
||||
void drawIcon(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
||||
void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight) const;
|
||||
|
||||
@ -17,7 +17,7 @@ constexpr int TILE_PADDING = 5;
|
||||
constexpr int THUMB_W = 90;
|
||||
constexpr int THUMB_H = 120;
|
||||
constexpr int TILE_TEXT_H = 60;
|
||||
constexpr int gridLeftOffset = 45;
|
||||
constexpr int gridLeftOffset = 37;
|
||||
constexpr int gridTopOffset = 125;
|
||||
} // namespace
|
||||
|
||||
@ -61,7 +61,7 @@ void GridBrowserActivity::loadFiles() {
|
||||
std::string ext = filename.substr(dot);
|
||||
basename = filename.substr(0, dot);
|
||||
// lowercase ext for case-insensitive compare
|
||||
for (char &c : ext) c = (char)tolower(c);
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
|
||||
if (ext == ".epub") {
|
||||
type = F_EPUB;
|
||||
} else if (ext == ".thumb.bmp") {
|
||||
@ -136,7 +136,7 @@ void GridBrowserActivity::loop() {
|
||||
}
|
||||
} else if (inputManager.wasPressed(InputManager::BTN_BACK)) {
|
||||
if (basepath != "/") {
|
||||
basepath = basepath.substr(0, basepath.rfind('/'));
|
||||
basepath.resize(basepath.rfind('/'));
|
||||
if (basepath.empty()) basepath = "/";
|
||||
loadFiles();
|
||||
renderRequired = true;
|
||||
@ -197,9 +197,9 @@ void GridBrowserActivity::render(bool clear) const {
|
||||
auto folderName = basepath == "/" ? "SD card" : basepath.substr(basepath.rfind('/') + 1).c_str();
|
||||
drawFullscreenWindowFrame(renderer, folderName);
|
||||
}
|
||||
bool hasGeyscaleBitmaps = false;
|
||||
|
||||
if (!files.empty()) {
|
||||
bool hasGeyscaleBitmaps = false;
|
||||
for (int pass = 0; pass < 3; pass++) {
|
||||
if (pass > 0) {
|
||||
renderer.clearScreen(0x00);
|
||||
@ -255,7 +255,7 @@ void GridBrowserActivity::render(bool clear) const {
|
||||
}
|
||||
} else if (pass == 1) {
|
||||
renderer.copyGrayscaleLsbBuffers();
|
||||
} else if (pass == 2) {
|
||||
} else {
|
||||
renderer.copyGrayscaleMsbBuffers();
|
||||
renderer.displayGrayBuffer();
|
||||
renderer.setRenderMode(GfxRenderer::BW);
|
||||
|
||||
@ -30,7 +30,7 @@ class GridBrowserActivity final : public Activity {
|
||||
std::vector<FileInfo> files;
|
||||
int selectorIndex = 0;
|
||||
int previousSelectorIndex = -1;
|
||||
int page;
|
||||
int page = 0;
|
||||
bool updateRequired = false;
|
||||
bool renderRequired = false;
|
||||
const std::function<void(const std::string&)> onSelect;
|
||||
|
||||
@ -14,6 +14,13 @@ constexpr int batteryHeight = 10;
|
||||
|
||||
void drawWindowFrame(GfxRenderer& renderer, int xMargin, int y, int height, bool hasShadow, const char* title) {
|
||||
const int windowWidth = GfxRenderer::getScreenWidth() - 2 * xMargin;
|
||||
|
||||
if (title) { // Header background
|
||||
renderer.fillRectGrey(xMargin, y, windowWidth, windowHeaderHeight, 5);
|
||||
renderer.fillArc(windowCornerRadius, xMargin + windowCornerRadius, y + windowCornerRadius, -1, -1, 0, -1); // TL
|
||||
renderer.fillArc(windowCornerRadius, windowWidth + xMargin - windowCornerRadius, y + windowCornerRadius, 1, -1, 0, -1); // TR
|
||||
}
|
||||
|
||||
renderer.drawRoundedRect(xMargin, y, windowWidth, height, windowBorderWidth, windowCornerRadius, true);
|
||||
|
||||
if (hasShadow) {
|
||||
@ -43,7 +50,6 @@ void drawStatusBar(GfxRenderer& renderer) {
|
||||
// Left aligned battery icon and percentage
|
||||
const uint16_t percentage = battery.readPercentage();
|
||||
const auto percentageText = std::to_string(percentage) + "%";
|
||||
const auto percentageTextWidth = renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str());
|
||||
renderer.drawText(SMALL_FONT_ID, fullscreenWindowMargin + batteryWidth + 5, textY, percentageText.c_str());
|
||||
|
||||
// 1 column on left, 2 columns on right, 5 columns of battery body
|
||||
|
||||
Loading…
Reference in New Issue
Block a user