565 lines
14 KiB
C++
565 lines
14 KiB
C++
#include "uDisplay.h"
|
|
#include "uDisplay_config.h"
|
|
#include "uDisplay_spi.h"
|
|
|
|
// ===== Basic Drawing Primitives =====
|
|
|
|
static constexpr uint16_t RGB16_TO_MONO = 0x8410;
|
|
static constexpr uint16_t RGB16_SWAP_TO_MONO = 0x1084;
|
|
#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; }
|
|
|
|
void uDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
#ifdef USE_ESP32_S3
|
|
if (interface == _UDSP_RGB) {
|
|
drawPixel_RGB(x, y, color);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (ep_mode) {
|
|
drawPixel_EPD(x, y, color);
|
|
return;
|
|
}
|
|
|
|
if (framebuffer) {
|
|
Renderer::drawPixel(x, y, color);
|
|
return;
|
|
}
|
|
|
|
if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return;
|
|
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
setAddrWindow_int(x, y, 1, 1);
|
|
WriteColor(color);
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
}
|
|
|
|
void uDisplay::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
|
|
if (ep_mode) {
|
|
drawFastHLine_EPD(x, y, w, color);
|
|
return;
|
|
}
|
|
|
|
if (framebuffer) {
|
|
Renderer::drawFastHLine(x, y, w, color);
|
|
return;
|
|
}
|
|
|
|
// Rudimentary clipping
|
|
if((x >= _width) || (y >= _height)) return;
|
|
if((x + w - 1) >= _width) w = _width - x;
|
|
|
|
#ifdef USE_ESP32_S3
|
|
if (interface == _UDSP_RGB) {
|
|
if (cur_rot > 0) {
|
|
while (w--) {
|
|
drawPixel_RGB(x , y , color);
|
|
x++;
|
|
}
|
|
} else {
|
|
uint16_t *fb = rgb_fb;
|
|
fb += (int32_t)y * _width;
|
|
fb += x;
|
|
while (w--) {
|
|
*fb = color;
|
|
Cache_WriteBack_Addr((uint32_t)fb, 2);
|
|
fb++;
|
|
x++;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
setAddrWindow_int(x, y, w, 1);
|
|
|
|
if (col_mode == 18) {
|
|
uint8_t r = (color & 0xF800) >> 11;
|
|
uint8_t g = (color & 0x07E0) >> 5;
|
|
uint8_t b = color & 0x001F;
|
|
r = (r * 255) / 31;
|
|
g = (g * 255) / 63;
|
|
b = (b * 255) / 31;
|
|
|
|
while (w--) {
|
|
ulcd_data8(r);
|
|
ulcd_data8(g);
|
|
ulcd_data8(b);
|
|
}
|
|
} else {
|
|
while (w--) {
|
|
WriteColor(color);
|
|
}
|
|
}
|
|
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
}
|
|
|
|
void uDisplay::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
|
|
if (ep_mode) {
|
|
drawFastVLine_EPD(x, y, h, color);
|
|
return;
|
|
}
|
|
|
|
if (framebuffer) {
|
|
Renderer::drawFastVLine(x, y, h, color);
|
|
return;
|
|
}
|
|
|
|
// Rudimentary clipping
|
|
if ((x >= _width) || (y >= _height)) return;
|
|
if ((y + h - 1) >= _height) h = _height - y;
|
|
|
|
#ifdef USE_ESP32_S3
|
|
if (interface == _UDSP_RGB) {
|
|
if (cur_rot > 0) {
|
|
while (h--) {
|
|
drawPixel_RGB(x , y , color);
|
|
y++;
|
|
}
|
|
} else {
|
|
uint16_t *fb = rgb_fb;
|
|
fb += (int32_t)y * _width;
|
|
fb += x;
|
|
while (h--) {
|
|
*fb = color;
|
|
Cache_WriteBack_Addr((uint32_t)fb, 2);
|
|
fb+=_width;
|
|
y++;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
setAddrWindow_int(x, y, 1, h);
|
|
|
|
if (col_mode == 18) {
|
|
uint8_t r = (color & 0xF800) >> 11;
|
|
uint8_t g = (color & 0x07E0) >> 5;
|
|
uint8_t b = color & 0x001F;
|
|
r = (r * 255) / 31;
|
|
g = (g * 255) / 63;
|
|
b = (b * 255) / 31;
|
|
|
|
while (h--) {
|
|
ulcd_data8(r);
|
|
ulcd_data8(g);
|
|
ulcd_data8(b);
|
|
}
|
|
} else {
|
|
while (h--) {
|
|
WriteColor(color);
|
|
}
|
|
}
|
|
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
}
|
|
|
|
void uDisplay::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
#ifdef USE_ESP32_S3
|
|
if (interface == _UDSP_RGB) {
|
|
for (uint32_t yp = y; yp < y + h; yp++) {
|
|
drawFastHLine(x, yp, w, color);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (ep_mode) {
|
|
fillRect_EPD(x, y, w, h, color);
|
|
return;
|
|
}
|
|
|
|
if (framebuffer) {
|
|
Renderer::fillRect(x, y, w, h, color);
|
|
return;
|
|
}
|
|
|
|
if((x >= _width) || (y >= _height)) return;
|
|
if((x + w - 1) >= _width) w = _width - x;
|
|
if((y + h - 1) >= _height) h = _height - y;
|
|
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
setAddrWindow_int(x, y, w, h);
|
|
|
|
if (col_mode == 18) {
|
|
uint8_t r = (color & 0xF800) >> 11;
|
|
uint8_t g = (color & 0x07E0) >> 5;
|
|
uint8_t b = color & 0x001F;
|
|
r = (r * 255) / 31;
|
|
g = (g * 255) / 63;
|
|
b = (b * 255) / 31;
|
|
|
|
for (y = h; y > 0; y--) {
|
|
for (x = w; x > 0; x--) {
|
|
ulcd_data8(r);
|
|
ulcd_data8(g);
|
|
ulcd_data8(b);
|
|
}
|
|
}
|
|
} else {
|
|
for (y = h; y > 0; y--) {
|
|
for (x = w; x > 0; x--) {
|
|
WriteColor(color);
|
|
}
|
|
}
|
|
}
|
|
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
}
|
|
|
|
void uDisplay::fillScreen(uint16_t color) {
|
|
fillRect(0, 0, width(), height(), color);
|
|
}
|
|
|
|
static inline void lvgl_color_swap(uint16_t *data, uint16_t len) { for (uint32_t i = 0; i < len; i++) (data[i] = data[i] << 8 | data[i] >> 8); }
|
|
|
|
void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean not_swapped) {
|
|
|
|
if (lvgl_param.swap_color) {
|
|
not_swapped = !not_swapped;
|
|
}
|
|
|
|
//Serial.printf("push %x - %d - %d - %d\n", (uint32_t)data, len, not_swapped, lvgl_param.data);
|
|
|
|
// Isolating _UDSP_RGB to increase code sharing
|
|
//
|
|
// Use ESP-IDF LCD driver to push colors and rely on the following assumptions:
|
|
// * bytes swapping is already handled in the driver configuration (see uDisplay::Init()),
|
|
// * pushColors() is only called with not_swapped equals true,
|
|
// * cache flushing is done by the LCD driver.
|
|
if (interface == _UDSP_RGB) {
|
|
#ifdef USE_ESP32_S3
|
|
if (!not_swapped) {
|
|
// internal error -> write error message but continue (with possibly wrong colors)
|
|
AddLog(LOG_LEVEL_ERROR, PSTR("DSP: Unexpected byte-swapping requested in pushColors()"));
|
|
}
|
|
|
|
// check that bytes count matches the size of area, and remove from inner loop
|
|
if ((seta_yp2 - seta_yp1) * (seta_xp2 - seta_xp2) > len) { return; }
|
|
|
|
esp_lcd_panel_draw_bitmap(_panel_handle, seta_xp1, seta_yp1, seta_xp2, seta_yp2, (void *)data);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
if (not_swapped == false) {
|
|
// called from LVGL bytes are swapped
|
|
if (bpp != 16) {
|
|
// lvgl_color_swap(data, len); -- no need to swap anymore, we have inverted the mask
|
|
pushColorsMono(data, len, true);
|
|
return;
|
|
}
|
|
|
|
if ( (col_mode != 18) && (spi_dc >= 0) && (spi_nr <= 2) ) {
|
|
// special version 8 bit spi I or II
|
|
#ifdef ESP8266
|
|
lvgl_color_swap(data, len);
|
|
while (len--) {
|
|
uspi->write(*data++);
|
|
}
|
|
#else
|
|
if (lvgl_param.use_dma) {
|
|
pushPixelsDMA(data, len );
|
|
} else {
|
|
uspi->writeBytes((uint8_t*)data, len * 2);
|
|
}
|
|
#endif
|
|
} else {
|
|
|
|
#ifdef ESP32
|
|
if ( (col_mode == 18) && (spi_dc >= 0) && (spi_nr <= 2) ) {
|
|
uint8_t *line = (uint8_t*)malloc(len * 3);
|
|
uint8_t *lp = line;
|
|
if (line) {
|
|
uint16_t color;
|
|
for (uint32_t cnt = 0; cnt < len; cnt++) {
|
|
color = *data++;
|
|
color = (color << 8) | (color >> 8);
|
|
uint8_t r = (color & 0xF800) >> 11;
|
|
uint8_t g = (color & 0x07E0) >> 5;
|
|
uint8_t b = color & 0x001F;
|
|
r = (r * 255) / 31;
|
|
g = (g * 255) / 63;
|
|
b = (b * 255) / 31;
|
|
*lp++ = r;
|
|
*lp++ = g;
|
|
*lp++ = b;
|
|
}
|
|
|
|
if (lvgl_param.use_dma) {
|
|
pushPixels3DMA(line, len );
|
|
} else {
|
|
uspi->writeBytes(line, len * 3);
|
|
}
|
|
free(line);
|
|
}
|
|
|
|
} else {
|
|
// 9 bit and others
|
|
if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) {
|
|
#ifdef USE_ESP32_S3
|
|
pb_pushPixels(data, len, true, false);
|
|
#endif // USE_ESP32_S3
|
|
} else {
|
|
lvgl_color_swap(data, len);
|
|
while (len--) {
|
|
WriteColor(*data++);
|
|
}
|
|
}
|
|
}
|
|
#endif // ESP32
|
|
|
|
#ifdef ESP8266
|
|
lvgl_color_swap(data, len);
|
|
while (len--) {
|
|
WriteColor(*data++);
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
// called from displaytext, no byte swap, currently no dma here
|
|
|
|
if (bpp != 16) {
|
|
pushColorsMono(data, len);
|
|
return;
|
|
}
|
|
if ( (col_mode != 18) && (spi_dc >= 0) && (spi_nr <= 2) ) {
|
|
// special version 8 bit spi I or II
|
|
#ifdef ESP8266
|
|
while (len--) {
|
|
//uspi->write(*data++);
|
|
WriteColor(*data++);
|
|
}
|
|
#else
|
|
uspi->writePixels(data, len * 2);
|
|
#endif
|
|
} else {
|
|
// 9 bit and others
|
|
if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) {
|
|
#ifdef USE_ESP32_S3
|
|
pb_pushPixels(data, len, false, false);
|
|
#endif // USE_ESP32_S3
|
|
} else {
|
|
while (len--) {
|
|
WriteColor(*data++);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// convert to mono, these are framebuffer based
|
|
void uDisplay::pushColorsMono(uint16_t *data, uint16_t len, bool rgb16_swap) {
|
|
// pixel is white if at least one of the 3 components is above 50%
|
|
// this is tested with a simple mask, swapped if needed
|
|
uint16_t rgb16_to_mono_mask = rgb16_swap ? RGB16_SWAP_TO_MONO : RGB16_TO_MONO;
|
|
|
|
for (uint32_t y = seta_yp1; y < seta_yp2; y++) {
|
|
seta_yp1++;
|
|
if (lvgl_param.invert_bw) {
|
|
for (uint32_t x = seta_xp1; x < seta_xp2; x++) {
|
|
uint16_t color = *data++;
|
|
if (bpp == 1) color = (color & rgb16_to_mono_mask) ? 0 : 1;
|
|
drawPixel(x, y, color); // todo - inline the method to save speed
|
|
len--;
|
|
if (!len) return; // failsafe - exist if len (pixel number) is exhausted
|
|
}
|
|
} else {
|
|
for (uint32_t x = seta_xp1; x < seta_xp2; x++) {
|
|
uint16_t color = *data++;
|
|
if (bpp == 1) color = (color & rgb16_to_mono_mask) ? 1 : 0;
|
|
drawPixel(x, y, color); // todo - inline the method to save speed
|
|
len--;
|
|
if (!len) return; // failsafe - exist if len (pixel number) is exhausted
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void uDisplay::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
|
if (bpp != 16 || interface == _UDSP_RGB) {
|
|
// Just save params or update frame
|
|
if (!x0 && !y0 && !x1 && !y1) {
|
|
if (!ep_mode) {
|
|
Updateframe();
|
|
}
|
|
} else {
|
|
seta_xp1 = x0;
|
|
seta_xp2 = x1;
|
|
seta_yp1 = y0;
|
|
seta_yp2 = y1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (interface == _UDSP_RGB) {
|
|
return;
|
|
}
|
|
|
|
if (!x0 && !y0 && !x1 && !y1) {
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
} else {
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
setAddrWindow_int(x0, y0, x1 - x0, y1 - y0);
|
|
}
|
|
}
|
|
|
|
void uDisplay::setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
|
if (interface == _UDSP_RGB) {
|
|
return;
|
|
}
|
|
|
|
x += x_addr_offs[cur_rot];
|
|
y += y_addr_offs[cur_rot];
|
|
|
|
if (sa_mode != 8) {
|
|
uint32_t xa = ((uint32_t)x << 16) | (x + w - 1);
|
|
uint32_t ya = ((uint32_t)y << 16) | (y + h - 1);
|
|
|
|
ulcd_command(saw_1);
|
|
ulcd_data32(xa);
|
|
|
|
ulcd_command(saw_2);
|
|
ulcd_data32(ya);
|
|
|
|
if (saw_3 != 0xff) {
|
|
ulcd_command(saw_3); // write to RAM
|
|
}
|
|
} else {
|
|
uint16_t x2 = x + w - 1,
|
|
y2 = y + h - 1;
|
|
|
|
if (cur_rot & 1) { // Vertical address increment mode
|
|
renderer_swap(x,y);
|
|
renderer_swap(x2,y2);
|
|
}
|
|
ulcd_command(saw_1);
|
|
if (allcmd_mode) {
|
|
ulcd_data8(x);
|
|
ulcd_data8(x2);
|
|
} else {
|
|
ulcd_command(x);
|
|
ulcd_command(x2);
|
|
}
|
|
ulcd_command(saw_2);
|
|
if (allcmd_mode) {
|
|
ulcd_data8(y);
|
|
ulcd_data8(y2);
|
|
} else {
|
|
ulcd_command(y);
|
|
ulcd_command(y2);
|
|
}
|
|
if (saw_3 != 0xff) {
|
|
ulcd_command(saw_3); // write to RAM
|
|
}
|
|
}
|
|
}
|
|
|
|
void uDisplay::setRotation(uint8_t rotation) {
|
|
cur_rot = rotation;
|
|
|
|
if (framebuffer) {
|
|
Renderer::setRotation(cur_rot);
|
|
return;
|
|
}
|
|
|
|
if (interface == _UDSP_SPI || interface == _UDSP_PAR8 || interface == _UDSP_PAR16) {
|
|
if (ep_mode) {
|
|
Renderer::setRotation(cur_rot);
|
|
return;
|
|
}
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
ulcd_command(madctrl);
|
|
|
|
if (!allcmd_mode) {
|
|
ulcd_data8(rot[cur_rot]);
|
|
} else {
|
|
ulcd_command(rot[cur_rot]);
|
|
}
|
|
|
|
if ((sa_mode == 8) && !allcmd_mode) {
|
|
ulcd_command(startline);
|
|
ulcd_data8((cur_rot < 2) ? height() : 0);
|
|
}
|
|
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
}
|
|
|
|
switch (rotation) {
|
|
case 0:
|
|
_width = gxs;
|
|
_height = gys;
|
|
break;
|
|
case 1:
|
|
_width = gys;
|
|
_height = gxs;
|
|
break;
|
|
case 2:
|
|
_width = gxs;
|
|
_height = gys;
|
|
break;
|
|
case 3:
|
|
_width = gys;
|
|
_height = gxs;
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_ESP32_S3
|
|
if (interface == _UDSP_RGB) {
|
|
// Utilize the ESP-IDF LCD driver's support for display rotation
|
|
esp_lcd_panel_mirror(_panel_handle, rotation == 1 || rotation == 2, rotation & 2);
|
|
esp_lcd_panel_swap_xy(_panel_handle, rotation & 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_ESP32_S3
|
|
void uDisplay::drawPixel_RGB(int16_t x, int16_t y, uint16_t color) {
|
|
int16_t w = _width, h = _height;
|
|
|
|
if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) {
|
|
return;
|
|
}
|
|
|
|
// check rotation, move pixel around if necessary
|
|
switch (cur_rot) {
|
|
case 1:
|
|
renderer_swap(w, h);
|
|
renderer_swap(x, y);
|
|
x = w - x - 1;
|
|
break;
|
|
case 2:
|
|
x = w - x - 1;
|
|
y = h - y - 1;
|
|
break;
|
|
case 3:
|
|
renderer_swap(w, h);
|
|
renderer_swap(x, y);
|
|
y = h - y - 1;
|
|
break;
|
|
}
|
|
|
|
uint16_t *fb = rgb_fb;
|
|
fb += (int32_t)y * w;
|
|
fb += x;
|
|
*fb = color;
|
|
Cache_WriteBack_Addr((uint32_t)fb, 2);
|
|
}
|
|
#endif |