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"]
|
||||
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
|
||||
|
||||
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 <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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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]
|
||||
|
||||
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"
|
||||
|
||||
#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"
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
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