mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 00:04:28 +01:00
Added compression to M3, moved stuff around
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
|||||||
[submodule "ARM_Tag_FW/common/uzlib"]
|
[submodule "ARM_Tag_FW/common/uzlib"]
|
||||||
path = ARM_Tag_FW/common/uzlib
|
path = ARM_Tag_FW/common/uzlib
|
||||||
url = https://github.com/pfalcon/uzlib.git
|
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
|
||||||
|
|||||||
7
ARM_Tag_FW/Newton_M3_nRF52811/include/compression.h
Normal file
7
ARM_Tag_FW/Newton_M3_nRF52811/include/compression.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef ENABLE_OEPLFS
|
||||||
|
#include "oepl_fs.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../../common/compression.h"
|
||||||
@@ -4,115 +4,6 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define COLOR_RED 1
|
#include "../../common/drawing.h"
|
||||||
#define COLOR_BLACK 0
|
|
||||||
|
|
||||||
#define IMAGE_OR 1
|
#endif
|
||||||
#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
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "FreeSans9pt7b.h"
|
#include "../../common/fonts/FreeSans9pt7b.h"
|
||||||
#include "FreeSansBold18pt7b.h"
|
#include "../../common/fonts/FreeSansBold18pt7b.h"
|
||||||
#include "FreeSansBold24pt7b.h"
|
#include "../../common/fonts/FreeSansBold24pt7b.h"
|
||||||
@@ -3,11 +3,16 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define FW_VERSION 0x0026 // version number (max 2.5.5 :) )
|
#define FW_VERSION 0x0027 // version number (max 2.5.5 :) )
|
||||||
#define FW_VERSION_SUFFIX "LED2" // suffix, like RC1 or whatever.
|
#define FW_VERSION_SUFFIX "ZLIB" // suffix, like RC1 or whatever.
|
||||||
//#define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
|
#define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define DEBUG_BUILD
|
||||||
|
#define DEBUG_DRAWING
|
||||||
|
|
||||||
|
|
||||||
#define SETTINGS_STRUCT_VERSION 0x01
|
#define SETTINGS_STRUCT_VERSION 0x01
|
||||||
|
|
||||||
#define DEFAULT_SETTING_FASTBOOT 0
|
#define DEFAULT_SETTING_FASTBOOT 0
|
||||||
|
|||||||
@@ -3,24 +3,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
extern uint8_t mSelfMac[];
|
#define PERSISTENTVAR
|
||||||
extern uint8_t currentChannel;
|
|
||||||
extern uint8_t APmac[];
|
|
||||||
|
|
||||||
extern uint8_t curImgSlot;
|
#include "../../common/oepl-protocol.h"
|
||||||
|
|
||||||
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);
|
|
||||||
@@ -12,6 +12,7 @@ lib_deps =
|
|||||||
stevemarple/AsyncDelay @ ^1.1.2
|
stevemarple/AsyncDelay @ ^1.1.2
|
||||||
https://github.com/calebstewart/md5
|
https://github.com/calebstewart/md5
|
||||||
https://github.com/ricmoo/QRCode
|
https://github.com/ricmoo/QRCode
|
||||||
|
https://github.com/pfalcon/uzlib
|
||||||
extra_scripts = post:preparefiles.py
|
extra_scripts = post:preparefiles.py
|
||||||
|
|
||||||
[env:Newton_M3_Universal]
|
[env:Newton_M3_Universal]
|
||||||
|
|||||||
25
ARM_Tag_FW/Newton_M3_nRF52811/src/compression.cpp
Normal file
25
ARM_Tag_FW/Newton_M3_nRF52811/src/compression.cpp
Normal 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"
|
||||||
@@ -18,632 +18,13 @@
|
|||||||
|
|
||||||
#include "epd_driver/epd_interface.h"
|
#include "epd_driver/epd_interface.h"
|
||||||
|
|
||||||
#define EEPROM_XFER_BLOCKSIZE 512 // shouldn't be any less than 256 bytes probably
|
#include "settings.h"
|
||||||
#define DRAWITEM_LIST_SIZE 24
|
|
||||||
|
|
||||||
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) {
|
uint32_t __attribute__((always_inline)) inline HAL_flashRead(uint32_t address, uint8_t *buffer, uint32_t num){
|
||||||
drawItem *di = new drawItem;
|
eepromRead(address, buffer, num);
|
||||||
|
return num;
|
||||||
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) {
|
#include "../../common/drawing.cpp"
|
||||||
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();
|
|
||||||
}
|
|
||||||
@@ -197,27 +197,38 @@ void uc8159::selectLUT(uint8_t lut) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void uc8159::epdWriteDisplayData() {
|
void uc8159::epdWriteDisplayData() {
|
||||||
uint8_t screenrow_bw[epd->effectiveXRes / 8];
|
uint8_t blocksize = 16;
|
||||||
uint8_t screenrow_r[epd->effectiveXRes / 8];
|
uint16_t byteWidth = this->effectiveXRes / 8;
|
||||||
uint8_t screenrowInterleaved[epd->effectiveXRes / 8 * 4];
|
uint8_t screenrow_bw[byteWidth * blocksize];
|
||||||
|
uint8_t screenrow_r[byteWidth * blocksize];
|
||||||
|
uint8_t screenrowInterleaved[byteWidth * 4];
|
||||||
|
|
||||||
epd_cmd(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
epd_cmd(CMD_DISPLAY_START_TRANSMISSION_DTM1);
|
||||||
markData();
|
markData();
|
||||||
epdSelect();
|
epdSelect();
|
||||||
for (uint16_t curY = 0; curY < epd->effectiveYRes; curY++) {
|
|
||||||
memset(screenrow_bw, 0, epd->effectiveXRes / 8);
|
for (uint16_t curY = 0; curY < this->effectiveYRes; curY += blocksize) { //
|
||||||
memset(screenrow_r, 0, epd->effectiveXRes / 8);
|
wdt30s();
|
||||||
drawItem::renderDrawLine(screenrow_bw, curY, 0);
|
memset(screenrow_bw, 0, byteWidth * blocksize);
|
||||||
drawItem::renderDrawLine(screenrow_r, curY, 1);
|
memset(screenrow_r, 0, byteWidth * blocksize);
|
||||||
if (curY != 0) {
|
|
||||||
|
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();
|
epdSPIWait();
|
||||||
epdDeselect();
|
epdDeselect();
|
||||||
epdSelect();
|
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();
|
epdSPIWait();
|
||||||
|
|
||||||
@@ -230,7 +241,7 @@ void uc8159::epdWriteDisplayData() {
|
|||||||
void uc8159::draw() {
|
void uc8159::draw() {
|
||||||
delay(1);
|
delay(1);
|
||||||
drawNoWait();
|
drawNoWait();
|
||||||
epdBusyWaitRising(25000);
|
epdBusyWaitRising(30000);
|
||||||
}
|
}
|
||||||
void uc8159::drawNoWait() {
|
void uc8159::drawNoWait() {
|
||||||
epdWriteDisplayData();
|
epdWriteDisplayData();
|
||||||
@@ -238,5 +249,5 @@ void uc8159::drawNoWait() {
|
|||||||
epdWrite(CMD_DISPLAY_REFRESH, 0);
|
epdWrite(CMD_DISPLAY_REFRESH, 0);
|
||||||
}
|
}
|
||||||
void uc8159::epdWaitRdy() {
|
void uc8159::epdWaitRdy() {
|
||||||
epdBusyWaitRising(25000);
|
epdBusyWaitRising(30000);
|
||||||
}
|
}
|
||||||
@@ -20,8 +20,6 @@
|
|||||||
#define SETTINGS_MAGIC 0xABBA5AA5
|
#define SETTINGS_MAGIC 0xABBA5AA5
|
||||||
|
|
||||||
struct tagsettings __xdata tagSettings = {0};
|
struct tagsettings __xdata tagSettings = {0};
|
||||||
extern uint8_t __xdata blockbuffer[];
|
|
||||||
uint8_t* __xdata settingsTempBuffer = 1024 + blockbuffer;
|
|
||||||
|
|
||||||
void loadDefaultSettings() {
|
void loadDefaultSettings() {
|
||||||
tagSettings.settingsVer = SETTINGS_STRUCT_VERSION;
|
tagSettings.settingsVer = SETTINGS_STRUCT_VERSION;
|
||||||
@@ -57,6 +55,7 @@ static void upgradeSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadSettings() {
|
void loadSettings() {
|
||||||
|
uint8_t settingsTempBuffer[sizeof(struct tagsettings)];
|
||||||
eepromRead(EEPROM_SETTINGS_AREA_START+4, (void*)settingsTempBuffer, sizeof(struct tagsettings));
|
eepromRead(EEPROM_SETTINGS_AREA_START+4, (void*)settingsTempBuffer, sizeof(struct tagsettings));
|
||||||
memcpy((void*)&tagSettings, (void*)settingsTempBuffer, sizeof(struct tagsettings));
|
memcpy((void*)&tagSettings, (void*)settingsTempBuffer, sizeof(struct tagsettings));
|
||||||
uint32_t valid = 0;
|
uint32_t valid = 0;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,7 @@ void addOverlay() {
|
|||||||
} else {
|
} else {
|
||||||
lowBatteryShown = false;
|
lowBatteryShown = false;
|
||||||
}
|
}
|
||||||
#ifdef DEBUGBLOCKS
|
#ifdef DEBUG_BUILD
|
||||||
fontrender fr(&FreeSansBold18pt7b);
|
fontrender fr(&FreeSansBold18pt7b);
|
||||||
drawMask(15, epd->Yres - 53, 129, 33, COLOR_BLACK);
|
drawMask(15, epd->Yres - 53, 129, 33, COLOR_BLACK);
|
||||||
drawMask(15, epd->Yres - 53, 129, 33, COLOR_RED);
|
drawMask(15, epd->Yres - 53, 129, 33, COLOR_RED);
|
||||||
|
|||||||
1
ARM_Tag_FW/common/QRCode
Submodule
1
ARM_Tag_FW/common/QRCode
Submodule
Submodule ARM_Tag_FW/common/QRCode added at eafbde4949
@@ -1,4 +1,4 @@
|
|||||||
#include <vector>
|
// #include <vector>
|
||||||
|
|
||||||
std::vector<decompress *> decompContexts;
|
std::vector<decompress *> decompContexts;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ int decompCallback(TINF_DATA *d) {
|
|||||||
return dec->getNextCompressedBlockFromFlash();
|
return dec->getNextCompressedBlockFromFlash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("Couldn't find callback...\n");
|
printf("FS: Couldn't find callback...\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ bool decompress::readHeader() {
|
|||||||
// read the window size from the zlib header
|
// read the window size from the zlib header
|
||||||
int res = uzlib_zlib_parse_header(this->ctx);
|
int res = uzlib_zlib_parse_header(this->ctx);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
printf("FS: Invalid zlib header\n");
|
printf("FS: Invalid zlib header\n");
|
||||||
return false;
|
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);
|
printf("FS: Asked to decompress a file with a specified window size of %d, I don't see that happening\n", window);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} 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
|
// allocate dict/window if not already allocated
|
||||||
if (!this->dictionary) this->dictionary = (uint8_t *)malloc(window);
|
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);
|
uzlib_uncompress_init(this->ctx, this->dictionary, window);
|
||||||
return true;
|
return true;
|
||||||
@@ -134,28 +137,30 @@ uint32_t decompress::getBlock(uint32_t address, uint8_t *target, uint32_t len) {
|
|||||||
this->ctx->source = this->compBuffer;
|
this->ctx->source = this->compBuffer;
|
||||||
compressedPos = 0;
|
compressedPos = 0;
|
||||||
decompressedPos = 0;
|
decompressedPos = 0;
|
||||||
|
#ifdef ENABLE_OEPLFS
|
||||||
if (this->fromFile)
|
if (this->fromFile)
|
||||||
this->getNextCompressedBlockFromFile();
|
this->getNextCompressedBlockFromFile();
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
this->getNextCompressedBlockFromFlash();
|
this->getNextCompressedBlockFromFlash();
|
||||||
this->ctx->source = this->compBuffer;
|
this->ctx->source = this->compBuffer;
|
||||||
this->readHeader();
|
this->readHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip to the next part of the output stream
|
// 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;
|
uint32_t bytesLeft = this->decompressedSize - this->decompressedPos;
|
||||||
if (len > bytesLeft) len = bytesLeft;
|
if (len > bytesLeft) len = bytesLeft;
|
||||||
|
|
||||||
|
|||||||
940
ARM_Tag_FW/common/drawing.cpp
Normal file
940
ARM_Tag_FW/common/drawing.cpp
Normal 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
152
ARM_Tag_FW/common/drawing.h
Normal 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
|
||||||
|
};
|
||||||
1043
ARM_Tag_FW/common/oepl-protocol.cpp
Normal file
1043
ARM_Tag_FW/common/oepl-protocol.cpp
Normal file
File diff suppressed because it is too large
Load Diff
21
ARM_Tag_FW/common/oepl-protocol.h
Normal file
21
ARM_Tag_FW/common/oepl-protocol.h
Normal 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);
|
||||||
Reference in New Issue
Block a user