Xteink-X4-crosspoint-reader/lib/hal/HalDisplay.cpp
2026-01-22 20:16:35 +01:00

213 lines
6.0 KiB
C++

#include <HalDisplay.h>
#include <string>
std::string base64_encode(char* buf, unsigned int bufLen);
HalDisplay::HalDisplay(int8_t sclk, int8_t mosi, int8_t cs, int8_t dc, int8_t rst, int8_t busy) : einkDisplay(sclk, mosi, cs, dc, rst, busy) {
if (is_emulated) {
emuFramebuffer0 = new uint8_t[BUFFER_SIZE];
}
}
HalDisplay::~HalDisplay() {
if (emuFramebuffer0) {
delete[] emuFramebuffer0;
emuFramebuffer0 = nullptr;
}
}
void HalDisplay::begin() {
if (!is_emulated) {
einkDisplay.begin();
} else {
Serial.printf("[%lu] [ ] Emulated display initialized\n", millis());
// no-op
}
}
void HalDisplay::clearScreen(uint8_t color) const {
if (!is_emulated) {
einkDisplay.clearScreen(color);
} else {
Serial.printf("[%lu] [ ] Emulated clear screen with color 0x%02X\n", millis(), color);
memset(emuFramebuffer0, color, BUFFER_SIZE);
}
}
void HalDisplay::drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem) const {
if (!is_emulated) {
einkDisplay.drawImage(imageData, x, y, w, h, fromProgmem);
} else {
Serial.printf("[%lu] [ ] Emulated draw image at (%u, %u) with size %ux%u\n", millis(), x, y, w, h);
// Calculate bytes per line for the image
const uint16_t imageWidthBytes = w / 8;
// Copy image data to frame buffer
for (uint16_t row = 0; row < h; row++) {
const uint16_t destY = y + row;
if (destY >= DISPLAY_HEIGHT)
break;
const uint16_t destOffset = destY * DISPLAY_WIDTH_BYTES + (x / 8);
const uint16_t srcOffset = row * imageWidthBytes;
for (uint16_t col = 0; col < imageWidthBytes; col++) {
if ((x / 8 + col) >= DISPLAY_WIDTH_BYTES)
break;
if (fromProgmem) {
emuFramebuffer0[destOffset + col] = pgm_read_byte(&imageData[srcOffset + col]);
} else {
emuFramebuffer0[destOffset + col] = imageData[srcOffset + col];
}
}
}
}
}
void HalDisplay::displayBuffer(RefreshMode mode) {
if (!is_emulated) {
einkDisplay.displayBuffer(mode);
} else {
Serial.printf("[%lu] [ ] Emulated display buffer with mode %d\n", millis(), static_cast<int>(mode));
std::string b64 = base64_encode(reinterpret_cast<char*>(emuFramebuffer0), BUFFER_SIZE);
Serial.printf("$$DATA:DISPLAY:{\"mode\":%d,\"buffer\":\"", static_cast<int>(mode));
Serial.print(b64.c_str());
Serial.print("\"}$$\n");
}
}
void HalDisplay::refreshDisplay(RefreshMode mode, bool turnOffScreen) {
if (!is_emulated) {
einkDisplay.refreshDisplay(mode, turnOffScreen);
} else {
Serial.printf("[%lu] [ ] Emulated refresh display with mode %d, turnOffScreen %d\n", millis(), static_cast<int>(mode), turnOffScreen);
// emulated delay
if (mode == RefreshMode::FAST_REFRESH) {
delay(50);
} else if (mode == RefreshMode::HALF_REFRESH) {
delay(800);
} else if (mode == RefreshMode::FULL_REFRESH) {
delay(1500);
}
}
}
void HalDisplay::deepSleep() {
if (!is_emulated) {
einkDisplay.deepSleep();
} else {
Serial.printf("[%lu] [ ] Emulated deep sleep\n", millis());
// no-op
}
}
uint8_t* HalDisplay::getFrameBuffer() const {
if (!is_emulated) {
return einkDisplay.getFrameBuffer();
} else {
return emuFramebuffer0;
}
}
void HalDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer) {
if (!is_emulated) {
einkDisplay.copyGrayscaleBuffers(lsbBuffer, msbBuffer);
} else {
Serial.printf("[%lu] [ ] Emulated copy grayscale buffers\n", millis());
// TODO: not sure what this does
}
}
void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) {
if (!is_emulated) {
einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer);
} else {
Serial.printf("[%lu] [ ] Emulated copy grayscale LSB buffers\n", millis());
// TODO: not sure what this does
}
}
void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) {
if (!is_emulated) {
einkDisplay.copyGrayscaleMsbBuffers(msbBuffer);
} else {
Serial.printf("[%lu] [ ] Emulated copy grayscale MSB buffers\n", millis());
// TODO: not sure what this does
}
}
void HalDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) {
if (!is_emulated) {
einkDisplay.cleanupGrayscaleBuffers(bwBuffer);
} else {
Serial.printf("[%lu] [ ] Emulated cleanup grayscale buffers\n", millis());
// TODO: not sure what this does
}
}
void HalDisplay::displayGrayBuffer() {
if (!is_emulated) {
einkDisplay.displayGrayBuffer();
} else {
Serial.printf("[%lu] [ ] Emulated display gray buffer\n", millis());
// TODO: not sure what this does
}
}
//
// Base64 utilities
//
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(char* buf, unsigned int bufLen) {
std::string ret;
ret.reserve(bufLen * 4 / 3 + 4); // reserve enough space
int i = 0;
int j = 0;
char char_array_3[3];
char char_array_4[4];
while (bufLen--) {
char_array_3[i++] = *(buf++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i < 4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i) {
for(j = i; j < 3; j++) {
char_array_3[j] = '\0';
}
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}