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 {
|
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
|
// Flip X and Y for portrait mode
|
||||||
einkDisplay.drawImage(bitmap, y, x, height, width);
|
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 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 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 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 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 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;
|
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_W = 90;
|
||||||
constexpr int THUMB_H = 120;
|
constexpr int THUMB_H = 120;
|
||||||
constexpr int TILE_TEXT_H = 60;
|
constexpr int TILE_TEXT_H = 60;
|
||||||
constexpr int gridLeftOffset = 45;
|
constexpr int gridLeftOffset = 37;
|
||||||
constexpr int gridTopOffset = 125;
|
constexpr int gridTopOffset = 125;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ void GridBrowserActivity::loadFiles() {
|
|||||||
std::string ext = filename.substr(dot);
|
std::string ext = filename.substr(dot);
|
||||||
basename = filename.substr(0, dot);
|
basename = filename.substr(0, dot);
|
||||||
// lowercase ext for case-insensitive compare
|
// 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") {
|
if (ext == ".epub") {
|
||||||
type = F_EPUB;
|
type = F_EPUB;
|
||||||
} else if (ext == ".thumb.bmp") {
|
} else if (ext == ".thumb.bmp") {
|
||||||
@ -136,7 +136,7 @@ void GridBrowserActivity::loop() {
|
|||||||
}
|
}
|
||||||
} else if (inputManager.wasPressed(InputManager::BTN_BACK)) {
|
} else if (inputManager.wasPressed(InputManager::BTN_BACK)) {
|
||||||
if (basepath != "/") {
|
if (basepath != "/") {
|
||||||
basepath = basepath.substr(0, basepath.rfind('/'));
|
basepath.resize(basepath.rfind('/'));
|
||||||
if (basepath.empty()) basepath = "/";
|
if (basepath.empty()) basepath = "/";
|
||||||
loadFiles();
|
loadFiles();
|
||||||
renderRequired = true;
|
renderRequired = true;
|
||||||
@ -197,9 +197,9 @@ void GridBrowserActivity::render(bool clear) const {
|
|||||||
auto folderName = basepath == "/" ? "SD card" : basepath.substr(basepath.rfind('/') + 1).c_str();
|
auto folderName = basepath == "/" ? "SD card" : basepath.substr(basepath.rfind('/') + 1).c_str();
|
||||||
drawFullscreenWindowFrame(renderer, folderName);
|
drawFullscreenWindowFrame(renderer, folderName);
|
||||||
}
|
}
|
||||||
bool hasGeyscaleBitmaps = false;
|
|
||||||
|
|
||||||
if (!files.empty()) {
|
if (!files.empty()) {
|
||||||
|
bool hasGeyscaleBitmaps = false;
|
||||||
for (int pass = 0; pass < 3; pass++) {
|
for (int pass = 0; pass < 3; pass++) {
|
||||||
if (pass > 0) {
|
if (pass > 0) {
|
||||||
renderer.clearScreen(0x00);
|
renderer.clearScreen(0x00);
|
||||||
@ -255,7 +255,7 @@ void GridBrowserActivity::render(bool clear) const {
|
|||||||
}
|
}
|
||||||
} else if (pass == 1) {
|
} else if (pass == 1) {
|
||||||
renderer.copyGrayscaleLsbBuffers();
|
renderer.copyGrayscaleLsbBuffers();
|
||||||
} else if (pass == 2) {
|
} else {
|
||||||
renderer.copyGrayscaleMsbBuffers();
|
renderer.copyGrayscaleMsbBuffers();
|
||||||
renderer.displayGrayBuffer();
|
renderer.displayGrayBuffer();
|
||||||
renderer.setRenderMode(GfxRenderer::BW);
|
renderer.setRenderMode(GfxRenderer::BW);
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class GridBrowserActivity final : public Activity {
|
|||||||
std::vector<FileInfo> files;
|
std::vector<FileInfo> files;
|
||||||
int selectorIndex = 0;
|
int selectorIndex = 0;
|
||||||
int previousSelectorIndex = -1;
|
int previousSelectorIndex = -1;
|
||||||
int page;
|
int page = 0;
|
||||||
bool updateRequired = false;
|
bool updateRequired = false;
|
||||||
bool renderRequired = false;
|
bool renderRequired = false;
|
||||||
const std::function<void(const std::string&)> onSelect;
|
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) {
|
void drawWindowFrame(GfxRenderer& renderer, int xMargin, int y, int height, bool hasShadow, const char* title) {
|
||||||
const int windowWidth = GfxRenderer::getScreenWidth() - 2 * xMargin;
|
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);
|
renderer.drawRoundedRect(xMargin, y, windowWidth, height, windowBorderWidth, windowCornerRadius, true);
|
||||||
|
|
||||||
if (hasShadow) {
|
if (hasShadow) {
|
||||||
@ -43,7 +50,6 @@ void drawStatusBar(GfxRenderer& renderer) {
|
|||||||
// Left aligned battery icon and percentage
|
// Left aligned battery icon and percentage
|
||||||
const uint16_t percentage = battery.readPercentage();
|
const uint16_t percentage = battery.readPercentage();
|
||||||
const auto percentageText = std::to_string(percentage) + "%";
|
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());
|
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
|
// 1 column on left, 2 columns on right, 5 columns of battery body
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user