Added compression to M3, moved stuff around

This commit is contained in:
jjwbruijn
2024-02-13 22:38:36 +01:00
parent d7f6fee31f
commit 57748d825b
19 changed files with 2281 additions and 1802 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "ARM_Tag_FW/common/uzlib"]
path = ARM_Tag_FW/common/uzlib
url = https://github.com/pfalcon/uzlib.git
[submodule "ARM_Tag_FW/common/QRCode"]
path = ARM_Tag_FW/common/QRCode
url = https://github.com/ricmoo/QRCode

View File

@@ -0,0 +1,7 @@
#pragma once
#ifdef ENABLE_OEPLFS
#include "oepl_fs.h"
#endif
#include "../../common/compression.h"

View File

@@ -4,115 +4,6 @@
#include <Arduino.h>
#include <stdint.h>
#define COLOR_RED 1
#define COLOR_BLACK 0
#include "../../common/drawing.h"
#define IMAGE_OR 1
#define IMAGE_REPLACE 0
#define DRAW_INVERTED 1
#define DRAW_NORMAL 0
typedef struct {
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint16_t first; ///< ASCII extents (first char)
uint16_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;
enum rotation {
ROTATE_0,
ROTATE_90,
ROTATE_180,
ROTATE_270
};
void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask);
void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image);
void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...);
void drawImageAtAddress(uint32_t addr, uint8_t lut);
void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color);
void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color);
class drawItem {
public:
drawItem();
~drawItem();
void setRotation(enum rotation ro);
void addItem(uint8_t *data, uint16_t width, uint16_t height);
bool addToList();
static void shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len);
static void renderDrawLine(uint8_t *line, uint16_t number, uint8_t c);
static void flushDrawItems();
// these are also used for rotated screens
static void reverseBytes(uint8_t *src, uint8_t src_len);
static uint8_t bitReverse(uint8_t byte);
enum drawType {
DRAW_FONT,
DRAW_BUFFERED_1BPP,
DRAW_MASK,
DRAW_EEPROM_1BPP,
DRAW_EEPROM_2BPP,
} type;
int16_t xpos;
int16_t ypos;
enum rotation rotate = ROTATE_0;
uint8_t color = 0;
bool direction = false;
bool mirrorH = false;
bool mirrorV = false;
uint16_t width;
uint16_t height;
// if this is true, clean up the reference (free memory).
bool cleanUp = true;
protected:
void copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset);
void getDrawLine(uint8_t *line, uint16_t number, uint8_t c);
void getXLine(uint8_t *line, uint16_t yPos, uint8_t color);
void getYLine(uint8_t *line, uint16_t xPos, uint8_t color);
uint8_t widthBytes = 0;
uint8_t *buffer;
};
class fontrender {
public:
void epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...);
fontrender(const GFXfont *font);
void setFont(const GFXfont *font);
protected:
GFXfont *gfxFont; // = &FreeSansBold18pt7b;
uint16_t bufferByteWidth = 0;
uint8_t *fb = nullptr;
uint16_t Xpixels;
uint8_t drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size);
uint8_t getCharWidth(uint16_t c);
void drawFastHLine(uint16_t x, uint16_t y, uint16_t w);
void fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
};
#endif
#endif

View File

@@ -1,5 +1,5 @@
#pragma once
#include <Arduino.h>
#include "FreeSans9pt7b.h"
#include "FreeSansBold18pt7b.h"
#include "FreeSansBold24pt7b.h"
#include "../../common/fonts/FreeSans9pt7b.h"
#include "../../common/fonts/FreeSansBold18pt7b.h"
#include "../../common/fonts/FreeSansBold24pt7b.h"

View File

@@ -3,11 +3,16 @@
#include <stdint.h>
#define FW_VERSION 0x0026 // version number (max 2.5.5 :) )
#define FW_VERSION_SUFFIX "LED2" // suffix, like RC1 or whatever.
//#define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
#define FW_VERSION 0x0027 // version number (max 2.5.5 :) )
#define FW_VERSION_SUFFIX "ZLIB" // suffix, like RC1 or whatever.
#define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
#endif
#define DEBUG_BUILD
#define DEBUG_DRAWING
#define SETTINGS_STRUCT_VERSION 0x01
#define DEFAULT_SETTING_FASTBOOT 0

View File

@@ -3,24 +3,6 @@
#include <stdint.h>
#include "settings.h"
extern uint8_t mSelfMac[];
extern uint8_t currentChannel;
extern uint8_t APmac[];
#define PERSISTENTVAR
extern uint8_t curImgSlot;
extern void setupRadio(void);
extern void killRadio(void);
extern uint8_t findSlotDataTypeArg(uint8_t arg);
extern uint8_t findNextSlideshowImage(uint8_t start);
extern uint8_t getEepromImageDataArgument(const uint8_t slot);
extern struct AvailDataInfo *getAvailDataInfo();
extern struct AvailDataInfo *getShortAvailDataInfo();
extern void drawImageFromEeprom(const uint8_t imgSlot, uint8_t lut);
extern void eraseImageBlocks();
extern bool processAvailDataInfo(struct AvailDataInfo *avail);
extern void initializeProto();
extern uint8_t detectAP(const uint8_t channel);
#include "../../common/oepl-protocol.h"

View File

@@ -12,6 +12,7 @@ lib_deps =
stevemarple/AsyncDelay @ ^1.1.2
https://github.com/calebstewart/md5
https://github.com/ricmoo/QRCode
https://github.com/pfalcon/uzlib
extra_scripts = post:preparefiles.py
[env:Newton_M3_Universal]

View File

@@ -0,0 +1,25 @@
#include <vector>
#include <Arduino.h>
#include "eeprom.h"
#include "comms.h"
#include "powermgt.h"
#include "syncedproto.h"
#include "hal.h"
#include "compression.h"
#include "../../../oepl-definitions.h"
#include "../../../oepl-proto.h"
#include "settings.h"
#define MAX_WINDOW_SIZE 8192
#define ZLIB_CACHE_SIZE 256
uint32_t __attribute__((always_inline)) inline HAL_flashRead(uint32_t address, uint8_t *buffer, uint16_t numbytes) {
eepromRead(address, (void *)buffer, numbytes);
return numbytes;;
}
#include "../../common/compression.cpp"

View File

@@ -18,632 +18,13 @@
#include "epd_driver/epd_interface.h"
#define EEPROM_XFER_BLOCKSIZE 512 // shouldn't be any less than 256 bytes probably
#define DRAWITEM_LIST_SIZE 24
#include "settings.h"
static drawItem *drawItems[DRAWITEM_LIST_SIZE] = {0};
#include "compression.h"
void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask) {
drawItem *di = new drawItem;
di->setRotation(ro);
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
uint16_t originalWidthBytes = (((uint16_t *)image)[0]) / 8;
uint16_t size = 0;
uint16_t width = ((uint16_t *)image)[0];
// find out if the original data was aligned in one byte; if not, add a byte
if (((uint16_t *)image)[0] % 8) originalWidthBytes++;
// if we're drawing in X direction, we shift the content here. Add extra space for shifting!
if (!di->direction) {
width += x % 8;
}
// check if the size is aligned in bytes; if not, add an extra for good measure;
if (width % 8) {
width /= 8;
width++;
} else {
width /= 8;
}
size = width * ((uint16_t *)image)[1];
size += 2; // not needed
uint8_t *im = (uint8_t *)calloc(size, 1);
for (uint16_t copyY = 0; copyY < ((uint16_t *)image)[1]; copyY++) {
memcpy(im + (copyY * width), image + 4 + (copyY * originalWidthBytes), originalWidthBytes);
// if we draw in X direction, we need to shift bytes in the array
if (!di->direction && (x % 8)) {
drawItem::shiftBytesRight(im + (copyY * width), x % 8, width);
}
}
di->addItem(im, width * 8, ((uint16_t *)image)[1]);
di->xpos = x;
di->ypos = y;
di->color = color;
if (mask)
di->type = drawItem::drawType::DRAW_MASK;
else
di->type = drawItem::drawType::DRAW_BUFFERED_1BPP;
di->addToList();
uint32_t __attribute__((always_inline)) inline HAL_flashRead(uint32_t address, uint8_t *buffer, uint32_t num){
eepromRead(address, buffer, num);
return num;
}
void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image) {
drawItem *di = new drawItem;
di->setRotation(ro);
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
di->addItem((uint8_t *)(image + 4), ((uint16_t *)image)[0], ((uint16_t *)image)[1]);
di->xpos = x;
di->ypos = y;
di->color = color;
di->cleanUp = false;
di->type = drawItem::drawType::DRAW_BUFFERED_1BPP;
di->addToList();
}
void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...) {
char out_buffer[256];
va_list lst;
va_start(lst, c);
vsnprintf(out_buffer, 255, c, lst);
va_end(lst);
QRCode qrcode;
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize(version)];
qrcode_initText(&qrcode, qrcodeBytes, version, ECC_LOW, out_buffer);
drawItem *di = new drawItem;
di->setRotation(rotation::ROTATE_0);
uint8_t xbytes = (qrcode.size * scale) / 8;
if (qrcode.size % 8) xbytes++;
uint16_t size = xbytes * (qrcode.size * scale);
uint8_t *im = (uint8_t *)calloc(size + 1, 1);
uint8_t *qrbuf = im;
for (uint8_t qry = 0; qry < qrcode.size; qry++) {
for (uint8_t scale_y = 0; scale_y < scale; scale_y++) {
for (uint8_t qrx = 0; qrx < qrcode.size; qrx++) {
for (uint8_t scale_x = 0; scale_x < scale; scale_x++) {
if (qrcode_getModule(&qrcode, qrx, qry)) {
// Calculate the position in the framebuffer for the scaled pixel
uint8_t scaled_qrx = (qrx * scale) + scale_x;
uint8_t scaled_qry = (qry * scale) + scale_y;
// Calculate the byte and bit positions in the framebuffer
uint8_t fb_byte = scaled_qrx / 8;
uint8_t fb_bit = 7 - (scaled_qrx % 8);
// Set the bit in the framebuffer
qrbuf[fb_byte + (scaled_qry * xbytes)] |= (1 << fb_bit);
}
}
}
}
}
di->addItem(im, xbytes * 8, qrcode.size * scale);
di->xpos = x;
di->ypos = y;
di->color = 0;
di->type = drawItem::drawType::DRAW_BUFFERED_1BPP;
di->addToList();
}
void drawImageAtAddress(uint32_t addr, uint8_t lut) {
powerUp(INIT_EEPROM);
uint8_t *xferbuffer = (uint8_t *)malloc(EEPROM_XFER_BLOCKSIZE);
struct EepromImageHeader *eih = (struct EepromImageHeader *)xferbuffer;
eepromRead(addr, xferbuffer, sizeof(struct EepromImageHeader));
switch (eih->dataType) {
case DATATYPE_IMG_RAW_1BPP: {
drawItem *di = new drawItem;
// di->setRotation(ro);
di->xpos = 0;
di->ypos = 0;
di->color = 0;
di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes);
di->type = drawItem::drawType::DRAW_EEPROM_1BPP;
di->direction = false;
di->cleanUp = false;
di->addToList();
} break;
case DATATYPE_IMG_RAW_2BPP: {
drawItem *di = new drawItem;
// di->setRotation(ro);
di->xpos = 0;
di->ypos = 0;
di->color = 0;
di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes);
di->type = drawItem::drawType::DRAW_EEPROM_2BPP;
di->direction = false;
di->cleanUp = false;
di->addToList();
} break;
}
free(xferbuffer);
addOverlay();
draw();
}
void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) {
uint16_t widthBytes = width / 8;
if (width % 8) widthBytes++;
uint32_t framebufferSize = widthBytes * height;
uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1);
if (framebuffer == NULL) {
return;
}
((uint16_t *)framebuffer)[0] = width;
((uint16_t *)framebuffer)[1] = height;
framebuffer += 4;
uint16_t w = width - 1;
uint16_t x = 1;
while (w--) {
framebuffer[(x / 8)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
for (uint16_t curY = 1; curY < (height - 1); curY++) {
framebuffer[widthBytes * curY] = 0x80;
if (width % 8)
framebuffer[(widthBytes * curY) + widthBytes - 1] = (uint8_t)(1 << (7 - ((uint8_t)width % 8)));
else
framebuffer[(widthBytes * curY) + widthBytes - 1] = 0x01;
}
w = width - 1;
x = 1;
while (w--) {
framebuffer[(x / 8) + ((height - 1) * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
framebuffer -= 4;
addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_NORMAL);
free(framebuffer);
}
void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) {
uint16_t widthBytes = width / 8;
if (width % 8) widthBytes++;
uint32_t framebufferSize = widthBytes * height;
uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1);
if (framebuffer == NULL) {
return;
}
((uint16_t *)framebuffer)[0] = width;
((uint16_t *)framebuffer)[1] = height;
framebuffer += 4;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t w = width;
uint16_t x = 0;
while (w--) {
framebuffer[(x / 8) + (curY * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
}
framebuffer -= 4;
addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_INVERTED);
free(framebuffer);
}
// drawItem (sprite) functions
void drawItem::shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len) {
// Ensure the shift value is within bounds (0 to 7)
shift = shift % 8;
// Handle the case where shift is 0 or len is 0
if (shift == 0 || len == 0) {
return;
}
// Loop through the array from right to left
for (int i = len - 1; i > 0; i--) {
// Perform the shift by combining bits from the current byte
// and the next byte to its right
data[i] = (data[i] >> shift) | (data[i - 1] << (8 - shift));
}
// For the leftmost byte, simply shift it to the right
data[0] >>= shift;
}
uint8_t drawItem::bitReverse(uint8_t byte) {
byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xAA);
byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xCC);
byte = (byte >> 4) | (byte << 4);
return byte;
}
void drawItem::reverseBytes(uint8_t *src, uint8_t src_len) {
// Check for valid input
if (src == NULL || src_len == 0) {
return;
}
// Reverse the entire source array
for (uint8_t i = 0; i < src_len / 2; i++) {
uint8_t temp = src[i];
src[i] = src[src_len - i - 1];
src[src_len - i - 1] = temp;
}
// Reverse the bits within the bytes
for (uint8_t i = 0; i < src_len; i++) {
src[i] = bitReverse(src[i]);
}
}
void drawItem::copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset) {
switch (type) {
case DRAW_MASK:
for (uint8_t i = 0; i < src_len; i++) {
dst[i + offset] &= ~(src[i]);
}
break;
default:
for (uint8_t i = 0; i < src_len; i++) {
dst[i + offset] |= src[i];
}
break;
}
}
void drawItem::renderDrawLine(uint8_t *line, uint16_t number, uint8_t c) {
drawItem *curDrawItem;
for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) {
curDrawItem = drawItems[i];
if (curDrawItem != nullptr) {
curDrawItem->getDrawLine(line, number, c);
}
}
}
void drawItem::flushDrawItems() {
drawItem *curDrawItem;
for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) {
curDrawItem = drawItems[i];
if (curDrawItem != nullptr) {
delete curDrawItem;
drawItems[i] = nullptr;
}
}
}
void drawItem::getXLine(uint8_t *line, uint16_t y, uint8_t c) {
switch (type) {
case DRAW_FONT:
case DRAW_BUFFERED_1BPP:
case DRAW_MASK:
if (c != color) return;
if ((y >= ypos) && (y < height + ypos)) { // was y > ypos, not >=
// y = height-y;
if (mirrorV) {
if (mirrorH) {
reverseBytes(&buffer[((height - (y - ypos)) * widthBytes)], widthBytes);
// reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes);
} else {
reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes);
}
}
if (mirrorH) {
copyWithByteShift(line, &buffer[((height - (y - ypos)) * widthBytes)], widthBytes, xpos / 8);
} else {
copyWithByteShift(line, &buffer[((y - ypos) * widthBytes)], widthBytes, xpos / 8);
}
}
break;
case DRAW_EEPROM_1BPP:
if (c != color) return;
if (epd->drawDirectionRight)
y = epd->effectiveYRes - 1 - y;
eepromRead((uint32_t)buffer + sizeof(struct EepromImageHeader) + (y * (epd->effectiveXRes / 8)), line, (epd->effectiveXRes / 8));
break;
case DRAW_EEPROM_2BPP:
if (epd->drawDirectionRight)
y = epd->effectiveYRes - 1 - y;
eepromRead((uint32_t)buffer + sizeof(struct EepromImageHeader) + ((y + (c * epd->effectiveYRes)) * (epd->effectiveXRes / 8)), line, (epd->effectiveXRes / 8));
break;
}
}
void drawItem::getYLine(uint8_t *line, uint16_t x, uint8_t c) {
switch (type) {
case DRAW_FONT:
case DRAW_BUFFERED_1BPP:
if (c != color) return;
if ((x >= xpos) && (x < width + xpos)) {
x -= xpos;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t curYMirrored = curY;
if (!mirrorH) curYMirrored = height - 1 - curY;
if (mirrorV) {
if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
} else {
if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
}
}
}
break;
case DRAW_MASK:
if (c != color) return;
if ((x >= xpos) && (x < width + xpos)) {
x -= xpos;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t curYMirrored = curY;
if (!mirrorH) curYMirrored = height - 1 - curY;
if (mirrorV) {
if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) {
line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8)));
}
} else {
if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) {
line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8)));
}
}
}
}
break;
default:
break;
}
}
void drawItem::getDrawLine(uint8_t *line, uint16_t number, uint8_t c) {
if (direction) {
getYLine(line, number, c);
} else {
getXLine(line, number, c);
}
}
void drawItem::addItem(uint8_t *data, uint16_t w, uint16_t h) {
width = w;
height = h;
widthBytes = w / 8;
if (w % 8) widthBytes++;
buffer = data;
}
bool drawItem::addToList() {
for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) {
if (drawItems[i] == nullptr) {
drawItems[i] = this;
return true;
};
}
return false;
}
drawItem::~drawItem() {
if (cleanUp) {
free(buffer);
}
}
drawItem::drawItem() {
if (epd->drawDirectionRight) {
direction = true;
mirrorH = true;
}
}
void drawItem::setRotation(enum rotation ro) {
if (epd->drawDirectionRight) {
direction = true;
mirrorH = true;
}
switch (ro) {
case ROTATE_0:
break;
case ROTATE_270:
direction = !direction;
mirrorH = !mirrorH;
mirrorV = !mirrorV;
break;
case ROTATE_180:
mirrorH = !mirrorH;
mirrorV = !mirrorV;
break;
case ROTATE_90:
direction = !direction;
break;
};
}
// font rendering functions
fontrender::fontrender(const GFXfont *font) {
gfxFont = (GFXfont *)font;
}
void fontrender::setFont(const GFXfont *font) {
gfxFont = (GFXfont *)font;
}
void fontrender::drawFastHLine(uint16_t x, uint16_t y, uint16_t w) {
while (w--) {
fb[(x / 8) + (y * bufferByteWidth)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
}
void fontrender::fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
for (uint16_t curY = y; curY < y + h; curY++) {
drawFastHLine(x, curY, w);
}
}
uint8_t fontrender::getCharWidth(uint16_t c) {
if ((c >= gfxFont->first) && (c <= gfxFont->last)) {
c -= gfxFont->first;
// GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]);
GFXglyph *glyph = &(gfxFont->glyph[c]);
return glyph->xAdvance;
}
return 0;
}
uint8_t fontrender::drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size) {
// Filter out bad characters not present in font
if ((c >= gfxFont->first) && (c <= gfxFont->last)) {
c -= gfxFont->first;
GFXglyph *glyph = &(gfxFont->glyph[c]);
uint8_t *bitmap = (uint8_t *)gfxFont->bitmap;
uint32_t bo = glyph->bitmapOffset;
uint8_t w = glyph->width,
h = glyph->height;
int8_t xo = glyph->xOffset,
yo = glyph->yOffset;
uint8_t xx, yy, bits = 0, bit = 0;
int16_t xo16 = 0, yo16 = 0;
if (size > 1) {
xo16 = xo;
yo16 = yo;
}
// GFXFF rendering speed up
uint16_t hpc = 0; // Horizontal foreground pixel count
for (yy = 0; yy < h; yy++) {
for (xx = 0; xx < w; xx++) {
if (bit == 0) {
bits = bitmap[bo++];
bit = 0x80;
}
if (bits & bit)
hpc++;
else {
if (hpc) {
if (size == 1)
drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc);
else
fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size);
hpc = 0;
}
}
bit >>= 1;
}
// Draw pixels for this line as we are about to increment yy
if (hpc) {
if (size == 1)
drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc);
else
fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size);
hpc = 0;
}
}
return glyph->xAdvance;
}
return 0;
}
void fontrender::epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...) {
drawItem *di = new drawItem;
if (di == nullptr) return;
di->setRotation(ro);
// prepare a drawItem, exchange x/y if necessary.
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
// output string using vsnprintf
char out_buffer[256];
va_list lst;
va_start(lst, c);
uint8_t len = vsnprintf(out_buffer, 255, c, lst);
va_end(lst);
// account for offset in font rendering
if (!di->direction) {
Xpixels = x % 8; // total drawing width increased by x%8
} else {
Xpixels = 0;
}
// find out the total length of the string
for (uint8_t c = 0; c < len; c++) {
Xpixels += (uint16_t)getCharWidth(out_buffer[c]);
}
// find out the high and low points for given font
int8_t high = 0;
int8_t low = 0;
for (uint8_t curchar = 0; curchar < len; curchar++) {
uint8_t c = out_buffer[curchar];
if ((c >= gfxFont->first) && (c <= gfxFont->last)) {
c -= gfxFont->first;
int8_t glyphUL = gfxFont->glyph[c].yOffset;
if (glyphUL < high) high = glyphUL;
int8_t glyphHeight = gfxFont->glyph[c].height;
if ((glyphUL + glyphHeight) > low) low = glyphUL + glyphHeight;
}
}
// Actual font height (reduces memory footprint)
int8_t height = -1 * (high - low) + 1;
// determine actual width
bufferByteWidth = Xpixels / 8;
if (Xpixels % 8) bufferByteWidth++;
// allocate framebuffer
fb = (uint8_t *)calloc(bufferByteWidth * height, 1);
if (!fb) {
printf("Failed to allocate buffer for drawitem, we can't render this text!\n");
delete di;
return;
}
uint16_t curX;
if (!di->direction) {
curX = x % 8; // start drawing at x%8s
} else {
curX = 0;
}
for (uint8_t c = 0; c < len; c++) {
curX += (uint16_t)drawChar(curX, height - low, out_buffer[c], 1);
}
di->addItem(fb, curX, height);
di->ypos = y;
di->xpos = x;
di->color = color;
di->type = drawItem::drawType::DRAW_FONT;
di->addToList();
}
#include "../../common/drawing.cpp"

View File

@@ -197,27 +197,38 @@ void uc8159::selectLUT(uint8_t lut) {
}
void uc8159::epdWriteDisplayData() {
uint8_t screenrow_bw[epd->effectiveXRes / 8];
uint8_t screenrow_r[epd->effectiveXRes / 8];
uint8_t screenrowInterleaved[epd->effectiveXRes / 8 * 4];
uint8_t blocksize = 16;
uint16_t byteWidth = this->effectiveXRes / 8;
uint8_t screenrow_bw[byteWidth * blocksize];
uint8_t screenrow_r[byteWidth * blocksize];
uint8_t screenrowInterleaved[byteWidth * 4];
epd_cmd(CMD_DISPLAY_START_TRANSMISSION_DTM1);
markData();
epdSelect();
for (uint16_t curY = 0; curY < epd->effectiveYRes; curY++) {
memset(screenrow_bw, 0, epd->effectiveXRes / 8);
memset(screenrow_r, 0, epd->effectiveXRes / 8);
drawItem::renderDrawLine(screenrow_bw, curY, 0);
drawItem::renderDrawLine(screenrow_r, curY, 1);
if (curY != 0) {
for (uint16_t curY = 0; curY < this->effectiveYRes; curY += blocksize) { //
wdt30s();
memset(screenrow_bw, 0, byteWidth * blocksize);
memset(screenrow_r, 0, byteWidth * blocksize);
for (uint8_t bcount = 0; bcount < blocksize; bcount++) {
drawItem::renderDrawLine(screenrow_bw + (byteWidth * bcount), curY + bcount, 0);
}
for (uint8_t bcount = 0; bcount < blocksize; bcount++) {
drawItem::renderDrawLine(screenrow_r + (byteWidth * bcount), curY + bcount, 1);
}
for (uint8_t bcount = 0; bcount < blocksize; bcount++) {
for (uint16_t curX = 0; curX < (byteWidth); curX++) {
interleaveColorToBuffer(screenrowInterleaved + (curX * 4), screenrow_bw[curX + (byteWidth * bcount)], screenrow_r[curX + (byteWidth * bcount)]);
}
epdSPIAsyncWrite(screenrowInterleaved, byteWidth * 4);
epdSPIWait();
epdDeselect();
epdSelect();
}
for (uint16_t curX = 0; curX < (epd->effectiveXRes / 8); curX++) {
interleaveColorToBuffer(screenrowInterleaved + (curX * 4), screenrow_bw[curX], screenrow_r[curX]);
}
epdSPIAsyncWrite(screenrowInterleaved, epd->effectiveXRes / 8 * 4);
}
epdSPIWait();
@@ -230,7 +241,7 @@ void uc8159::epdWriteDisplayData() {
void uc8159::draw() {
delay(1);
drawNoWait();
epdBusyWaitRising(25000);
epdBusyWaitRising(30000);
}
void uc8159::drawNoWait() {
epdWriteDisplayData();
@@ -238,5 +249,5 @@ void uc8159::drawNoWait() {
epdWrite(CMD_DISPLAY_REFRESH, 0);
}
void uc8159::epdWaitRdy() {
epdBusyWaitRising(25000);
epdBusyWaitRising(30000);
}

View File

@@ -20,8 +20,6 @@
#define SETTINGS_MAGIC 0xABBA5AA5
struct tagsettings __xdata tagSettings = {0};
extern uint8_t __xdata blockbuffer[];
uint8_t* __xdata settingsTempBuffer = 1024 + blockbuffer;
void loadDefaultSettings() {
tagSettings.settingsVer = SETTINGS_STRUCT_VERSION;
@@ -57,6 +55,7 @@ static void upgradeSettings() {
}
void loadSettings() {
uint8_t settingsTempBuffer[sizeof(struct tagsettings)];
eepromRead(EEPROM_SETTINGS_AREA_START+4, (void*)settingsTempBuffer, sizeof(struct tagsettings));
memcpy((void*)&tagSettings, (void*)settingsTempBuffer, sizeof(struct tagsettings));
uint32_t valid = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -46,7 +46,7 @@ void addOverlay() {
} else {
lowBatteryShown = false;
}
#ifdef DEBUGBLOCKS
#ifdef DEBUG_BUILD
fontrender fr(&FreeSansBold18pt7b);
drawMask(15, epd->Yres - 53, 129, 33, COLOR_BLACK);
drawMask(15, epd->Yres - 53, 129, 33, COLOR_RED);

View File

@@ -1,4 +1,4 @@
#include <vector>
// #include <vector>
std::vector<decompress *> decompContexts;
@@ -16,7 +16,7 @@ int decompCallback(TINF_DATA *d) {
return dec->getNextCompressedBlockFromFlash();
}
}
printf("Couldn't find callback...\n");
printf("FS: Couldn't find callback...\n");
return -1;
}
@@ -33,7 +33,7 @@ bool decompress::readHeader() {
// read the window size from the zlib header
int res = uzlib_zlib_parse_header(this->ctx);
if (res < 0) {
printf("FS: Invalid zlib header\n");
printf("FS: Invalid zlib header\n");
return false;
}
@@ -45,11 +45,14 @@ bool decompress::readHeader() {
printf("FS: Asked to decompress a file with a specified window size of %d, I don't see that happening\n", window);
return false;
} else {
// printf("FS: Opened compressed file with dictionary size %d\n", window);
//printf("FS: Opened compressed file with dictionary size %d\n", window);
}
window = 8192;
// allocate dict/window if not already allocated
if (!this->dictionary) this->dictionary = (uint8_t *)malloc(window);
if (!this->dictionary) printf("FS: window malloc failed\n");
uzlib_uncompress_init(this->ctx, this->dictionary, window);
return true;
@@ -134,28 +137,30 @@ uint32_t decompress::getBlock(uint32_t address, uint8_t *target, uint32_t len) {
this->ctx->source = this->compBuffer;
compressedPos = 0;
decompressedPos = 0;
#ifdef ENABLE_OEPLFS
if (this->fromFile)
this->getNextCompressedBlockFromFile();
else
#endif
this->getNextCompressedBlockFromFlash();
this->ctx->source = this->compBuffer;
this->readHeader();
}
// skip to the next part of the output stream
uint8_t *temp = nullptr;
if (address != decompressedPos) temp = (uint8_t *)malloc(ZLIB_CACHE_SIZE);
while (this->decompressedPos < address) {
uint32_t readBytes = address - decompressedPos;
if (readBytes > ZLIB_CACHE_SIZE) readBytes = ZLIB_CACHE_SIZE;
decompressedPos += readBytes;
this->ctx->dest = temp;
ctx->dest_start = ctx->dest;
ctx->dest_limit = ctx->dest + readBytes;
uzlib_uncompress(ctx);
}
if (temp) free(temp);
if (address != decompressedPos) {
uint8_t temp[ZLIB_CACHE_SIZE];
while (this->decompressedPos < address) {
uint32_t readBytes = address - decompressedPos;
if (readBytes > ZLIB_CACHE_SIZE) readBytes = ZLIB_CACHE_SIZE;
decompressedPos += readBytes;
this->ctx->dest = temp;
ctx->dest_start = ctx->dest;
ctx->dest_limit = ctx->dest + readBytes;
uzlib_uncompress(ctx);
}
}
uint32_t bytesLeft = this->decompressedSize - this->decompressedPos;
if (len > bytesLeft) len = bytesLeft;

View File

@@ -0,0 +1,940 @@
#define EEPROM_XFER_BLOCKSIZE 512 // shouldn't be any less than 256 bytes probably
#define DRAWITEM_LIST_SIZE 24
struct __attribute__((packed)) imageHeader {
uint16_t width;
uint16_t height;
uint8_t bpp : 4;
uint8_t reserved : 4;
};
static drawItem *drawItems[DRAWITEM_LIST_SIZE] = {0};
void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask) {
drawItem *di = new drawItem;
di->setRotation(ro);
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
uint16_t originalWidthBytes = (((uint16_t *)image)[0]) / 8;
uint16_t size = 0;
uint16_t width = ((uint16_t *)image)[0];
// find out if the original data was aligned in one byte; if not, add a byte
if (((uint16_t *)image)[0] % 8) originalWidthBytes++;
// if we're drawing in X direction, we shift the content here. Add extra space for shifting!
if (!di->direction) {
width += x % 8;
}
// check if the size is aligned in bytes; if not, add an extra for good measure;
if (width % 8) {
width /= 8;
width++;
} else {
width /= 8;
}
size = width * ((uint16_t *)image)[1];
size += 2; // not needed
uint8_t *im = (uint8_t *)calloc(size, 1);
for (uint16_t copyY = 0; copyY < ((uint16_t *)image)[1]; copyY++) {
memcpy(im + (copyY * width), image + 4 + (copyY * originalWidthBytes), originalWidthBytes);
// if we draw in X direction, we need to shift bytes in the array
if (!di->direction && (x % 8)) {
drawItem::shiftBytesRight(im + (copyY * width), x % 8, width);
}
}
di->addItem(im, width * 8, ((uint16_t *)image)[1]);
di->xpos = x;
di->ypos = y;
di->color = color;
if (mask)
di->type = drawItem::drawType::DRAW_MASK;
else
di->type = drawItem::drawType::DRAW_BUFFERED_1BPP;
di->addToList();
}
void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image) {
drawItem *di = new drawItem;
di->setRotation(ro);
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
di->addItem((uint8_t *)(image + 4), ((uint16_t *)image)[0], ((uint16_t *)image)[1]);
di->xpos = x;
di->ypos = y;
di->color = color;
di->cleanUp = false;
di->type = drawItem::drawType::DRAW_BUFFERED_1BPP;
di->addToList();
}
#ifdef ENABLE_OEPLFS
void addFSImage(uint16_t x, uint16_t y, uint8_t color, enum rotation ro, char *name) {
drawItem *di = new drawItem;
di->setRotation(ro);
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
OEPLFile *file = fs->getFile(name);
if (!file) {
delete di;
return;
}
uint16_t width, height;
file->getBlock(0, (uint8_t *)&width, 2);
file->getBlock(2, (uint8_t *)&height, 2);
di->addItem((uint8_t *)file, width, height);
di->xpos = x;
di->ypos = y;
di->color = color;
di->cleanUp = true;
if (color == 2) {
di->type = drawItem::drawType::DRAW_OEPLFS_2BPP;
} else {
di->type = drawItem::drawType::DRAW_OEPLFS_1BPP;
}
di->addToList();
}
void addCompressedFSImage(uint16_t x, uint16_t y, enum rotation ro, char *name) {
drawItem *di = new drawItem;
di->type = drawItem::drawType::DRAW_COMPRESSED;
di->setRotation(ro);
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
decompress *decomp = new decompress;
if (!decomp->openFromFile(name)) {
delete di;
delete decomp;
return;
}
di->imageHeaderOffset = decomp->readByte(0);
struct imageHeader imgheader;
decomp->getBlock(1, (uint8_t *)&imgheader, sizeof(struct imageHeader));
di->addItem((uint8_t *)decomp, imgheader.width, imgheader.height);
di->xpos = x;
di->ypos = y;
di->color = imgheader.bpp;
if (di->color == 1) di->color = 0;
di->cleanUp = true;
di->addToList();
}
#endif
void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...) {
char out_buffer[256];
va_list lst;
va_start(lst, c);
vsnprintf(out_buffer, 255, c, lst);
va_end(lst);
QRCode qrcode;
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize(version)];
qrcode_initText(&qrcode, qrcodeBytes, version, ECC_LOW, out_buffer);
drawItem *di = new drawItem;
di->setRotation(rotation::ROTATE_0);
uint8_t xbytes = (qrcode.size * scale) / 8;
if (qrcode.size % 8) xbytes++;
uint16_t size = xbytes * (qrcode.size * scale);
uint8_t *im = (uint8_t *)calloc(size + 1, 1);
uint8_t *qrbuf = im;
for (uint8_t qry = 0; qry < qrcode.size; qry++) {
for (uint8_t scale_y = 0; scale_y < scale; scale_y++) {
for (uint8_t qrx = 0; qrx < qrcode.size; qrx++) {
for (uint8_t scale_x = 0; scale_x < scale; scale_x++) {
if (qrcode_getModule(&qrcode, qrx, qry)) {
// Calculate the position in the framebuffer for the scaled pixel
uint8_t scaled_qrx = (qrx * scale) + scale_x;
uint8_t scaled_qry = (qry * scale) + scale_y;
// Calculate the byte and bit positions in the framebuffer
uint8_t fb_byte = scaled_qrx / 8;
uint8_t fb_bit = 7 - (scaled_qrx % 8);
// Set the bit in the framebuffer
qrbuf[fb_byte + (scaled_qry * xbytes)] |= (1 << fb_bit);
}
}
}
}
}
di->addItem(im, xbytes * 8, qrcode.size * scale);
di->xpos = x;
di->ypos = y;
di->color = 0;
di->type = drawItem::drawType::DRAW_BUFFERED_1BPP;
di->addToList();
}
void drawImageAtAddressWrap(uint32_t addr, uint8_t lut) {
// powerUp(INIT_EEPROM);
// epdSetup();
selectLUT(lut);
struct EepromImageHeader eih;
HAL_flashRead(addr, (uint8_t *)&eih, sizeof(struct EepromImageHeader));
#ifdef DEBUG_DRAWING
printf("Drawing image of type 0x%02X from location 0x%08X\n", eih.dataType, addr);
#endif
switch (eih.dataType) {
case DATATYPE_IMG_RAW_1BPP: {
drawItem *di = new drawItem;
// di->setRotation(ro);
di->xpos = 0;
di->ypos = 0;
di->color = 0;
di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes);
di->type = drawItem::drawType::DRAW_EEPROM_1BPP;
di->direction = false;
if (di->mirrorH) di->mirrorV = !di->mirrorV;
di->cleanUp = false;
di->addToList();
} break;
case DATATYPE_IMG_RAW_2BPP: {
drawItem *di = new drawItem;
// di->setRotation(ro);
di->xpos = 0;
di->ypos = 0;
di->color = 0;
di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes);
di->type = drawItem::drawType::DRAW_EEPROM_2BPP;
di->direction = false;
if (di->mirrorH) di->mirrorV = !di->mirrorV;
di->cleanUp = false;
di->addToList();
} break;
case DATATYPE_IMG_ZLIB: {
#ifdef DEBUG_DRAWING
printf("DRAW: drawing compressed image\n");
#endif
drawItem *di = new drawItem;
decompress *decomp = new decompress;
di->type = drawItem::drawType::DRAW_COMPRESSED;
addr += sizeof(struct EepromImageHeader);
if (!decomp->openFromFlash(addr, eih.size)) {
printf("DRAW: failed to open\n");
delete di;
delete decomp;
return;
}
di->imageHeaderOffset = decomp->readByte(0);
struct imageHeader imgheader;
decomp->getBlock(1, (uint8_t *)&imgheader, sizeof(struct imageHeader));
di->addItem((uint8_t *)decomp, imgheader.width, imgheader.height);
di->xpos = 0;
di->ypos = 0;
di->direction = false;
if (di->mirrorH) di->mirrorV = !di->mirrorV;
if (imgheader.bpp == 1) di->color = 0;
if (imgheader.bpp == 2) di->color = 2;
di->cleanUp = true;
di->addToList();
} break;
}
addOverlay();
draw();
}
extern "C" {
void drawImageAtAddress(uint32_t addr, uint8_t lut) {
drawImageAtAddressWrap(addr, lut);
}
}
void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) {
uint16_t widthBytes = width / 8;
if (width % 8) widthBytes++;
uint32_t framebufferSize = (widthBytes + 1) * height;
uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1);
if (framebuffer == NULL) {
return;
}
((uint16_t *)framebuffer)[0] = width + 1;
((uint16_t *)framebuffer)[1] = height;
framebuffer += 4;
uint16_t w = width - 1;
uint16_t x = 1;
while (w--) {
framebuffer[(x / 8)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
for (uint16_t curY = 1; curY < (height - 1); curY++) {
framebuffer[widthBytes * curY] = 0x80;
if (width % 8)
framebuffer[(widthBytes * curY) + widthBytes - 1] = (uint8_t)(1 << (7 - ((uint8_t)width % 8)));
else
framebuffer[(widthBytes * curY) + widthBytes - 1] = 0x01;
}
w = width - 1;
x = 1;
while (w--) {
framebuffer[(x / 8) + ((height - 1) * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
framebuffer -= 4;
addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_NORMAL);
free(framebuffer);
}
void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) {
uint16_t widthBytes = width / 8;
if (width % 8) widthBytes++;
uint32_t framebufferSize = widthBytes * height;
uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1);
if (framebuffer == NULL) {
return;
}
((uint16_t *)framebuffer)[0] = width;
((uint16_t *)framebuffer)[1] = height;
framebuffer += 4;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t w = width;
uint16_t x = 0;
while (w--) {
framebuffer[(x / 8) + (curY * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
}
framebuffer -= 4;
addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_INVERTED);
free(framebuffer);
}
// drawItem (sprite) functions
void drawItem::shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len) {
// Ensure the shift value is within bounds (0 to 7)
shift = shift % 8;
// Handle the case where shift is 0 or len is 0
if (shift == 0 || len == 0) {
return;
}
// Loop through the array from right to left
for (int i = len - 1; i > 0; i--) {
// Perform the shift by combining bits from the current byte
// and the next byte to its right
data[i] = (data[i] >> shift) | (data[i - 1] << (8 - shift));
}
// For the leftmost byte, simply shift it to the right
data[0] >>= shift;
}
uint8_t drawItem::bitReverse(uint8_t byte) {
byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xAA);
byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xCC);
byte = (byte >> 4) | (byte << 4);
return byte;
}
void drawItem::reverseBytes(uint8_t *src, uint8_t src_len) {
// Check for valid input
if (src == NULL || src_len == 0) {
return;
}
// Reverse the entire source array
for (uint8_t i = 0; i < src_len / 2; i++) {
uint8_t temp = src[i];
src[i] = src[src_len - i - 1];
src[src_len - i - 1] = temp;
}
// Reverse the bits within the bytes
for (uint8_t i = 0; i < src_len; i++) {
src[i] = bitReverse(src[i]);
}
}
void drawItem::copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset) {
switch (type) {
case DRAW_MASK:
for (uint8_t i = 0; i < src_len; i++) {
dst[i + offset] &= ~(src[i]);
}
break;
default:
for (uint8_t i = 0; i < src_len; i++) {
dst[i + offset] |= src[i];
}
break;
}
}
void drawItem::renderDrawLine(uint8_t *line, uint16_t number, uint8_t c) {
drawItem *curDrawItem;
for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) {
curDrawItem = drawItems[i];
if (curDrawItem != nullptr) {
curDrawItem->getDrawLine(line, number, c);
}
}
}
void drawItem::flushDrawItems() {
drawItem *curDrawItem;
for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) {
curDrawItem = drawItems[i];
if (curDrawItem != nullptr) {
delete curDrawItem;
drawItems[i] = nullptr;
}
}
}
void drawItem::getXLine(uint8_t *line, uint16_t y, uint8_t c) {
switch (type) {
case DRAW_FONT:
case DRAW_BUFFERED_1BPP:
case DRAW_MASK:
if (c != color) return;
if ((y >= ypos) && (y < height + ypos)) { // was y > ypos, not >=
// y = height-y;
if (mirrorV) {
if (mirrorH) {
reverseBytes(&buffer[((height - (y - ypos)) * widthBytes)], widthBytes);
// reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes);
} else {
reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes);
}
}
if (mirrorH) {
copyWithByteShift(line, &buffer[((height - (y - ypos)) * widthBytes)], widthBytes, xpos / 8);
} else {
copyWithByteShift(line, &buffer[((y - ypos) * widthBytes)], widthBytes, xpos / 8);
}
}
break;
#ifdef ENABLE_OEPLFS
case DRAW_OEPLFS_1BPP:
case DRAW_OEPLFS_2BPP:
if ((color < 2) && (c != color)) return;
if ((y >= ypos) && (y < height + ypos)) { // was y > ypos, not >=
uint32_t offset = 4;
offset += c * height * widthBytes;
OEPLFile *file = (OEPLFile *)this->buffer;
uint8_t *dbuffer = (uint8_t *)malloc(widthBytes);
if (mirrorH) {
file->getBlock(offset + (height - (y - ypos)) * widthBytes, dbuffer, widthBytes);
} else {
file->getBlock(offset + (y - ypos) * widthBytes, dbuffer, widthBytes);
}
if (mirrorV) {
reverseBytes(dbuffer, widthBytes);
}
copyWithByteShift(line, dbuffer, widthBytes, xpos / 8);
free(dbuffer);
}
break;
#endif
case DRAW_COMPRESSED:
if ((color < 2) && (c != color)) return;
if ((y >= ypos) && (y < height + ypos)) {
uint32_t offset = this->imageHeaderOffset;
offset += c * height * widthBytes;
decompress *decomp = (decompress *)this->buffer;
uint8_t *dbuffer = (uint8_t *)malloc(widthBytes);
if (mirrorH) {
decomp->getBlock(offset + (height - (y - ypos)) * widthBytes, dbuffer, widthBytes);
} else {
decomp->getBlock(offset + (y - ypos) * widthBytes, dbuffer, widthBytes);
}
if (mirrorV) {
reverseBytes(dbuffer, widthBytes);
}
copyWithByteShift(line, dbuffer, widthBytes, xpos / 8);
free(dbuffer);
}
break;
case DRAW_EEPROM_1BPP:
if (c != color) return;
if (epd->drawDirectionRight)
y = epd->effectiveYRes - 1 - y;
HAL_flashRead((uint32_t)buffer + sizeof(struct EepromImageHeader) + (y * (epd->effectiveXRes / 8)), line, (epd->effectiveXRes / 8));
break;
case DRAW_EEPROM_2BPP:
if (epd->drawDirectionRight)
y = epd->effectiveYRes - 1 - y;
HAL_flashRead((uint32_t)(buffer + sizeof(struct EepromImageHeader) + ((y + (c * epd->effectiveYRes)) * (epd->effectiveXRes / 8))), line, (epd->effectiveXRes / 8));
break;
default:
printf("DRAW: Not supported mode!\n");
break;
}
}
void drawItem::getYLine(uint8_t *line, uint16_t x, uint8_t c) {
switch (type) {
case DRAW_FONT:
case DRAW_BUFFERED_1BPP:
if (c != color) return;
if ((x >= xpos) && (x < width + xpos)) {
x -= xpos;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t curYMirrored = curY;
if (!mirrorH) curYMirrored = height - 1 - curY;
if (mirrorV) {
if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
} else {
if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
}
}
}
break;
case DRAW_MASK:
if (c != color) return;
if ((x >= xpos) && (x < width + xpos)) {
x -= xpos;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t curYMirrored = curY;
if (!mirrorH) curYMirrored = height - 1 - curY;
if (mirrorV) {
if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) {
line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8)));
}
} else {
if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) {
line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8)));
}
}
}
}
break;
#ifdef ENABLE_OEPLFS
case DRAW_OEPLFS_1BPP:
case DRAW_OEPLFS_2BPP:
// this is incredibly slow. For larger images, it'll probably read 128 bytes of eeprom for every -bit- in the image.
if ((color < 2) && (c != color)) return;
if ((x >= xpos) && (x < width + xpos)) {
uint32_t offset = 4;
offset += c * height * widthBytes;
OEPLFile *file = (OEPLFile *)this->buffer;
x -= xpos;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t curYMirrored = curY;
if (!mirrorH) curYMirrored = height - 1 - curY;
if (mirrorV) {
if (file->readByte(offset + ((width - x) / 8) + (curYMirrored * widthBytes)) & (1 << (7 - ((width - x) % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
} else {
if (file->readByte(offset + (x / 8) + (curYMirrored * widthBytes)) & (1 << (7 - (x % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
}
}
}
break;
#endif
case DRAW_COMPRESSED:
// this is incredibly slow, and very naive. Just don't. Maybe if you want to test if everything works. It'll decompress about 50% of the file *PER PIXEL*. Good for load testing
if ((color < 2) && (c != color)) return;
if ((x >= xpos) && (x < width + xpos)) {
uint32_t offset = this->imageHeaderOffset;
offset += c * height * widthBytes;
decompress *decomp = (decompress *)this->buffer;
x -= xpos;
for (uint16_t curY = 0; curY < height; curY++) {
uint16_t curYMirrored = curY;
if (!mirrorH) curYMirrored = height - 1 - curY;
if (mirrorV) {
if (decomp->readByte(offset + ((width - x) / 8) + (curYMirrored * widthBytes)) & (1 << (7 - ((width - x) % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
} else {
if (decomp->readByte(offset + (x / 8) + (curYMirrored * widthBytes)) & (1 << (7 - (x % 8)))) {
line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8)));
}
}
}
}
break;
default:
break;
}
}
void drawItem::getDrawLine(uint8_t *line, uint16_t number, uint8_t c) {
if (direction) {
getYLine(line, number, c);
} else {
getXLine(line, number, c);
}
}
void drawItem::addItem(uint8_t *data, uint16_t w, uint16_t h) {
width = w;
height = h;
widthBytes = w / 8;
if (w % 8) widthBytes++;
buffer = data;
}
bool drawItem::addToList() {
for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) {
if (drawItems[i] == nullptr) {
drawItems[i] = this;
return true;
};
}
return false;
}
drawItem::~drawItem() {
if (cleanUp) {
switch (this->type) {
#ifdef ENABLE_OEPLFS
case drawItem::drawType::DRAW_OEPLFS_1BPP:
case drawItem::drawType::DRAW_OEPLFS_2BPP: {
OEPLFile *file = (OEPLFile *)this->buffer;
if (file) delete file;
} break;
#endif
case drawItem::drawType::DRAW_COMPRESSED: {
decompress *dec = (decompress *)this->buffer;
if (dec) delete dec;
} break;
default:
free(buffer);
break;
}
}
}
drawItem::drawItem() {
if (epd->drawDirectionRight) {
direction = true;
mirrorH = true;
}
}
void drawItem::setRotation(enum rotation ro) {
if (epd->drawDirectionRight) {
direction = true;
mirrorH = true;
}
switch (ro) {
case ROTATE_0:
break;
case ROTATE_270:
direction = !direction;
mirrorH = !mirrorH;
mirrorV = !mirrorV;
break;
case ROTATE_180:
mirrorH = !mirrorH;
mirrorV = !mirrorV;
break;
case ROTATE_90:
direction = !direction;
break;
};
}
void fontrender::drawFastHLine(uint16_t x, uint16_t y, uint16_t w) {
while (w--) {
fb[(x / 8) + (y * bufferByteWidth)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8)));
x++;
}
}
void fontrender::fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
for (uint16_t curY = y; curY < y + h; curY++) {
drawFastHLine(x, curY, w);
}
}
#ifdef ENABLE_OEPLFS
// font rendering functions for OEPLFS
fontrender::fontrender(char *name) {
this->setFont(name);
}
void fontrender::setFont(char *name) {
if (this->glyphFile) delete this->glyphFile;
if (this->bitmapFile) delete this->bitmapFile;
OEPLFile *font = fs->getFile(name);
if (!font) {
#ifdef DEBUG_DRAWING
printf("DRAW: Couldn't open font file %s\n", name);
#endif
}
font->getBlock(0, (uint8_t *)&this->gfxFont, sizeof(GFXFontOEPL));
this->glyphFile = fs->getFile(this->gfxFont.glyphFile);
this->bitmapFile = fs->getFile(this->gfxFont.bitmapFile);
if (!this->glyphFile) printf("DRAW: Couldn't open font glyph file %s\n", name);
if (!this->bitmapFile) printf("DRAW: Couldn't open font bitmap file %s\n", name);
if (font) delete (font);
}
fontrender::~fontrender() {
if (this->glyphFile) delete this->glyphFile;
if (this->bitmapFile) delete this->bitmapFile;
}
uint8_t fontrender::getCharWidth(uint16_t c) {
if ((c >= gfxFont.first) && (c <= gfxFont.last)) {
c -= gfxFont.first;
GFXglyph glyph = this->getGlyph(c);
return glyph.xAdvance;
}
return 0;
}
GFXglyph fontrender::getGlyph(uint16_t c) {
GFXglyph g;
this->glyphFile->getBlock(sizeof(GFXglyph) * c, (uint8_t *)&g, sizeof(GFXglyph));
return g;
}
#else
// 'regular' font rendering functions
uint8_t fontrender::getCharWidth(uint16_t c) {
if ((c >= gfxFont->first) && (c <= gfxFont->last)) {
c -= gfxFont->first;
GFXglyph *glyph = &(gfxFont->glyph[c]);
return glyph->xAdvance;
}
return 0;
}
fontrender::fontrender(const GFXfont *font) {
gfxFont = (GFXfont *)font;
}
void fontrender::setFont(const GFXfont *font) {
gfxFont = (GFXfont *)font;
}
#endif
uint8_t fontrender::drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size) {
// Filter out bad characters not present in font
#ifdef ENABLE_OEPLFS
if ((c >= gfxFont.first) && (c <= gfxFont.last)) {
c -= gfxFont.first;
GFXglyph glyph = this->getGlyph(c);
// uint8_t *bitmap = (uint8_t *)gfxFont->bitmap;
uint32_t bo = glyph.bitmapOffset;
uint8_t w = glyph.width,
h = glyph.height;
int8_t xo = glyph.xOffset,
yo = glyph.yOffset;
#else
if ((c >= gfxFont->first) && (c <= gfxFont->last)) {
c -= gfxFont->first;
GFXglyph *glyph = &(gfxFont->glyph[c]);
uint8_t *bitmap = (uint8_t *)gfxFont->bitmap;
uint32_t bo = glyph->bitmapOffset;
uint8_t w = glyph->width,
h = glyph->height;
int8_t xo = glyph->xOffset,
yo = glyph->yOffset;
#endif
uint8_t xx, yy, bits = 0, bit = 0;
int16_t xo16 = 0, yo16 = 0;
if (size > 1) {
xo16 = xo;
yo16 = yo;
}
// GFXFF rendering speed up
uint16_t hpc = 0; // Horizontal foreground pixel count
for (yy = 0; yy < h; yy++) {
for (xx = 0; xx < w; xx++) {
if (bit == 0) {
#ifdef ENABLE_OEPLFS
bits = (*this->bitmapFile)(bo++);
#else
bits = bitmap[bo++];
#endif
bit = 0x80;
}
if (bits & bit)
hpc++;
else {
if (hpc) {
if (size == 1)
drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc);
else
fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size);
hpc = 0;
}
}
bit >>= 1;
}
// Draw pixels for this line as we are about to increment yy
if (hpc) {
if (size == 1)
drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc);
else
fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size);
hpc = 0;
}
}
#ifdef ENABLE_OEPLFS
return glyph.xAdvance;
#else
return glyph->xAdvance;
#endif
}
return 0;
}
void fontrender::epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...) {
drawItem *di = new drawItem;
if (di == nullptr) return;
di->setRotation(ro);
// prepare a drawItem, exchange x/y if necessary.
if (di->direction ^ epd->drawDirectionRight) {
int16_t temp = x;
x = y;
y = temp;
}
// output string using vsnprintf
char out_buffer[256];
va_list lst;
va_start(lst, c);
uint8_t len = vsnprintf(out_buffer, 255, c, lst);
va_end(lst);
// account for offset in font rendering
if (!di->direction) {
Xpixels = x % 8; // total drawing width increased by x%8
} else {
Xpixels = 0;
}
// find out the total length of the string
for (uint8_t c = 0; c < len; c++) {
Xpixels += (uint16_t)getCharWidth(out_buffer[c]);
}
// find out the high and low points for given font
int8_t high = 0;
int8_t low = 0;
for (uint8_t curchar = 0; curchar < len; curchar++) {
uint8_t c = out_buffer[curchar];
#ifdef ENABLE_OEPLFS
if ((c >= gfxFont.first) && (c <= gfxFont.last)) {
c -= gfxFont.first;
GFXglyph g = this->getGlyph(c);
int8_t glyphUL = g.yOffset;
if (glyphUL < high) high = glyphUL;
int8_t glyphHeight = g.height;
#else
if ((c >= gfxFont->first) && (c <= gfxFont->last)) {
c -= gfxFont->first;
int8_t glyphUL = gfxFont->glyph[c].yOffset;
if (glyphUL < high) high = glyphUL;
int8_t glyphHeight = gfxFont->glyph[c].height;
#endif
if ((glyphUL + glyphHeight) > low) low = glyphUL + glyphHeight;
}
}
// Actual font height (reduces memory footprint)
int8_t height = -1 * (high - low) + 1;
// determine actual width
bufferByteWidth = Xpixels / 8;
if (Xpixels % 8) bufferByteWidth++;
// allocate framebuffer
fb = (uint8_t *)calloc(bufferByteWidth * height, 1);
if (!fb) {
printf("DRAW: Tried to allocate a buffer %d x %d\n and failed...", bufferByteWidth, height);
delete di;
return;
}
uint16_t curX;
if (!di->direction) {
curX = x % 8; // start drawing at x%8s
} else {
curX = 0;
}
for (uint8_t c = 0; c < len; c++) {
curX += (uint16_t)drawChar(curX, height - low, out_buffer[c], 1);
}
di->addItem(fb, curX, height);
di->ypos = y;
di->xpos = x;
di->color = color;
di->type = drawItem::drawType::DRAW_FONT;
di->addToList();
}

152
ARM_Tag_FW/common/drawing.h Normal file
View File

@@ -0,0 +1,152 @@
#define COLOR_RED 1
#define COLOR_BLACK 0
#define COLOR_DUAL 2
#define IMAGE_OR 1
#define IMAGE_REPLACE 0
#define DRAW_INVERTED 1
#define DRAW_NORMAL 0
#define FILENAME_LENGTH 32
typedef struct __attribute__((packed)) {
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
enum rotation {
ROTATE_0,
ROTATE_90,
ROTATE_180,
ROTATE_270
};
#ifdef ENABLE_OEPLFS
typedef struct __attribute__((packed)) {
uint16_t first;
uint16_t last;
uint8_t yAdvance;
char glyphFile[FILENAME_LENGTH];
char bitmapFile[FILENAME_LENGTH];
} GFXFontOEPL;
#else
/// Data stored for FONT AS A WHOLE
typedef struct {
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint16_t first; ///< ASCII extents (first char)
uint16_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;
#endif
extern "C" {
void drawImageAtAddress(uint32_t addr, uint8_t lut);
}
void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask);
void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image);
void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...);
void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color);
void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color);
#ifdef ENABLE_OEPLFS
void addFSImage(uint16_t x, uint16_t y, uint8_t color, enum rotation ro, char *name);
void addCompressedFSImage(uint16_t x, uint16_t y, enum rotation ro, char *name);
#endif
class drawItem {
public:
drawItem();
~drawItem();
void setRotation(enum rotation ro);
void addItem(uint8_t *data, uint16_t width, uint16_t height);
bool addToList();
static void shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len);
static void renderDrawLine(uint8_t *line, uint16_t number, uint8_t c);
static void flushDrawItems();
// these are also used for rotated screens
static void reverseBytes(uint8_t *src, uint8_t src_len);
static uint8_t bitReverse(uint8_t byte);
enum drawType {
DRAW_FONT,
DRAW_BUFFERED_1BPP,
DRAW_MASK,
DRAW_EEPROM_1BPP,
DRAW_EEPROM_2BPP,
DRAW_COMPRESSED,
DRAW_OEPLFS_1BPP,
DRAW_OEPLFS_2BPP
} type;
int16_t xpos;
int16_t ypos;
enum rotation rotate = ROTATE_0;
uint8_t color = 0;
bool direction = false;
bool mirrorH = false;
bool mirrorV = false;
uint16_t width;
uint16_t height;
uint8_t imageHeaderOffset = 0;
// if this is true, clean up the reference (free memory).
bool cleanUp = true;
protected:
void copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset);
void getDrawLine(uint8_t *line, uint16_t number, uint8_t c);
void getXLine(uint8_t *line, uint16_t yPos, uint8_t color);
void getYLine(uint8_t *line, uint16_t xPos, uint8_t color);
uint8_t widthBytes = 0;
uint8_t *buffer = nullptr;
};
class fontrender {
public:
void epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...);
#ifdef ENABLE_OEPLFS
fontrender(char *name);
~fontrender();
void setFont(char *name);
#else
fontrender(const GFXfont *font);
void setFont(const GFXfont *font);
#endif
protected:
#ifdef ENABLE_OEPLFS
GFXFontOEPL gfxFont;
#else
GFXfont *gfxFont; // = &FreeSansBold18pt7b;
#endif
uint16_t bufferByteWidth = 0;
uint8_t *fb = nullptr;
uint16_t Xpixels;
uint8_t drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size);
uint8_t getCharWidth(uint16_t c);
void drawFastHLine(uint16_t x, uint16_t y, uint16_t w);
void fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
#ifdef ENABLE_OEPLFS
// The OEPLFS version needs some additional pointers to the file objects
GFXglyph getGlyph(uint16_t c);
OEPLFile *glyphFile = nullptr;
OEPLFile *bitmapFile = nullptr;
#endif
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
extern PERSISTENTVAR uint8_t mSelfMac[];
extern PERSISTENTVAR volatile uint8_t currentChannel;
extern PERSISTENTVAR uint8_t APmac[];
extern PERSISTENTVAR uint8_t curImgSlot;
extern void setupRadio(void);
extern void killRadio(void);
extern uint8_t findSlotDataTypeArg(uint8_t arg);
extern uint8_t findNextSlideshowImage(uint8_t start);
extern uint8_t getEepromImageDataArgument(const uint8_t slot);
extern struct AvailDataInfo *getAvailDataInfo();
extern struct AvailDataInfo *getShortAvailDataInfo();
extern void drawImageFromEeprom(const uint8_t imgSlot, uint8_t lut);
extern void eraseImageBlocks();
extern bool processAvailDataInfo(struct AvailDataInfo *avail);
extern void initializeProto();
extern uint8_t detectAP(const uint8_t channel);