Add support for GIF images
This commit is contained in:
1014
project/Core/Src/gifdec.c
Normal file
1014
project/Core/Src/gifdec.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,7 @@ int main(void)
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
MX_LWIP_Process();
|
||||
lcd_task();
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user