mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-06 23:57:39 +03:00
Compare commits
4 Commits
72de88c3bf
...
7bfb3af0da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bfb3af0da | ||
|
|
e6f6f14b37 | ||
|
|
a4310bf2c3 | ||
|
|
75cc861bd0 |
@ -805,10 +805,11 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
|
|||||||
free(rowBytes);
|
free(rowBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxRenderer::drawTransparentBitmap(const Bitmap& bitmap, const int x, const int y, const int w, const int h) const {
|
void GfxRenderer::drawTransparentBitmap(const Bitmap& bitmap, const int x, const int y, const int w,
|
||||||
|
const int h) const {
|
||||||
// Similar to drawBitmap1Bit but strictly skips 1s (white) in the source 1-bit data
|
// Similar to drawBitmap1Bit but strictly skips 1s (white) in the source 1-bit data
|
||||||
// The Bitmap reader returns 2-bit packed data where 0-2=Black and 3=White for 1-bit sources
|
// The Bitmap reader returns 2-bit packed data where 0-2=Black and 3=White for 1-bit sources
|
||||||
|
|
||||||
float scale = 1.0f;
|
float scale = 1.0f;
|
||||||
bool isScaled = false;
|
bool isScaled = false;
|
||||||
if (w > 0) {
|
if (w > 0) {
|
||||||
@ -839,11 +840,11 @@ void GfxRenderer::drawTransparentBitmap(const Bitmap& bitmap, const int x, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int bmpYOffset = bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY;
|
const int bmpYOffset = bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY;
|
||||||
|
|
||||||
// Calculate target Y span
|
// Calculate target Y span
|
||||||
int startY = y + static_cast<int>(std::floor(bmpYOffset * scale));
|
int startY = y + static_cast<int>(std::floor(bmpYOffset * scale));
|
||||||
int endY = y + static_cast<int>(std::floor((bmpYOffset + 1) * scale));
|
int endY = y + static_cast<int>(std::floor((bmpYOffset + 1) * scale));
|
||||||
|
|
||||||
// Clamp to screen
|
// Clamp to screen
|
||||||
if (startY < 0) startY = 0;
|
if (startY < 0) startY = 0;
|
||||||
if (endY > getScreenHeight()) endY = getScreenHeight();
|
if (endY > getScreenHeight()) endY = getScreenHeight();
|
||||||
@ -853,7 +854,7 @@ void GfxRenderer::drawTransparentBitmap(const Bitmap& bitmap, const int x, const
|
|||||||
// Calculate target X span
|
// Calculate target X span
|
||||||
int startX = x + static_cast<int>(std::floor(bmpX * scale));
|
int startX = x + static_cast<int>(std::floor(bmpX * scale));
|
||||||
int endX = x + static_cast<int>(std::floor((bmpX + 1) * scale));
|
int endX = x + static_cast<int>(std::floor((bmpX + 1) * scale));
|
||||||
|
|
||||||
if (startX < 0) startX = 0;
|
if (startX < 0) startX = 0;
|
||||||
if (endX > getScreenWidth()) endX = getScreenWidth();
|
if (endX > getScreenWidth()) endX = getScreenWidth();
|
||||||
if (startX >= endX) continue;
|
if (startX >= endX) continue;
|
||||||
@ -864,7 +865,7 @@ void GfxRenderer::drawTransparentBitmap(const Bitmap& bitmap, const int x, const
|
|||||||
if (val < 3) {
|
if (val < 3) {
|
||||||
for (int sy = startY; sy < endY; sy++) {
|
for (int sy = startY; sy < endY; sy++) {
|
||||||
for (int sx = startX; sx < endX; sx++) {
|
for (int sx = startX; sx < endX; sx++) {
|
||||||
drawPixel(sx, sy, true); // Black
|
drawPixel(sx, sy, true); // Black
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -945,11 +946,11 @@ void GfxRenderer::drawRoundedBitmap(const Bitmap& bitmap, const int x, const int
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int bmpYOffset = bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY;
|
const int bmpYOffset = bitmap.isTopDown() ? bmpY : bitmap.getHeight() - 1 - bmpY;
|
||||||
|
|
||||||
// Calculate target Y span
|
// Calculate target Y span
|
||||||
int startY = y + static_cast<int>(std::floor(bmpYOffset * scale));
|
int startY = y + static_cast<int>(std::floor(bmpYOffset * scale));
|
||||||
int endY = y + static_cast<int>(std::floor((bmpYOffset + 1) * scale));
|
int endY = y + static_cast<int>(std::floor((bmpYOffset + 1) * scale));
|
||||||
|
|
||||||
if (startY < 0) startY = 0;
|
if (startY < 0) startY = 0;
|
||||||
if (endY > getScreenHeight()) endY = getScreenHeight();
|
if (endY > getScreenHeight()) endY = getScreenHeight();
|
||||||
if (startY >= endY) continue;
|
if (startY >= endY) continue;
|
||||||
@ -957,7 +958,7 @@ void GfxRenderer::drawRoundedBitmap(const Bitmap& bitmap, const int x, const int
|
|||||||
for (int bmpX = 0; bmpX < bitmap.getWidth(); bmpX++) {
|
for (int bmpX = 0; bmpX < bitmap.getWidth(); bmpX++) {
|
||||||
int startX = x + static_cast<int>(std::floor(bmpX * scale));
|
int startX = x + static_cast<int>(std::floor(bmpX * scale));
|
||||||
int endX = x + static_cast<int>(std::floor((bmpX + 1) * scale));
|
int endX = x + static_cast<int>(std::floor((bmpX + 1) * scale));
|
||||||
|
|
||||||
if (startX < 0) startX = 0;
|
if (startX < 0) startX = 0;
|
||||||
if (endX > getScreenWidth()) endX = getScreenWidth();
|
if (endX > getScreenWidth()) endX = getScreenWidth();
|
||||||
if (startX >= endX) continue;
|
if (startX >= endX) continue;
|
||||||
@ -968,7 +969,7 @@ void GfxRenderer::drawRoundedBitmap(const Bitmap& bitmap, const int x, const int
|
|||||||
if (renderMode == BW) {
|
if (renderMode == BW) {
|
||||||
pixelBlack = (val < 2);
|
pixelBlack = (val < 2);
|
||||||
} else if (renderMode == GRAYSCALE_MSB) {
|
} else if (renderMode == GRAYSCALE_MSB) {
|
||||||
pixelBlack = (val < 3); // Draw all non-white as black for icons/covers
|
pixelBlack = (val < 3); // Draw all non-white as black for icons/covers
|
||||||
} else if (renderMode == GRAYSCALE_LSB) {
|
} else if (renderMode == GRAYSCALE_LSB) {
|
||||||
pixelBlack = (val == 0);
|
pixelBlack = (val == 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,7 +78,8 @@ class GfxRenderer {
|
|||||||
float cropY = 0) const;
|
float cropY = 0) const;
|
||||||
void drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y, const int maxWidth, const int maxHeight) const;
|
void drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y, const int maxWidth, const int maxHeight) const;
|
||||||
void drawTransparentBitmap(const Bitmap& bitmap, const int x, const int y, const int w, const int h) const;
|
void drawTransparentBitmap(const Bitmap& bitmap, const int x, const int y, const int w, const int h) const;
|
||||||
void drawRoundedBitmap(const Bitmap& bitmap, const int x, const int y, const int w, const int h, const int radius) const;
|
void drawRoundedBitmap(const Bitmap& bitmap, const int x, const int y, const int w, const int h,
|
||||||
|
const int radius) const;
|
||||||
void draw2BitImage(const uint8_t data[], int x, int y, int w, int h) const;
|
void draw2BitImage(const uint8_t data[], int x, int y, int w, int h) const;
|
||||||
void fillPolygon(const int* xPoints, const int* yPoints, int numPoints, bool state = true) const;
|
void fillPolygon(const int* xPoints, const int* yPoints, int numPoints, bool state = true) const;
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ class Container : public UIElement {
|
|||||||
int borderRadius = 0; // Corner radius (for future rounded rect support)
|
int borderRadius = 0; // Corner radius (for future rounded rect support)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Container(const std::string& id) : UIElement(id) { bgColorExpr = Expression::parse("0xFF"); }
|
explicit Container(const std::string& id) : UIElement(id), bgColorExpr(Expression::parse("0xFF")) {}
|
||||||
virtual ~Container() {
|
virtual ~Container() {
|
||||||
for (auto child : children) delete child;
|
for (auto child : children) delete child;
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ class Rectangle : public UIElement {
|
|||||||
Expression colorExpr;
|
Expression colorExpr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Rectangle(const std::string& id) : UIElement(id) { colorExpr = Expression::parse("0x00"); }
|
explicit Rectangle(const std::string& id) : UIElement(id), colorExpr(Expression::parse("0x00")) {}
|
||||||
ElementType getType() const override { return ElementType::Rectangle; }
|
ElementType getType() const override { return ElementType::Rectangle; }
|
||||||
|
|
||||||
void setFill(bool f) {
|
void setFill(bool f) {
|
||||||
@ -199,7 +199,7 @@ class Label : public UIElement {
|
|||||||
bool ellipsis = true; // Truncate with ... if too long
|
bool ellipsis = true; // Truncate with ... if too long
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Label(const std::string& id) : UIElement(id) { colorExpr = Expression::parse("0x00"); }
|
explicit Label(const std::string& id) : UIElement(id), colorExpr(Expression::parse("0x00")) {}
|
||||||
ElementType getType() const override { return ElementType::Label; }
|
ElementType getType() const override { return ElementType::Label; }
|
||||||
|
|
||||||
void setText(const std::string& expr) {
|
void setText(const std::string& expr) {
|
||||||
@ -262,67 +262,67 @@ class Label : public UIElement {
|
|||||||
// Binary search for cut point
|
// Binary search for cut point
|
||||||
int len = remaining.length();
|
int len = remaining.length();
|
||||||
int cut = len;
|
int cut = len;
|
||||||
|
|
||||||
// Find split point
|
// Find split point
|
||||||
// Optimistic start: approximate chars that fit
|
// Optimistic start: approximate chars that fit
|
||||||
int avgCharWidth = renderer.getTextWidth(fontId, "a");
|
int avgCharWidth = renderer.getTextWidth(fontId, "a");
|
||||||
if (avgCharWidth < 1) avgCharWidth = 8;
|
if (avgCharWidth < 1) avgCharWidth = 8;
|
||||||
int approxChars = absW / avgCharWidth;
|
int approxChars = absW / avgCharWidth;
|
||||||
if (approxChars < 1) approxChars = 1;
|
if (approxChars < 1) approxChars = 1;
|
||||||
if (approxChars >= len) approxChars = len - 1;
|
if (approxChars >= len) approxChars = len - 1;
|
||||||
|
|
||||||
// Refine from approxChars
|
// Refine from approxChars
|
||||||
int w = renderer.getTextWidth(fontId, remaining.substr(0, approxChars).c_str());
|
int w = renderer.getTextWidth(fontId, remaining.substr(0, approxChars).c_str());
|
||||||
if (w < absW) {
|
if (w < absW) {
|
||||||
// Grow
|
// Grow
|
||||||
for (int i = approxChars; i <= len; i++) {
|
for (int i = approxChars; i <= len; i++) {
|
||||||
if (renderer.getTextWidth(fontId, remaining.substr(0, i).c_str()) > absW) {
|
if (renderer.getTextWidth(fontId, remaining.substr(0, i).c_str()) > absW) {
|
||||||
cut = i - 1;
|
cut = i - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cut = i;
|
cut = i;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Shrink
|
// Shrink
|
||||||
for (int i = approxChars; i > 0; i--) {
|
for (int i = approxChars; i > 0; i--) {
|
||||||
if (renderer.getTextWidth(fontId, remaining.substr(0, i).c_str()) <= absW) {
|
if (renderer.getTextWidth(fontId, remaining.substr(0, i).c_str()) <= absW) {
|
||||||
cut = i;
|
cut = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find last space before cut
|
// Find last space before cut
|
||||||
if (cut < (int)remaining.length()) {
|
if (cut < (int)remaining.length()) {
|
||||||
int space = -1;
|
int space = -1;
|
||||||
for (int i = cut; i > 0; i--) {
|
for (int i = cut; i > 0; i--) {
|
||||||
if (remaining[i] == ' ') {
|
if (remaining[i] == ' ') {
|
||||||
space = i;
|
space = i;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (space != -1) cut = space;
|
}
|
||||||
|
if (space != -1) cut = space;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line = remaining.substr(0, cut);
|
std::string line = remaining.substr(0, cut);
|
||||||
|
|
||||||
// If we're at the last allowed line but still have more text
|
// If we're at the last allowed line but still have more text
|
||||||
if ((int)lines.size() == maxLines - 1 && cut < (int)remaining.length()) {
|
if ((int)lines.size() == maxLines - 1 && cut < (int)remaining.length()) {
|
||||||
if (ellipsis) {
|
if (ellipsis) {
|
||||||
line = renderer.truncatedText(fontId, remaining.c_str(), absW);
|
line = renderer.truncatedText(fontId, remaining.c_str(), absW);
|
||||||
}
|
}
|
||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
// Advance
|
// Advance
|
||||||
if (cut < (int)remaining.length()) {
|
if (cut < (int)remaining.length()) {
|
||||||
// Skip the space if check
|
// Skip the space if check
|
||||||
if (remaining[cut] == ' ') cut++;
|
if (remaining[cut] == ' ') cut++;
|
||||||
remaining = remaining.substr(cut);
|
remaining = remaining.substr(cut);
|
||||||
} else {
|
} else {
|
||||||
remaining = "";
|
remaining = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -332,29 +332,29 @@ class Label : public UIElement {
|
|||||||
}
|
}
|
||||||
lines.push_back(finalText);
|
lines.push_back(finalText);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw lines
|
// Draw lines
|
||||||
int totalTextHeight = lines.size() * lineHeight;
|
int totalTextHeight = lines.size() * lineHeight;
|
||||||
int startY = absY;
|
int startY = absY;
|
||||||
|
|
||||||
// Vertical centering
|
// Vertical centering
|
||||||
if (absH > 0 && totalTextHeight < absH) {
|
if (absH > 0 && totalTextHeight < absH) {
|
||||||
startY = absY + (absH - totalTextHeight) / 2;
|
startY = absY + (absH - totalTextHeight) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < lines.size(); i++) {
|
for (size_t i = 0; i < lines.size(); i++) {
|
||||||
int lineWidth = renderer.getTextWidth(fontId, lines[i].c_str());
|
int lineWidth = renderer.getTextWidth(fontId, lines[i].c_str());
|
||||||
int drawX = absX;
|
int drawX = absX;
|
||||||
|
|
||||||
if (alignment == Alignment::Center && absW > 0) {
|
if (alignment == Alignment::Center && absW > 0) {
|
||||||
drawX = absX + (absW - lineWidth) / 2;
|
drawX = absX + (absW - lineWidth) / 2;
|
||||||
} else if (alignment == Alignment::Right && absW > 0) {
|
} else if (alignment == Alignment::Right && absW > 0) {
|
||||||
drawX = absX + absW - lineWidth;
|
drawX = absX + absW - lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.drawText(fontId, drawX, startY + i * lineHeight, lines[i].c_str(), black);
|
renderer.drawText(fontId, drawX, startY + i * lineHeight, lines[i].c_str(), black);
|
||||||
}
|
}
|
||||||
|
|
||||||
markClean();
|
markClean();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -367,7 +367,7 @@ class BitmapElement : public UIElement {
|
|||||||
int borderRadius = 0;
|
int borderRadius = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BitmapElement(const std::string& id) : UIElement(id) {
|
explicit BitmapElement(const std::string& id) : UIElement(id) {
|
||||||
cacheable = true; // Bitmaps benefit from caching
|
cacheable = true; // Bitmaps benefit from caching
|
||||||
}
|
}
|
||||||
ElementType getType() const override { return ElementType::Bitmap; }
|
ElementType getType() const override { return ElementType::Bitmap; }
|
||||||
@ -407,12 +407,13 @@ class ProgressBar : public UIElement {
|
|||||||
int borderWidth = 1;
|
int borderWidth = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ProgressBar(const std::string& id) : UIElement(id) {
|
explicit ProgressBar(const std::string& id)
|
||||||
valueExpr = Expression::parse("0");
|
: UIElement(id),
|
||||||
maxExpr = Expression::parse("100");
|
valueExpr(Expression::parse("0")),
|
||||||
fgColorExpr = Expression::parse("0x00"); // Black fill
|
maxExpr(Expression::parse("100")),
|
||||||
bgColorExpr = Expression::parse("0xFF"); // White background
|
fgColorExpr(Expression::parse("0x00")), // Black fill
|
||||||
}
|
bgColorExpr(Expression::parse("0xFF")) // White background
|
||||||
|
{}
|
||||||
|
|
||||||
ElementType getType() const override { return ElementType::ProgressBar; }
|
ElementType getType() const override { return ElementType::ProgressBar; }
|
||||||
|
|
||||||
@ -480,7 +481,7 @@ class Divider : public UIElement {
|
|||||||
int thickness = 1;
|
int thickness = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Divider(const std::string& id) : UIElement(id) { colorExpr = Expression::parse("0x00"); }
|
explicit Divider(const std::string& id) : UIElement(id), colorExpr(Expression::parse("0x00")) {}
|
||||||
|
|
||||||
ElementType getType() const override { return ElementType::Divider; }
|
ElementType getType() const override { return ElementType::Divider; }
|
||||||
|
|
||||||
@ -524,9 +525,9 @@ class BatteryIcon : public UIElement {
|
|||||||
Expression colorExpr;
|
Expression colorExpr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BatteryIcon(const std::string& id) : UIElement(id) {
|
explicit BatteryIcon(const std::string& id)
|
||||||
valueExpr = Expression::parse("0");
|
: UIElement(id), valueExpr(Expression::parse("0")), colorExpr(Expression::parse("0x00")) {
|
||||||
colorExpr = Expression::parse("0x00"); // Black by default
|
// Black by default
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementType getType() const override { return ElementType::BatteryIcon; }
|
ElementType getType() const override { return ElementType::BatteryIcon; }
|
||||||
|
|||||||
@ -83,7 +83,7 @@ class ThemeContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ThemeContext(const ThemeContext* parent = nullptr) : parent(parent) {}
|
explicit ThemeContext(const ThemeContext* parent = nullptr) : parent(parent) {}
|
||||||
|
|
||||||
void setString(const std::string& key, const std::string& value) { strings[key] = value; }
|
void setString(const std::string& key, const std::string& value) { strings[key] = value; }
|
||||||
void setInt(const std::string& key, int value) { ints[key] = value; }
|
void setInt(const std::string& key, int value) { ints[key] = value; }
|
||||||
|
|||||||
@ -34,7 +34,7 @@ struct Dimension {
|
|||||||
struct Color {
|
struct Color {
|
||||||
uint8_t value; // For E-Ink: 0 (Black) to 255 (White), or simplified palette
|
uint8_t value; // For E-Ink: 0 (Black) to 255 (White), or simplified palette
|
||||||
|
|
||||||
Color(uint8_t v) : value(v) {}
|
explicit Color(uint8_t v) : value(v) {}
|
||||||
Color() : value(0) {}
|
Color() : value(0) {}
|
||||||
|
|
||||||
static Color parse(const std::string& str) {
|
static Color parse(const std::string& str) {
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class UIElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UIElement(const std::string& id) : id(id) { visibleExpr = Expression::parse("true"); }
|
UIElement(const std::string& id) : id(id), visibleExpr(Expression::parse("true")) {}
|
||||||
|
|
||||||
virtual ~UIElement() {
|
virtual ~UIElement() {
|
||||||
if (cachedRender) {
|
if (cachedRender) {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
#include "BasicElements.h"
|
#include "BasicElements.h"
|
||||||
|
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
#include "Bitmap.h"
|
#include "Bitmap.h"
|
||||||
#include "ListElement.h"
|
#include "ListElement.h"
|
||||||
#include "ThemeManager.h"
|
#include "ThemeManager.h"
|
||||||
#include "ThemeTypes.h"
|
#include "ThemeTypes.h"
|
||||||
#include <GfxRenderer.h>
|
|
||||||
|
|
||||||
namespace ThemeEngine {
|
namespace ThemeEngine {
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ void BitmapElement::draw(const GfxRenderer& renderer, const ThemeContext& contex
|
|||||||
|
|
||||||
// Resolve simplified or relative paths
|
// Resolve simplified or relative paths
|
||||||
if (path.find('/') == std::string::npos || (path.length() > 0 && path[0] != '/')) {
|
if (path.find('/') == std::string::npos || (path.length() > 0 && path[0] != '/')) {
|
||||||
path = ThemeManager::get().getAssetPath(path);
|
path = ThemeManager::get().getAssetPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check if we have a cached 1-bit render
|
// 1. Check if we have a cached 1-bit render
|
||||||
@ -47,46 +48,46 @@ void BitmapElement::draw(const GfxRenderer& renderer, const ThemeContext& contex
|
|||||||
|
|
||||||
// 2. Try Streaming (Absolute paths, large images)
|
// 2. Try Streaming (Absolute paths, large images)
|
||||||
if (path.length() > 0 && path[0] == '/') {
|
if (path.length() > 0 && path[0] == '/') {
|
||||||
FsFile file;
|
FsFile file;
|
||||||
if (SdMan.openFileForRead("HOME", path, file)) {
|
if (SdMan.openFileForRead("HOME", path, file)) {
|
||||||
Bitmap bmp(file, true); // (file, dithering=true)
|
Bitmap bmp(file, true); // (file, dithering=true)
|
||||||
if (bmp.parseHeaders() == BmpReaderError::Ok) {
|
if (bmp.parseHeaders() == BmpReaderError::Ok) {
|
||||||
// Center logic
|
// Center logic
|
||||||
int drawX = absX;
|
int drawX = absX;
|
||||||
int drawY = absY;
|
int drawY = absY;
|
||||||
if (bmp.getWidth() < absW) drawX += (absW - bmp.getWidth()) / 2;
|
if (bmp.getWidth() < absW) drawX += (absW - bmp.getWidth()) / 2;
|
||||||
if (bmp.getHeight() < absH) drawY += (absH - bmp.getHeight()) / 2;
|
if (bmp.getHeight() < absH) drawY += (absH - bmp.getHeight()) / 2;
|
||||||
|
|
||||||
if (borderRadius > 0) {
|
if (borderRadius > 0) {
|
||||||
renderer.drawRoundedBitmap(bmp, drawX, drawY, absW, absH, borderRadius);
|
renderer.drawRoundedBitmap(bmp, drawX, drawY, absW, absH, borderRadius);
|
||||||
} else {
|
} else {
|
||||||
renderer.drawBitmap(bmp, drawX, drawY, absW, absH);
|
renderer.drawBitmap(bmp, drawX, drawY, absW, absH);
|
||||||
}
|
}
|
||||||
drawSuccess = true;
|
drawSuccess = true;
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Fallback to RAM Cache (Standard method)
|
// 3. Fallback to RAM Cache (Standard method)
|
||||||
if (!drawSuccess) {
|
if (!drawSuccess) {
|
||||||
const std::vector<uint8_t>* data = ThemeManager::get().getCachedAsset(path);
|
const std::vector<uint8_t>* data = ThemeManager::get().getCachedAsset(path);
|
||||||
if (data && !data->empty()) {
|
if (data && !data->empty()) {
|
||||||
Bitmap bmp(data->data(), data->size());
|
Bitmap bmp(data->data(), data->size());
|
||||||
if (bmp.parseHeaders() == BmpReaderError::Ok) {
|
if (bmp.parseHeaders() == BmpReaderError::Ok) {
|
||||||
int drawX = absX;
|
int drawX = absX;
|
||||||
int drawY = absY;
|
int drawY = absY;
|
||||||
if (bmp.getWidth() < absW) drawX += (absW - bmp.getWidth()) / 2;
|
if (bmp.getWidth() < absW) drawX += (absW - bmp.getWidth()) / 2;
|
||||||
if (bmp.getHeight() < absH) drawY += (absH - bmp.getHeight()) / 2;
|
if (bmp.getHeight() < absH) drawY += (absH - bmp.getHeight()) / 2;
|
||||||
|
|
||||||
if (borderRadius > 0) {
|
if (borderRadius > 0) {
|
||||||
renderer.drawRoundedBitmap(bmp, drawX, drawY, absW, absH, borderRadius);
|
renderer.drawRoundedBitmap(bmp, drawX, drawY, absW, absH, borderRadius);
|
||||||
} else {
|
} else {
|
||||||
renderer.drawBitmap(bmp, drawX, drawY, absW, absH);
|
renderer.drawBitmap(bmp, drawX, drawY, absW, absH);
|
||||||
}
|
|
||||||
drawSuccess = true;
|
|
||||||
}
|
}
|
||||||
}
|
drawSuccess = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Cache result if successful
|
// 4. Cache result if successful
|
||||||
|
|||||||
@ -229,21 +229,6 @@ bool HomeActivity::storeCoverBuffer() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HomeActivity::restoreCoverBuffer() {
|
|
||||||
if (!coverBuffer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* frameBuffer = renderer.getFrameBuffer();
|
|
||||||
if (!frameBuffer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t bufferSize = GfxRenderer::getBufferSize();
|
|
||||||
memcpy(frameBuffer, coverBuffer, bufferSize);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HomeActivity::freeCoverBuffer() {
|
void HomeActivity::freeCoverBuffer() {
|
||||||
if (coverBuffer) {
|
if (coverBuffer) {
|
||||||
free(coverBuffer);
|
free(coverBuffer);
|
||||||
@ -364,6 +349,74 @@ void HomeActivity::render() {
|
|||||||
context.setBool("HasCover", hasContinueReading && hasCoverImage && !coverBmpPath.empty());
|
context.setBool("HasCover", hasContinueReading && hasCoverImage && !coverBmpPath.empty());
|
||||||
context.setBool("ShowInfoBox", true);
|
context.setBool("ShowInfoBox", true);
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
std::string chapterTitle = "";
|
||||||
|
std::string currentPageStr = "-";
|
||||||
|
std::string totalPagesStr = "-";
|
||||||
|
int progressPercent = 0;
|
||||||
|
|
||||||
|
if (hasContinueReading) {
|
||||||
|
if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".epub")) {
|
||||||
|
Epub epub(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
|
epub.load(false);
|
||||||
|
|
||||||
|
// Read progress
|
||||||
|
FsFile f;
|
||||||
|
if (SdMan.openFileForRead("HOME", epub.getCachePath() + "/progress.bin", f)) {
|
||||||
|
uint8_t data[4];
|
||||||
|
if (f.read(data, 4) == 4) {
|
||||||
|
int spineIndex = data[0] + (data[1] << 8);
|
||||||
|
int spineCount = epub.getSpineItemsCount();
|
||||||
|
|
||||||
|
currentPageStr = std::to_string(spineIndex + 1); // Display 1-based
|
||||||
|
totalPagesStr = std::to_string(spineCount);
|
||||||
|
|
||||||
|
if (spineCount > 0) {
|
||||||
|
progressPercent = (spineIndex * 100) / spineCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Chapter Title
|
||||||
|
auto spineEntry = epub.getSpineItem(spineIndex);
|
||||||
|
if (spineEntry.tocIndex != -1) {
|
||||||
|
auto tocEntry = epub.getTocItem(spineEntry.tocIndex);
|
||||||
|
chapterTitle = tocEntry.title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtc") ||
|
||||||
|
StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtch")) {
|
||||||
|
Xtc xtc(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
|
if (xtc.load()) {
|
||||||
|
// Read progress
|
||||||
|
FsFile f;
|
||||||
|
if (SdMan.openFileForRead("HOME", xtc.getCachePath() + "/progress.bin", f)) {
|
||||||
|
uint8_t data[4];
|
||||||
|
if (f.read(data, 4) == 4) {
|
||||||
|
uint32_t currentPage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
||||||
|
uint32_t totalPages = xtc.getPageCount();
|
||||||
|
|
||||||
|
currentPageStr = std::to_string(currentPage + 1); // 1-based
|
||||||
|
totalPagesStr = std::to_string(totalPages);
|
||||||
|
|
||||||
|
if (totalPages > 0) {
|
||||||
|
progressPercent = (currentPage * 100) / totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
chapterTitle = "Page " + currentPageStr;
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setString("BookChapter", chapterTitle);
|
||||||
|
context.setString("BookCurrentPage", currentPageStr);
|
||||||
|
context.setString("BookTotalPages", totalPagesStr);
|
||||||
|
context.setInt("BookProgressPercent", progressPercent);
|
||||||
|
context.setString("BookProgressPercentStr", std::to_string(progressPercent));
|
||||||
|
|
||||||
// --- Main Menu Data ---
|
// --- Main Menu Data ---
|
||||||
// Menu items start after the book slot
|
// Menu items start after the book slot
|
||||||
const int menuStartIdx = navBookCount;
|
const int menuStartIdx = navBookCount;
|
||||||
|
|||||||
@ -46,8 +46,8 @@ class HomeActivity final : public Activity {
|
|||||||
[[noreturn]] void displayTaskLoop();
|
[[noreturn]] void displayTaskLoop();
|
||||||
void render();
|
void render();
|
||||||
int getMenuItemCount() const;
|
int getMenuItemCount() const;
|
||||||
bool storeCoverBuffer(); // Store frame buffer for cover image
|
bool storeCoverBuffer(); // Store frame buffer for cover image
|
||||||
bool restoreCoverBuffer(); // Restore frame buffer from stored cover
|
|
||||||
void freeCoverBuffer(); // Free the stored cover buffer
|
void freeCoverBuffer(); // Free the stored cover buffer
|
||||||
void loadRecentBooksData(); // Load and cache recent books data
|
void loadRecentBooksData(); // Load and cache recent books data
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user