mirror of
https://gitcode.com/gh_mirrors/es/esp32-opencv.git
synced 2025-08-06 18:24:38 +08:00
483 lines
13 KiB
C++
483 lines
13 KiB
C++
|
|
#include "app_screen.h"
|
|
|
|
#include "esp32/rom/tjpgd.h"
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include "esp_freertos_hooks.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_system.h"
|
|
#include "esp_log.h"
|
|
#include "lwip/sockets.h"
|
|
|
|
|
|
#include "board_def.h"
|
|
|
|
|
|
#define TAG "[Screen]"
|
|
|
|
CEspLcd *tft = NULL;
|
|
|
|
int image_debug = 0;
|
|
|
|
// === Special coordinates constants ===
|
|
typedef struct __attribute__((__packed__))
|
|
{
|
|
uint8_t r;
|
|
uint8_t g;
|
|
uint8_t b;
|
|
} color_t;
|
|
|
|
typedef struct {
|
|
uint16_t x1;
|
|
uint16_t y1;
|
|
uint16_t x2;
|
|
uint16_t y2;
|
|
} dispWin_t;
|
|
|
|
dispWin_t dispWin = {
|
|
.x1 = 0,
|
|
.y1 = 0,
|
|
.x2 = TFT_WITDH,
|
|
.y2 = TFT_HEIGHT,
|
|
};
|
|
|
|
typedef struct {
|
|
FILE *fhndl; // File handler for input function
|
|
int x; // image top left point X position
|
|
int y; // image top left point Y position
|
|
int s;
|
|
bool found_header;
|
|
uint8_t *membuff; // memory buffer containing the image
|
|
uint32_t bufsize; // size of the memory buffer
|
|
uint32_t bufptr; // memory buffer current position
|
|
color_t *linbuf[2]; // memory buffer used for display output
|
|
uint8_t linbuf_idx;
|
|
} JPGIODEV;
|
|
|
|
|
|
|
|
// User defined call-back function to input JPEG data from file
|
|
//---------------------
|
|
static UINT tjd_s_input(
|
|
JDEC *jd, // Decompression object
|
|
BYTE *buff, // Pointer to the read buffer (NULL:skip)
|
|
UINT nd // Number of bytes to read/skip from input stream
|
|
)
|
|
{
|
|
int rb = 0;
|
|
char recv_buf[16];
|
|
// Device identifier for the session (5th argument of jd_prepare function)
|
|
JPGIODEV *dev = (JPGIODEV *)jd->device;
|
|
|
|
while (!dev->found_header)
|
|
{
|
|
rb += read(dev->s, recv_buf, 1);
|
|
putchar(recv_buf[0]); // debug
|
|
if (recv_buf[0] == '\r')
|
|
{
|
|
rb += read(dev->s, recv_buf, 3);
|
|
putchar(recv_buf[0]); putchar(recv_buf[1]); putchar(recv_buf[2]); // debug
|
|
if ((recv_buf[0] == '\n') && (recv_buf[1] == '\r') && (recv_buf[2] == '\n'))
|
|
{
|
|
ESP_LOGI("TFT", "Found header ended at %d", rb);
|
|
dev->found_header = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buff)
|
|
{ // Read nd bytes from the input strem
|
|
rb = read(dev->s, buff, nd);
|
|
// ESP_LOGI("TFT", "Socket read %d, actual %d", nd, rb);
|
|
return rb; // Returns actual number of bytes read
|
|
}
|
|
else
|
|
{ // Remove nd bytes from the input stream
|
|
int seek_size = nd;
|
|
while (seek_size)
|
|
{
|
|
if (seek_size > sizeof(recv_buf))
|
|
{
|
|
rb = read(dev->s, recv_buf, sizeof(recv_buf));
|
|
}
|
|
else
|
|
{
|
|
rb = read(dev->s, recv_buf, seek_size);
|
|
}
|
|
seek_size -= rb;
|
|
}
|
|
return nd; // Returns actual number of bytes read
|
|
// if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd;
|
|
// else return 0;
|
|
}
|
|
}
|
|
|
|
// User defined call-back function to input JPEG data from file
|
|
//---------------------
|
|
static UINT tjd_input(
|
|
JDEC *jd, // Decompression object
|
|
BYTE *buff, // Pointer to the read buffer (NULL:skip)
|
|
UINT nd // Number of bytes to read/skip from input stream
|
|
)
|
|
{
|
|
int rb = 0;
|
|
// Device identifier for the session (5th argument of jd_prepare function)
|
|
JPGIODEV *dev = (JPGIODEV *)jd->device;
|
|
|
|
if (buff)
|
|
{ // Read nd bytes from the input strem
|
|
rb = fread(buff, 1, nd, dev->fhndl);
|
|
return rb; // Returns actual number of bytes read
|
|
}
|
|
else
|
|
{ // Remove nd bytes from the input stream
|
|
if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0)
|
|
return nd;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// User defined call-back function to input JPEG data from memory buffer
|
|
//-------------------------
|
|
static UINT tjd_buf_input(
|
|
JDEC *jd, // Decompression object
|
|
BYTE *buff, // Pointer to the read buffer (NULL:skip)
|
|
UINT nd // Number of bytes to read/skip from input stream
|
|
)
|
|
{
|
|
// Device identifier for the session (5th argument of jd_prepare function)
|
|
JPGIODEV *dev = (JPGIODEV *)jd->device;
|
|
if (!dev->membuff)
|
|
return 0;
|
|
if (dev->bufptr >= (dev->bufsize + 2))
|
|
return 0; // end of stream
|
|
|
|
if ((dev->bufptr + nd) > (dev->bufsize + 2))
|
|
nd = (dev->bufsize + 2) - dev->bufptr;
|
|
|
|
if (buff) {
|
|
// Read nd bytes from the input strem
|
|
memcpy(buff, dev->membuff + dev->bufptr, nd);
|
|
dev->bufptr += nd;
|
|
return nd; // Returns number of bytes read
|
|
} else {
|
|
// Remove nd bytes from the input stream
|
|
dev->bufptr += nd;
|
|
return nd;
|
|
}
|
|
}
|
|
|
|
|
|
// User defined call-back function to output RGB bitmap to display device
|
|
//----------------------
|
|
static UINT tjd_output(
|
|
JDEC *jd, // Decompression object of current session
|
|
void *bitmap, // Bitmap data to be output
|
|
JRECT *rect // Rectangular region to output
|
|
)
|
|
{
|
|
// Device identifier for the session (5th argument of jd_prepare function)
|
|
JPGIODEV *dev = (JPGIODEV *)jd->device;
|
|
|
|
// ** Put the rectangular into the display device **
|
|
int x;
|
|
int y;
|
|
int dleft, dtop, dright, dbottom;
|
|
BYTE *src = (BYTE *)bitmap;
|
|
|
|
int left = rect->left + dev->x;
|
|
int top = rect->top + dev->y;
|
|
int right = rect->right + dev->x;
|
|
int bottom = rect->bottom + dev->y;
|
|
|
|
if ((left > dispWin.x2) || (top > dispWin.y2))
|
|
return 1; // out of screen area, return
|
|
if ((right < dispWin.x1) || (bottom < dispWin.y1))
|
|
return 1; // out of screen area, return
|
|
|
|
if (left < dispWin.x1)
|
|
dleft = dispWin.x1;
|
|
else
|
|
dleft = left;
|
|
if (top < dispWin.y1)
|
|
dtop = dispWin.y1;
|
|
else
|
|
dtop = top;
|
|
if (right > dispWin.x2)
|
|
dright = dispWin.x2;
|
|
else
|
|
dright = right;
|
|
if (bottom > dispWin.y2)
|
|
dbottom = dispWin.y2;
|
|
else
|
|
dbottom = bottom;
|
|
|
|
if ((dleft > dispWin.x2) || (dtop > dispWin.y2))
|
|
return 1; // out of screen area, return
|
|
if ((dright < dispWin.x1) || (dbottom < dispWin.y1))
|
|
return 1; // out of screen area, return
|
|
|
|
uint32_t len = ((dright - dleft + 1) * (dbottom - dtop + 1)); // calculate length of data
|
|
|
|
if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) {
|
|
uint8_t *dest = (uint8_t *)(dev->linbuf[dev->linbuf_idx]);
|
|
|
|
for (y = top; y <= bottom; y++) {
|
|
for (x = left; x <= right; x++) {
|
|
// Clip to display area
|
|
if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) {
|
|
*dest++ = (*src++) & 0xFC;
|
|
*dest++ = (*src++) & 0xFC;
|
|
*dest++ = (*src++) & 0xFC;
|
|
} else
|
|
src += 3; // skip
|
|
}
|
|
}
|
|
|
|
//ESP_LOGI(TAG, "x1:%d x2:%d y1:%d y2:%d\n", dleft, dright, dtop, dbottom);
|
|
tft->setAddrWindow(dleft, dtop, dright, dbottom);
|
|
|
|
uint16_t *p = (uint16_t *)malloc(sizeof(uint16_t) * len);
|
|
if (!p) {
|
|
ESP_LOGE(TAG, "malloc fail");
|
|
return 0;
|
|
}
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
p[i] = tft->color565(dev->linbuf[dev->linbuf_idx][i].r, dev->linbuf[dev->linbuf_idx][i].g, dev->linbuf[dev->linbuf_idx][i].b);
|
|
}
|
|
tft->_fastSendBuf(p, len, true); // TODO: Swapping bytes directly in the camera register could enhance display
|
|
free(p);
|
|
|
|
dev->linbuf_idx = ((dev->linbuf_idx + 1) & 1);
|
|
} else {
|
|
ESP_LOGE(TAG, "Data size error: %d jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", len, left, top, right, bottom, dleft, dtop, dright, dbottom);
|
|
return 0; // stop decompression
|
|
}
|
|
return 1; // Continue to decompression
|
|
}
|
|
|
|
//==================================================================================
|
|
void TFT_jpg_image(int x, int y, uint8_t scale, int s, char *fname, uint8_t *buf, int size)
|
|
{
|
|
JPGIODEV dev;
|
|
struct stat sb;
|
|
char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
|
|
UINT sz_work = 3800; // Size of the working buffer (must be power of 2)
|
|
JDEC jd; // Decompression object (70 bytes)
|
|
JRESULT rc;
|
|
|
|
dev.linbuf[0] = NULL;
|
|
dev.linbuf[1] = NULL;
|
|
dev.linbuf_idx = 0;
|
|
|
|
dev.fhndl = NULL;
|
|
dev.s = s;
|
|
if (s >= 0)
|
|
{
|
|
// image from socket
|
|
dev.membuff = NULL;
|
|
dev.found_header = false;
|
|
dev.bufsize = 0;
|
|
dev.bufptr = 0;
|
|
}
|
|
else if (fname == NULL)
|
|
{
|
|
// image from buffer
|
|
dev.membuff = buf;
|
|
dev.bufsize = size;
|
|
dev.bufptr = 0;
|
|
}
|
|
else
|
|
{
|
|
// image from file
|
|
dev.membuff = NULL;
|
|
dev.bufsize = 0;
|
|
dev.bufptr = 0;
|
|
|
|
if (stat(fname, &sb) != 0)
|
|
{
|
|
if (image_debug)
|
|
printf("File error: %ss\r\n", strerror(errno));
|
|
goto exit;
|
|
}
|
|
|
|
dev.fhndl = fopen(fname, "r");
|
|
if (!dev.fhndl)
|
|
{
|
|
if (image_debug)
|
|
printf("Error opening file: %s\r\n", strerror(errno));
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (scale > 3)
|
|
scale = 3;
|
|
|
|
work = (char*)malloc(sz_work);
|
|
if (work)
|
|
{
|
|
if (dev.s >= 0)
|
|
rc = jd_prepare(&jd, tjd_s_input, (void *)work, sz_work, &dev);
|
|
else if (dev.membuff)
|
|
rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev);
|
|
else
|
|
rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev);
|
|
if (rc == JDR_OK)
|
|
{
|
|
if (x == CENTER)
|
|
x = ((dispWin.x2 - dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + dispWin.x1;
|
|
else if (x == RIGHT)
|
|
x = dispWin.x2 + 1 - (int)(jd.width >> scale);
|
|
|
|
if (y == CENTER)
|
|
y = ((dispWin.y2 - dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + dispWin.y1;
|
|
else if (y == BOTTOM)
|
|
y = dispWin.y2 + 1 - (int)(jd.height >> scale);
|
|
|
|
if (x < ((dispWin.x2 - 1) * -1))
|
|
x = (dispWin.x2 - 1) * -1;
|
|
if (y < ((dispWin.y2 - 1)) * -1)
|
|
y = (dispWin.y2 - 1) * -1;
|
|
if (x > (dispWin.x2 - 1))
|
|
x = dispWin.x2 - 1;
|
|
if (y > (dispWin.y2 - 1))
|
|
y = dispWin.y2 - 1;
|
|
|
|
dev.x = x;
|
|
dev.y = y;
|
|
|
|
dev.linbuf[0] = (color_t*)heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE * 3, MALLOC_CAP_DMA);
|
|
if (dev.linbuf[0] == NULL)
|
|
{
|
|
if (image_debug)
|
|
printf("Error allocating line buffer #0\r\n");
|
|
goto exit;
|
|
}
|
|
dev.linbuf[1] = (color_t*)heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE * 3, MALLOC_CAP_DMA);
|
|
if (dev.linbuf[1] == NULL)
|
|
{
|
|
if (image_debug)
|
|
printf("Error allocating line buffer #1\r\n");
|
|
goto exit;
|
|
}
|
|
|
|
// Start to decode the JPEG file
|
|
rc = jd_decomp(&jd, tjd_output, scale);
|
|
|
|
if (rc != JDR_OK)
|
|
{
|
|
if (image_debug)
|
|
printf("jpg decompression error %d\r\n", rc);
|
|
}
|
|
if (image_debug)
|
|
printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool);
|
|
}
|
|
else
|
|
{
|
|
if (image_debug)
|
|
printf("jpg prepare error %d\r\n", rc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (image_debug)
|
|
printf("work buffer allocation error\r\n");
|
|
}
|
|
|
|
exit:
|
|
if (work)
|
|
free(work); // free work buffer
|
|
if (dev.linbuf[0])
|
|
free(dev.linbuf[0]);
|
|
if (dev.linbuf[1])
|
|
free(dev.linbuf[1]);
|
|
if (dev.fhndl)
|
|
fclose(dev.fhndl); // close input file
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// lvgl
|
|
// =====================================================================================================================
|
|
|
|
#include "lvgl.h"
|
|
|
|
/* lvgl internal graphics buffers */
|
|
#define DISP_BUF_SIZE LV_HOR_RES_MAX * 24 // Horizontal resolution X number of lines sent to the driver
|
|
static lv_disp_buf_t disp_buf;
|
|
static lv_color_t buf_1[DISP_BUF_SIZE];
|
|
static lv_color_t buf_2[DISP_BUF_SIZE];
|
|
|
|
/*Write the internal buffer to the display. 'lv_flush_ready()' has to be called when finished*/
|
|
static void ex_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
|
|
{
|
|
tft->drawBitmap((int16_t)area->x1, (int16_t)area->y1, (const uint16_t *)color_p, (int16_t)(area->x2 - area->x1 + 1), (int16_t)(area->y2 - area->y1 + 1));
|
|
/* IMPORTANT!!!
|
|
* Inform the graphics library that you are ready with the flushing*/
|
|
lv_disp_t *disp = _lv_refr_get_disp_refreshing();
|
|
lv_disp_flush_ready(&disp->driver);
|
|
}
|
|
|
|
/*Calls `lv_tick_inc()` at regular interval to inform lvgl of elapsed time*/
|
|
static void IRAM_ATTR lv_tick_cb(void) {
|
|
lv_tick_inc(portTICK_RATE_MS);
|
|
}
|
|
|
|
void lvgl_init()
|
|
{
|
|
/* register lvgl tick callback function */
|
|
esp_register_freertos_tick_hook(lv_tick_cb);
|
|
|
|
/*lvgl initialize*/
|
|
lv_init();
|
|
|
|
/* init display buffers */
|
|
lv_disp_buf_init(&disp_buf, buf_1, buf_2, DISP_BUF_SIZE);
|
|
|
|
/* screen driver */
|
|
lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
|
|
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
|
|
|
|
disp_drv.flush_cb = ex_disp_flush;
|
|
disp_drv.buffer = &disp_buf;
|
|
|
|
/* Finally register the driver */
|
|
lv_disp_drv_register(&disp_drv);
|
|
}
|
|
|
|
// todo: separate lcv_hal_init() and lvgl_init()
|
|
void lvgl_lcd_hal_init()
|
|
{
|
|
lcd_conf_t lcd_pins = {
|
|
.lcd_model = LCD_MOD_ST7789,
|
|
.pin_num_miso = TFT_MISO,
|
|
.pin_num_mosi = TFT_MOSI,
|
|
.pin_num_clk = TFT_SCLK,
|
|
.pin_num_cs = TFT_CS,
|
|
.pin_num_dc = TFT_DC,
|
|
.pin_num_rst = TFT_RST,
|
|
.pin_num_bckl = TFT_BK,
|
|
.clk_freq = 26 * 1000 * 1000,
|
|
.rst_active_level = 0,
|
|
.bckl_active_level = 1,
|
|
.spi_host = HSPI_HOST,
|
|
.init_spi_bus = true,
|
|
};
|
|
|
|
/*Initialize SPI Handler*/
|
|
if (tft == NULL) {
|
|
tft = new CEspLcd(&lcd_pins, TFT_HEIGHT, TFT_WITDH);
|
|
}
|
|
|
|
/*screen initialize*/
|
|
tft->invertDisplay(true);
|
|
tft->setRotation(0);
|
|
tft->fillScreen(COLOR_BLACK);
|
|
|
|
/*init lvgl*/
|
|
lvgl_init();
|
|
}
|