Add support for GIF images

This commit is contained in:
L-diy
2023-11-14 23:25:41 +01:00
parent e6255c4fea
commit ad2a630400
5 changed files with 1493 additions and 8 deletions

1014
project/Core/Src/gifdec.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,20 @@
* @file lcd_api.c
* @brief LCD API implementation
* @author Tim S.
* @author Lorenz C.
*/
#include "lcd_api.h"
static const char* TAG = "lcd_api";
static DMA2D_HandleTypeDef hDma2dHandler2;
static lcd_gif_t gifs[LCD_MAX_GIFS]; // Array of GIF slots
static lcd_gif_t* get_free_gif_slot(void);
static void gif_draw_cb(GIFDRAW* pDraw);
static inline void free_gif_slot(lcd_gif_t* gif);
void lcd_init(bool bl_on) {
LOG_INFO(TAG, "Init LCD");
@@ -28,7 +36,51 @@ void lcd_init(bool bl_on) {
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_12, GPIO_PIN_RESET);
}
void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT *font) {
void lcd_task(void) {
// Play the next frame of each GIF if it's time
for (uint8_t i = 0; i < LCD_MAX_GIFS; i++) {
int ret;
// Check if the GIF is in use
if (gifs[i].src == NULL) {
continue;
}
// Check if it's time to play the next frame
if (HAL_GetTick() - gifs[i].last_frame_time < gifs[i].frame_delay) {
continue;
}
// Play the next frame
ret = GIF_playFrame(&gifs[i].gif, &gifs[i].frame_delay, &gifs[i].gif);
if (ret == 0) { // No more frames
// Infinite loop
if (gifs[i].loop_count <= 0) {
GIF_reset(&gifs[i].gif);
continue;
}
// Loop again
if (gifs[i].cur_loop < gifs[i].loop_count) {
GIF_reset(&gifs[i].gif);
gifs[i].cur_loop++;
continue;
}
// No more loops, free slot
LOG_DEBUG(TAG, "GIF finished, freeing slot");
free_gif_slot(&gifs[i]);
} else if (ret == -1) { // Error
LOG_WARN(TAG, "GIF_playFrame error, freeing slot");
free_gif_slot(&gifs[i]);
}
// Update the last frame time
gifs[i].last_frame_time = HAL_GetTick();
}
}
void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t color, uint32_t bg_color, sFONT* font) {
LOG_INFO(TAG, "Display text: %s @x=%d,y=%d", text, x_pos, y_pos);
uint16_t tot_length = x_pos + (strlen(text) * font->Width);
@@ -61,13 +113,11 @@ void lcd_display_text(uint8_t* text, uint16_t x_pos, uint16_t y_pos, uint32_t co
}
void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_t x_size, uint32_t y_size, uint32_t color_mode) {
uint32_t address = hLtdcHandler.LayerCfg[1].FBStartAdress + (((BSP_LCD_GetXSize() * y_pos) + x_pos) * (4));
void *p_dst = (void *)address;
void* p_dst = (void*)address;
hDma2dHandler2.Init.Mode = DMA2D_M2M_PFC;
hDma2dHandler2.Init.ColorMode = DMA2D_ARGB8888;
hDma2dHandler2.Init.Mode = DMA2D_M2M_PFC;
hDma2dHandler2.Init.ColorMode = DMA2D_ARGB8888;
hDma2dHandler2.Init.OutputOffset = BSP_LCD_GetXSize() - x_size;
hDma2dHandler2.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
@@ -83,7 +133,7 @@ void lcd_draw_raw_img(const void* p_src, uint32_t x_pos, uint32_t y_pos, uint32_
}
LOG_INFO(TAG, "DMA2D config layer");
if (HAL_DMA2D_ConfigLayer(&hDma2dHandler2, 1) != HAL_OK) {
LOG_CRIT(TAG, "HAL_DMA2D_ConfigLayer error");
LOG_CRIT(TAG, "HAL_DMA2D_ConfigLayer error");
return;
}
LOG_INFO(TAG, "DMA2D start");
@@ -102,7 +152,7 @@ void lcd_draw_bmp_img(uint8_t* bmp_buff, uint32_t x_pos, uint32_t y_pos) {
void lcd_draw_img_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos) {
llfs_file_t* file = llfs_file_open(name);
if (file != NULL) {
BSP_LCD_DrawBitmap(x_pos, y_pos, file->data);
BSP_LCD_DrawBitmap(x_pos, y_pos, (uint8_t*)file->data);
return;
}
LOG_WARN(TAG, "File \"%s\" not found", file->name);
@@ -111,3 +161,128 @@ void lcd_draw_img_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos) {
void lcd_clear(uint32_t color) {
BSP_LCD_Clear(color);
}
lcd_gif_t* lcd_draw_gif(uint8_t* src, size_t size, uint32_t x_pos, uint32_t y_pos) {
lcd_gif_t* gif;
// Get a free GIF slot
if ((gif = get_free_gif_slot()) == NULL) {
LOG_WARN(TAG, "No free GIF slots");
return NULL;
}
// Open the GIF and reset slot values
GIF_begin(&(gif->gif), GIF_PALETTE_RGB888);
if (GIF_openRAM(&(gif->gif), src, (int)size, gif_draw_cb)) {
gif->src = src;
gif->x_pos = x_pos;
gif->y_pos = y_pos;
gif->last_frame_time = 0;
gif->loop_count = GIF_getLoopCount(&(gif->gif));
gif->cur_loop = 1;
gif->frame_delay = 0;
gif->gif.ucDrawType = GIF_DRAW_RAW;
gif->gif.ucPaletteType = GIF_PALETTE_RGB888;
return gif;
}
// Failed to open GIF
LOG_WARN(TAG, "GIF_openRAM failed");
free_gif_slot(gif);
return NULL;
}
lcd_gif_t* lcd_draw_gif_from_fs(const char* name, uint32_t x_pos, uint32_t y_pos) {
lcd_gif_t* gif;
llfs_file_t* file;
// Get the file from llfs
file = llfs_file_open(name);
if (file == NULL) {
LOG_WARN(TAG, "File \"%s\" not found", name);
return NULL;
}
// Draw the GIF using the file data
gif = lcd_draw_gif((uint8_t*)file->data, file->len, x_pos, y_pos);
return gif;
}
void lcd_stop_gif(lcd_gif_t* gif) {
free_gif_slot(gif);
}
bool lcd_gif_is_playing(lcd_gif_t* gif) {
return gif->src != NULL;
}
/**
* @brief Get a free GIF slot
* Get the next GIF slot that is not in use
*
* @return Pointer to the GIF slot, NULL if none are available
*/
static lcd_gif_t* get_free_gif_slot(void) {
for (int i = 0; i < 5; i++) {
if (gifs[i].src == NULL) {
return &gifs[i];
}
}
return NULL;
}
/**
* @brief Free a GIF slot
* Release a GIF slot, and free the memory
*
* @param gif Pointer to the GIF slot to free
*/
static inline void free_gif_slot(lcd_gif_t* gif) {
gif->src = NULL;
GIF_close(&gif->gif);
}
/**
* @brief Callback function used by the GIF decoder
* This function is called by the GIF decoder to draw a new line of pixels
* @todo See if it's possible to use DMA2D for this
*
* @param pDraw Pointer to the GIFDRAW struct
*/
static void gif_draw_cb(GIFDRAW* pDraw) {
lcd_gif_t* gif = (lcd_gif_t*)pDraw->pUser;
uint8_t* palette = pDraw->pPalette24; // The RGB888 color palette
uint8_t* p_src = pDraw->pPixels; // Source pixel pointer
int y = pDraw->iY + pDraw->y; // Current line being drawn
// Calculate the destination address of the first pixel in the line
uint32_t address = hLtdcHandler.LayerCfg[1].FBStartAdress + (((BSP_LCD_GetXSize() * gif->y_pos) + gif->x_pos) * 4)
+ (y * (BSP_LCD_GetXSize() * 4));
// Restore the background
if (pDraw->ucDisposalMethod == 2) {
for (int x = 0; x < pDraw->iWidth; x++) {
if (p_src[x] == pDraw->ucTransparent) {
p_src[x] = pDraw->ucBackground;
}
}
pDraw->ucHasTransparency = 0;
}
// Draw each pixel in the line
for (int x = 0; x < pDraw->iWidth; x++) {
uint8_t pixel = *p_src++;
// Skip transparent pixels
if (pDraw->ucHasTransparency && pixel == pDraw->ucTransparent) {
continue;
}
// Get the color from the palette and convert it to ARGB8888
uint8_t *p = palette + (pixel * 3);
uint32_t color = (0xFF << 24) | (p[0] << 16) | (p[1] << 8) | p[2];
// Draw the pixel
((uint32_t *)address)[x] = color;
}
}

View File

@@ -136,6 +136,7 @@ int main(void)
/* USER CODE BEGIN 3 */
MX_LWIP_Process();
lcd_task();
}
/* USER CODE END 3 */
}