Tasmota/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp
2021-04-18 09:20:54 +02:00

683 lines
17 KiB
C++

/**
* @filename : epdpaint.cpp
* @brief : Paint tools
* @author : Yehui from Waveshare
*
* Copyright (C) Waveshare September 9 2017
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documnetation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <pgmspace.h>
#include "renderer.h"
#define USE_EPD_FONTS
//#define USE_ALL_EPD_FONTS
//#define USE_GFX_FONTS
#define USE_TINY_FONT
#define USE_7SEG_FONT
uint8_t wr_redir=0;
uint8_t *buffer;
#define register
#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str);
#define OLED_FONT_WIDTH 6
#define OLED_FONT_HEIGTH 8
#define BLACK 0
Renderer::Renderer(int16_t x, int16_t y) :
Adafruit_GFX(x, y) {
font=0;
#ifdef USE_EPD_FONTS
selected_font = &Font12;
#endif
}
uint16_t Renderer::GetColorFromIndex(uint8_t index) {
if (index>0) return 1;
return 0;
}
void Renderer::dim(uint8_t contrast) {
}
void Renderer::pushColors(uint16_t *data, uint16_t len, boolean first) {
}
void Renderer::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
}
void Renderer::DisplayOnff(int8_t on) {
}
void Renderer::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) {
}
void Renderer::Begin(int16_t p1,int16_t p2,int16_t p3) {
}
void Renderer::Updateframe() {
}
void Renderer::setTextSize(uint8_t sf) {
if (sf < 1) sf = 1;
if (sf > 4) sf = 4;
tsize = sf;
Adafruit_GFX::setTextSize(sf);
}
/**
* @brief: this draws a charactor on the frame buffer but not refresh
*/
void Renderer::DrawCharAt(int16_t x, int16_t y, char ascii_char,int16_t colored) {
#ifdef USE_EPD_FONTS
sFONT *xfont = selected_font;
int i, j;
unsigned int char_offset = (ascii_char - ' ') * xfont->Height * (xfont->Width / 8 + (xfont->Width % 8 ? 1 : 0));
const unsigned char* ptr = &xfont->table[char_offset];
uint8_t sf = tsize;
for (j = 0; j < xfont->Height; j++) {
for (i = 0; i < xfont->Width; i++) {
if (pgm_read_byte(ptr) & (0x80 >> (i % 8))) {
if (sf == 1) {
writePixel(x + i, y + j, colored);
} else {
writeFillRect(x + i * sf, y + j * sf, sf, sf, colored);
}
} else {
// fill background
if (!drawmode) {
if (sf == 1) {
writePixel(x + i, y + j, textbgcolor);
} else {
writeFillRect(x + i * sf, y + j * sf, sf, sf, textbgcolor);
}
}
}
if (i % 8 == 7) {
ptr++;
}
}
if (xfont->Width % 8 != 0) {
ptr++;
}
}
#endif
}
/**
* @brief: this displays a string on the frame buffer but not refresh
*/
void Renderer::DrawStringAt(int16_t x, int16_t y, const char* text, uint16_t colored, uint8_t flag) {
const char* p_text = text;
unsigned int counter = 0;
int refcolumn = x;
sFONT *xfont = selected_font;
#ifndef USE_EPD_FONTS
font=0;
#endif
#ifndef USE_GFX_FONTS
if (!font || font == 5) {
#endif
if (flag) {
x=(x-1)*OLED_FONT_WIDTH*textsize_x;
y=(y-1)*OLED_FONT_HEIGTH*textsize_y;
}
setCursor(x,y);
setTextColor(colored,textbgcolor);
print(text);
return;
#ifndef USE_GFX_FONTS
}
#endif
if (flag) {
x=(x-1)*xfont->Width*tsize;
y=(y-1)*xfont->Height*tsize;
refcolumn = x;
}
/* Send the string character by character on EPD */
if (font==7) {
return FastString(x,y,colored,p_text);
}
while (*p_text != 0) {
/* Display one character on EPD */
DrawCharAt(refcolumn, y, *p_text, colored);
/* increment the column position */
refcolumn += xfont->Width*tsize;
/* Point on the next character */
p_text++;
counter++;
}
}
void Renderer::FastString(uint16_t x,uint16_t y,uint16_t tcolor, const char* str) {
}
#include <Fonts/FreeMono12pt7b.h>
#include <Fonts/FreeMono18pt7b.h>
#include <Fonts/FreeMono24pt7b.h>
sFONT RAFont = {
0,
7, /* Width */
12, /* Height */
};
extern uint8_t *loaded_font;
void Renderer::setTextFont(uint8_t f) {
font = f;
setFont(0);
#ifdef USE_GFX_FONTS
switch (f) {
case 0:
setFont(0);
break;
case 1:
setFont(&FreeMono12pt7b);
break;
case 2:
setFont(&FreeMono18pt7b);
break;
case 3:
setFont(&FreeMono24pt7b);
break;
default:
setFont(0);
break;
}
#endif
#ifdef USE_ALL_EPD_FONTS
switch (font) {
case 1:
selected_font = &Font12;
break;
case 2:
selected_font = &Font24;
break;
case 3:
selected_font = &Font8;
break;
case 4:
selected_font = &Font16;
break;
case 5:
selected_font = &Font20;
break;
case 7:
selected_font = &RAFont;
break;
default:
selected_font = &Font12;
break;
}
#else
#ifdef USE_EPD_FONTS
switch (font) {
case 1:
selected_font = &Font12;
break;
case 2:
selected_font = &Font24;
break;
case 3:
#ifdef USE_TINY_FONT
selected_font = &Font8;
#else
selected_font = &Font24;
#endif
break;
case 4:
#ifdef USE_7SEG_FONT
selected_font = &Font24_7seg;
break;
#endif
case 5:
setFont(ramfont);
break;
default:
selected_font = &Font12;
break;
}
#endif
#endif
}
void Renderer::SetRamfont(uint8_t *font) {
ramfont = (GFXfont*)font;
uint32_t bitmap_offset = (uint32_t)ramfont->bitmap;
uint32_t glyph_offset = (uint32_t)ramfont->glyph;
ramfont->bitmap = (uint8_t*)((uint32_t)font + bitmap_offset);
ramfont->glyph = (GFXglyph*)((uint32_t)font + glyph_offset);
setFont(ramfont);
}
void Renderer::clearDisplay(void) {
fillScreen(BLACK);
}
#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; }
void Renderer::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
boolean bSwap = false;
if (!buffer) return;
switch(getRotation()) {
case 0:
// 0 degree rotation, do nothing
break;
case 1:
// 90 degree rotation, swap x & y for rotation, then invert x
bSwap = true;
renderer_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
// 180 degree rotation, invert x and y - then shift y around for height.
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
x -= (w-1);
break;
case 3:
// 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h)
bSwap = true;
renderer_swap(x, y);
y = HEIGHT - y - 1;
y -= (w-1);
break;
}
if(bSwap) {
drawFastVLineInternal(x, y, w, color);
} else {
drawFastHLineInternal(x, y, w, color);
}
}
void Renderer::drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) {
// Do bounds/limit checks
if(y < 0 || y >= HEIGHT) { return; }
// make sure we don't try to draw below 0
if(x < 0) {
w += x;
x = 0;
}
// make sure we don't go off the edge of the display
if( (x + w) > WIDTH) {
w = (WIDTH - x);
}
// if our width is now negative, punt
if(w <= 0) { return; }
// set up the pointer for movement through the buffer
register uint8_t *pBuf = buffer;
// adjust the buffer pointer for the current row
pBuf += ((y/8) * WIDTH);
// and offset x columns in
pBuf += x;
register uint8_t mask = 1 << (y&7);
switch (color)
{
case WHITE: while(w--) { *pBuf++ |= mask; }; break;
case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break;
case INVERSE: while(w--) { *pBuf++ ^= mask; }; break;
}
}
void Renderer::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
if (!buffer) return;
bool bSwap = false;
switch(getRotation()) {
case 0:
break;
case 1:
// 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
bSwap = true;
renderer_swap(x, y);
x = WIDTH - x - 1;
x -= (h-1);
break;
case 2:
// 180 degree rotation, invert x and y - then shift y around for height.
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
y -= (h-1);
break;
case 3:
// 270 degree rotation, swap x & y for rotation, then invert y
bSwap = true;
renderer_swap(x, y);
y = HEIGHT - y - 1;
break;
}
if(bSwap) {
drawFastHLineInternal(x, y, h, color);
} else {
drawFastVLineInternal(x, y, h, color);
}
}
void Renderer::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) {
// do nothing if we're off the left or right side of the screen
if(x < 0 || x >= WIDTH) { return; }
// make sure we don't try to draw below 0
if(__y < 0) {
// __y is negative, this will subtract enough from __h to account for __y being 0
__h += __y;
__y = 0;
}
// make sure we don't go past the height of the display
if( (__y + __h) > HEIGHT) {
__h = (HEIGHT - __y);
}
// if our height is now negative, punt
if(__h <= 0) {
return;
}
// this display doesn't need ints for coordinates, use local byte registers for faster juggling
register uint8_t y = __y;
register uint8_t h = __h;
// set up the pointer for fast movement through the buffer
register uint8_t *pBuf = buffer;
// adjust the buffer pointer for the current row
pBuf += ((y/8) * WIDTH);
// and offset x columns in
pBuf += x;
// do the first partial byte, if necessary - this requires some masking
register uint8_t mod = (y&7);
if(mod) {
// mask off the high n bits we want to set
mod = 8-mod;
// note - lookup table results in a nearly 10% performance improvement in fill* functions
// register uint8_t mask = ~(0xFF >> (mod));
static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
register uint8_t mask = premask[mod];
// adjust the mask if we're not going to reach the end of this byte
if( h < mod) {
mask &= (0XFF >> (mod-h));
}
switch (color)
{
case WHITE: *pBuf |= mask; break;
case BLACK: *pBuf &= ~mask; break;
case INVERSE: *pBuf ^= mask; break;
}
// fast exit if we're done here!
if(h<mod) { return; }
h -= mod;
pBuf += WIDTH;
}
// write solid bytes while we can - effectively doing 8 rows at a time
if(h >= 8) {
if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop
do {
*pBuf=~(*pBuf);
// adjust the buffer forward 8 rows worth of data
pBuf += WIDTH;
// adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
h -= 8;
} while(h >= 8);
}
else {
// store a local value to work with
register uint8_t val = (color == WHITE) ? 255 : 0;
do {
// write our value in
*pBuf = val;
// adjust the buffer forward 8 rows worth of data
pBuf += WIDTH;
// adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
h -= 8;
} while(h >= 8);
}
}
// now do the final partial byte, if necessary
if(h) {
mod = h & 7;
// this time we want to mask the low bits of the byte, vs the high bits we did above
// register uint8_t mask = (1 << mod) - 1;
// note - lookup table results in a nearly 10% performance improvement in fill* functions
static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
register uint8_t mask = postmask[mod];
switch (color)
{
case WHITE: *pBuf |= mask; break;
case BLACK: *pBuf &= ~mask; break;
case INVERSE: *pBuf ^= mask; break;
}
}
}
/*
void Renderer::drawPixel(int16_t x, int16_t y, uint16_t color) {
if (jdrawPixel)(*jdrawPixel)(x,y,color);
}
*/
// the most basic function, set a single pixel
void Renderer::drawPixel(int16_t x, int16_t y, uint16_t color) {
if (!buffer) return;
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
return;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 1:
renderer_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
renderer_swap(x, y);
y = HEIGHT - y - 1;
break;
}
// x is which column
switch (color)
{
case WHITE: buffer[x+ (y/8)*WIDTH] |= (1 << (y&7)); break;
case BLACK: buffer[x+ (y/8)*WIDTH] &= ~(1 << (y&7)); break;
case INVERSE: buffer[x+ (y/8)*WIDTH] ^= (1 << (y&7)); break;
}
}
// this is called for every driver
void Renderer::setDrawMode(uint8_t mode) {
drawmode=mode;
}
void Renderer::invertDisplay(boolean i) {
}
void Renderer::reverseDisplay(boolean i) {
}
void Renderer::setScrollMargins(uint16_t top, uint16_t bottom) {
}
void Renderer::scrollTo(uint16_t y) {
}
void VButton::xdrawButton(bool inverted) {
wr_redir=1;
drawButton(inverted);
wr_redir=0;
}
boolean VButton::didhit(int16_t x, int16_t y) {
return ((x >= spars.xp) && (x < (int16_t) (spars.xp + spars.xs)) &&
(y >= spars.yp) && (y < (int16_t) (spars.yp + spars.ys)));
}
void VButton::SliderInit(Renderer *renderer, uint16_t xp, uint16_t yp, uint16_t xs, uint16_t ys, uint16_t nelem, uint16_t bgcol, uint16_t frcol, uint16_t barcol) {
spars.xp = xp;
spars.yp = yp;
spars.xs = xs;
spars.ys = ys;
spars.nelem = nelem;
spars.bgcol = bgcol;
spars.frcol = frcol;
spars.barcol = barcol;
rend = renderer;
rend->fillRect(spars.xp, spars.yp, spars.xs, spars.ys, spars.bgcol);
if (xs < ys) {
float bxs = spars.xs - 6;
float bys = (float)(spars.ys - 6) / (float)nelem;
float bxp = xp + 3;
float byp = yp + 3;
for (uint32_t count = 0; count < spars.nelem; count++) {
rend->fillRect(bxp, byp, bxs, bys - 3, spars.barcol);
rend->drawRect(bxp, byp, bxs, bys - 3, spars.frcol);
byp += bys;
}
} else {
float bys = spars.ys - 6;
float bxs = (float)(spars.xs - 6) / (float)nelem;
float byp = yp + 3;
float bxp = xp + 3;
for (uint32_t count = 0; count < spars.nelem; count++) {
rend->fillRect(bxp, byp, bxs - 3 , bys, spars.barcol);
rend->drawRect(bxp, byp, bxs - 3, bys, spars.frcol);
bxp += bxs;
}
}
}
uint16_t VButton::UpdateSlider(int16_t x, int16_t y) {
uint16_t elems = spars.nelem + 1;
if (x < 0) {
x = spars.xp + (-x * spars.xs) / 100;
y = spars.yp + (spars.ys - (-y * spars.ys) / 100);
}
if (spars.xs < spars.ys) {
uint16_t dy = spars.ys - (y - spars.yp);
uint16_t limit = elems - ((float)dy /(float)spars.ys * elems);
float bxs = spars.xs - 6;
float bys = (float)(spars.ys - 6) / (float)spars.nelem;
float bxp = spars.xp + 3;
float byp = spars.yp + 3;
uint16_t col;
for (uint32_t count = 0; count < spars.nelem; count++) {
if (count >= limit) {
col = spars.barcol;
} else {
col = spars.bgcol;
}
rend->fillRect(bxp, byp, bxs, bys - 3, col);
rend->drawRect(bxp, byp, bxs, bys - 3, spars.frcol);
byp += bys;
}
return 100 - (float(y - spars.yp) / (float)spars.ys) * 100.0;
} else {
uint16_t limit = (x - spars.xp) * elems / spars.xs;
float bys = spars.ys - 6;
float bxs = (float)(spars.xs - 6) / (float)spars.nelem;
float byp = spars.yp + 3;
float bxp = spars.xp + 3;
uint16_t col;
for (uint32_t count = 0; count < spars.nelem; count++) {
if (count < limit) {
col = spars.barcol;
} else {
col = spars.bgcol;
}
rend->fillRect(bxp, byp, bxs - 3, bys, col);
rend->drawRect(bxp, byp, bxs - 3 , bys, spars.frcol);
bxp += bxs;
}
return (float(x - spars.xp) / (float)spars.xs) * 100.0;
}
}
/* END OF FILE */