mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-08-23 18:38:38 +08:00
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled
Weekly CI Scheduler / Trigger and Monitor CIs (push) Has been cancelled
Weekly CI Scheduler / Create Discussion Report (push) Has been cancelled
1. 8250 serila family (OFW, PCI, DWC, early) 2. Virtual serial (by graphic and input) 3. HVC early serial 4. ARM PL011 serial Signed-off-by: GuEe-GUI <2991707448@qq.com>
662 lines
15 KiB
C
662 lines
15 KiB
C
/*
|
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2023-02-25 GuEe-GUI the first version
|
|
*/
|
|
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include "psf.h"
|
|
#include "render.h"
|
|
|
|
struct render
|
|
{
|
|
struct rt_device *fbdev;
|
|
struct rt_device_graphic_info info;
|
|
struct fb_var_screeninfo var;
|
|
struct fb_fix_screeninfo fix;
|
|
|
|
struct psf_font psf;
|
|
rt_uint32_t xlate;
|
|
rt_uint32_t font_line_length;
|
|
rt_uint32_t buffer_idx;
|
|
rt_size_t buffer_size;
|
|
rt_size_t line_size;
|
|
rt_size_t screen_size;
|
|
rt_size_t font_size;
|
|
rt_uint8_t color_size;
|
|
rt_uint8_t *cursor, *cursor_backend;
|
|
|
|
struct render_point position, start_point, end_point;
|
|
|
|
rt_uint32_t foreground, background;
|
|
rt_uint32_t raw_foreground, raw_background;
|
|
rt_uint32_t red_off, green_off, blue_off, alpha_off;
|
|
rt_uint32_t red_mask, green_mask, blue_mask, alpha_mask;
|
|
|
|
void *hook_ptr;
|
|
|
|
void (*set_pixel)(void *fb, rt_uint32_t color);
|
|
rt_uint32_t (*get_pixel)(void *fb);
|
|
|
|
void *(*last_buffer)(void);
|
|
void *(*next_buffer)(void);
|
|
void (*move_buffer)(void *dst, void *src, rt_size_t size);
|
|
void (*pan_display)(void);
|
|
};
|
|
|
|
static struct render _render = {};
|
|
|
|
rt_err_t render_load_fbdev(struct rt_device *fbdev)
|
|
{
|
|
rt_err_t err = RT_EOK;
|
|
struct render *rd = &_render;
|
|
|
|
if ((err = rt_device_open(fbdev, 0)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
rd->fbdev = fbdev;
|
|
|
|
err |= rt_device_control(fbdev, FBIOGET_VSCREENINFO, &rd->var);
|
|
err |= rt_device_control(fbdev, FBIOGET_FSCREENINFO, &rd->fix);
|
|
err |= rt_device_control(fbdev, RTGRAPHIC_CTRL_GET_INFO, &rd->info);
|
|
|
|
rt_device_close(fbdev);
|
|
|
|
return err;
|
|
}
|
|
|
|
static rt_uint32_t parse_color(struct render_color *color)
|
|
{
|
|
rt_uint32_t color_value;
|
|
struct render *rd = &_render;
|
|
|
|
color_value = ((color->red & rd->red_mask) << rd->red_off) |
|
|
((color->green & rd->green_mask) << rd->green_off) |
|
|
((color->blue & rd->blue_mask) << rd->blue_off);
|
|
|
|
if (rd->var.transp.length)
|
|
{
|
|
color_value |= (color->alpha & rd->alpha_mask) << rd->alpha_off;
|
|
}
|
|
|
|
return color_value;
|
|
}
|
|
|
|
static void set_pixel16(void *fb, rt_uint32_t color)
|
|
{
|
|
rt_memcpy(fb, &color, 2);
|
|
}
|
|
|
|
static rt_uint32_t get_pixel16(void *fb)
|
|
{
|
|
rt_uint16_t color;
|
|
|
|
rt_memcpy(&color, fb, 2);
|
|
|
|
return color;
|
|
}
|
|
|
|
static void set_pixel24(void *fb, rt_uint32_t color)
|
|
{
|
|
rt_memcpy(fb, &color, 3);
|
|
}
|
|
|
|
static rt_uint32_t get_pixel24(void *fb)
|
|
{
|
|
rt_uint32_t color;
|
|
|
|
rt_memcpy(&color, fb, 3);
|
|
|
|
return color;
|
|
}
|
|
|
|
static void set_pixel32(void *fb, rt_uint32_t color)
|
|
{
|
|
*(rt_uint32_t *)fb = color;
|
|
}
|
|
|
|
static rt_uint32_t get_pixel32(void *fb)
|
|
{
|
|
return *(rt_uint32_t *)fb;
|
|
}
|
|
|
|
static void *last_buffer_single(void)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
return (void *)rd->info.framebuffer;
|
|
}
|
|
|
|
static void *fb_next_buffer_dummy(void)
|
|
{
|
|
return last_buffer_single();
|
|
}
|
|
|
|
static void fb_move_buffer_single(void *dst, void *src, rt_size_t size)
|
|
{
|
|
rt_memmove(dst, src, size);
|
|
}
|
|
|
|
static void fb_pan_display_dummy(void)
|
|
{
|
|
}
|
|
|
|
static void *last_buffer_multi(void)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
return (void *)rd->info.framebuffer + rd->buffer_idx * rd->line_size;
|
|
}
|
|
|
|
static void *fb_next_buffer(void)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
if (rd->buffer_idx < rd->buffer_size)
|
|
{
|
|
++rd->buffer_idx;
|
|
}
|
|
else
|
|
{
|
|
rd->buffer_idx = 0;
|
|
}
|
|
|
|
return last_buffer_multi();
|
|
}
|
|
|
|
static void fb_move_buffer_multi(void *dst, void *src, rt_size_t size)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
if (rd->buffer_idx == 0)
|
|
{
|
|
fb_move_buffer_single(dst, src, size);
|
|
}
|
|
}
|
|
|
|
static void fb_pan_display(void)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
rd->var.yoffset = rd->buffer_idx * rd->psf.height;
|
|
|
|
rt_device_control(rd->fbdev, FBIOPAN_DISPLAY, &rd->var);
|
|
}
|
|
|
|
static void color_hook_dummy(void *, rt_uint32_t *);
|
|
|
|
rt_err_t render_load_font(const char *psf_data, rt_size_t size,
|
|
struct render_color *foreground, struct render_color *background,
|
|
struct render_point *out_start_point, struct render_point *out_end_point)
|
|
{
|
|
rt_err_t err;
|
|
rt_uint8_t *font_data, *cursor, *cursor_backend;
|
|
rt_uint32_t foreground_color, background_color;
|
|
struct psf_font new_psf;
|
|
struct render *rd = &_render;
|
|
|
|
if ((err = psf_initialize(psf_data, &new_psf)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
if (!new_psf.count)
|
|
{
|
|
return -RT_EEMPTY;
|
|
}
|
|
|
|
/* Font + Cursor + Cursor backend */
|
|
rd->color_size = rd->var.bits_per_pixel / 8;
|
|
font_data = rt_malloc_align(rd->color_size * new_psf.glyph * (new_psf.count + 2), sizeof(rt_ubase_t));
|
|
|
|
if (!font_data)
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
cursor = font_data + rd->color_size * new_psf.glyph * new_psf.count;
|
|
cursor_backend = cursor + rd->color_size * new_psf.glyph * new_psf.count;
|
|
|
|
rd->red_off = rd->var.red.offset;
|
|
rd->red_mask = RT_GENMASK(rd->var.red.length, 0);
|
|
rd->green_off = rd->var.green.offset;
|
|
rd->green_mask = RT_GENMASK(rd->var.green.length, 0);
|
|
rd->blue_off = rd->var.blue.offset;
|
|
rd->blue_mask = RT_GENMASK(rd->var.blue.length, 0);
|
|
rd->alpha_off = rd->var.transp.offset;
|
|
rd->alpha_mask = RT_GENMASK(rd->var.transp.length, 0);
|
|
|
|
foreground_color = parse_color(foreground);
|
|
background_color = parse_color(background);
|
|
|
|
if ((err = psf_parse(&new_psf, font_data, cursor_backend,
|
|
rd->color_size, foreground_color, background_color)))
|
|
{
|
|
rt_free(font_data);
|
|
|
|
return err;
|
|
}
|
|
|
|
if (rd->psf.font_data)
|
|
{
|
|
rt_free((void *)rd->psf.font_data);
|
|
}
|
|
|
|
rd->foreground = rd->raw_foreground = foreground_color;
|
|
rd->background = rd->raw_background = background_color;
|
|
rd->hook_ptr = &color_hook_dummy;
|
|
|
|
rt_memcpy(&rd->psf, &new_psf, sizeof(rd->psf));
|
|
|
|
rd->font_line_length = rd->psf.width * rd->color_size;
|
|
rd->xlate = rd->fix.line_length - rd->font_line_length;
|
|
|
|
rd->line_size = rd->psf.height * rd->fix.line_length;
|
|
rd->screen_size = rd->var.xres * rd->var.yres * rd->color_size;
|
|
rd->font_size = rd->psf.glyph * rd->color_size;
|
|
rd->buffer_idx = 0;
|
|
rd->buffer_size = (rd->fix.smem_len - rd->screen_size) / rd->line_size;
|
|
|
|
if (rd->buffer_size)
|
|
{
|
|
rd->var.yres_virtual = (rd->fix.smem_len / rd->screen_size) * rd->var.yres;
|
|
|
|
if (rt_device_control(rd->fbdev, FBIOPUT_VSCREENINFO, &rd->var))
|
|
{
|
|
/* Enable double buffer fail */
|
|
rd->buffer_size = 0;
|
|
}
|
|
}
|
|
|
|
if (rd->buffer_size)
|
|
{
|
|
rd->last_buffer = last_buffer_multi;
|
|
rd->next_buffer = fb_next_buffer;
|
|
rd->move_buffer = fb_move_buffer_single;
|
|
rd->pan_display = fb_pan_display;
|
|
}
|
|
else
|
|
{
|
|
rd->last_buffer = last_buffer_single;
|
|
rd->next_buffer = fb_next_buffer_dummy;
|
|
rd->move_buffer = fb_move_buffer_multi;
|
|
rd->pan_display = fb_pan_display_dummy;
|
|
}
|
|
|
|
rd->cursor = cursor;
|
|
rd->cursor_backend = cursor_backend;
|
|
|
|
rd->start_point.row = rd->var.yoffset / rd->psf.height;
|
|
rd->start_point.col = rd->var.xoffset / rd->psf.width;
|
|
rd->end_point.row = rd->var.yres / rd->psf.height - 1;
|
|
rd->end_point.col = rd->var.xres / rd->psf.width - 1;
|
|
|
|
if (out_start_point)
|
|
{
|
|
rt_memcpy(out_start_point, &rd->start_point, sizeof(*out_start_point));
|
|
}
|
|
|
|
if (out_end_point)
|
|
{
|
|
rt_memcpy(out_end_point, &rd->end_point, sizeof(*out_end_point));
|
|
}
|
|
|
|
switch (rd->var.bits_per_pixel)
|
|
{
|
|
case 32:
|
|
rd->set_pixel = set_pixel32;
|
|
rd->get_pixel = get_pixel32;
|
|
break;
|
|
|
|
case 24:
|
|
rd->set_pixel = set_pixel24;
|
|
rd->get_pixel = get_pixel24;
|
|
break;
|
|
|
|
case 16:
|
|
rd->set_pixel = set_pixel16;
|
|
rd->get_pixel = get_pixel16;
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
void render_clear_display(void)
|
|
{
|
|
void *fb, *fb_end;
|
|
rt_uint32_t color;
|
|
rt_size_t color_size;
|
|
struct render *rd = &_render;
|
|
typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel;
|
|
|
|
fb = rd->next_buffer();
|
|
fb_end = fb + rd->screen_size;
|
|
|
|
color = rd->background;
|
|
color_size = rd->color_size;
|
|
|
|
while (fb < fb_end)
|
|
{
|
|
set_pixel_handler(fb, color);
|
|
|
|
fb += color_size;
|
|
}
|
|
|
|
rd->pan_display();
|
|
render_move_cursor(RT_NULL);
|
|
}
|
|
|
|
static void roll_display(void)
|
|
{
|
|
rt_uint32_t color;
|
|
rt_size_t screen_size, flush_size, color_size;
|
|
void *old_fb, *fb, *fb_end;
|
|
struct render *rd = &_render;
|
|
typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel;
|
|
|
|
color = rd->background;
|
|
color_size = rd->color_size;
|
|
screen_size = rd->screen_size;
|
|
flush_size = screen_size - rd->line_size;
|
|
|
|
old_fb = rd->last_buffer();
|
|
fb = rd->next_buffer();
|
|
fb_end = fb + screen_size;
|
|
|
|
rd->move_buffer(fb, old_fb + rd->line_size, flush_size);
|
|
/* The last line */
|
|
fb += flush_size;
|
|
|
|
while (fb < fb_end)
|
|
{
|
|
set_pixel_handler(fb, color);
|
|
|
|
fb += color_size;
|
|
}
|
|
|
|
rd->pan_display();
|
|
rt_device_control(rd->fbdev, FBIO_WAITFORVSYNC, RT_NULL);
|
|
}
|
|
|
|
static void color_hook_dummy(void *fb, rt_uint32_t *out_color)
|
|
{
|
|
}
|
|
|
|
static void color_hook_transform(void *fb, rt_uint32_t *out_color)
|
|
{
|
|
rt_uint32_t color;
|
|
struct render *rd = &_render;
|
|
|
|
rt_memcpy(&color, out_color, rd->color_size);
|
|
|
|
if (color == rd->raw_foreground)
|
|
{
|
|
*out_color = rd->foreground;
|
|
}
|
|
else if (color == rd->raw_background)
|
|
{
|
|
*out_color = rd->background;
|
|
}
|
|
}
|
|
|
|
void render_set_foreground(struct render_color *foreground)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
rd->foreground = parse_color(foreground);
|
|
|
|
if (rd->foreground != rd->raw_foreground)
|
|
{
|
|
rd->hook_ptr = color_hook_transform;
|
|
}
|
|
else
|
|
{
|
|
rd->hook_ptr = color_hook_dummy;
|
|
}
|
|
}
|
|
|
|
void render_set_background(struct render_color *background)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
rd->background = parse_color(background);
|
|
|
|
if (rd->foreground != rd->raw_foreground)
|
|
{
|
|
rd->hook_ptr = color_hook_transform;
|
|
}
|
|
else
|
|
{
|
|
rd->hook_ptr = color_hook_dummy;
|
|
}
|
|
}
|
|
|
|
static void color_hook_invert(void *fb, rt_uint32_t *out_color)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
*out_color ^= rd->get_pixel(fb) & ~(rd->alpha_mask << rd->alpha_off);
|
|
}
|
|
|
|
static void draw_block(void *block, void (*color_hook)(void *fb, rt_uint32_t *out_color))
|
|
{
|
|
void *fb;
|
|
rt_size_t color_size;
|
|
int font_width, font_height, xlate;
|
|
struct render *rd = &_render;
|
|
typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel;
|
|
|
|
color_size = rd->color_size;
|
|
font_width = rd->psf.width;
|
|
font_height = rd->psf.height;
|
|
xlate = rd->xlate;
|
|
|
|
fb = rd->last_buffer();
|
|
fb += rd->position.col * rd->font_line_length + rd->position.row * rd->line_size;
|
|
|
|
for (int y = 0; y < font_height; ++y)
|
|
{
|
|
for (int x = 0; x < font_width; ++x)
|
|
{
|
|
rt_uint32_t color = *(rt_uint32_t *)block;
|
|
|
|
color_hook(fb, &color);
|
|
set_pixel_handler(fb, color);
|
|
|
|
fb += color_size;
|
|
block += color_size;
|
|
}
|
|
|
|
fb += xlate;
|
|
}
|
|
}
|
|
|
|
static void cursor_leave(void)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
draw_block(rd->cursor, &color_hook_invert);
|
|
}
|
|
|
|
static void cursor_update(void)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
draw_block(rd->cursor, &color_hook_invert);
|
|
|
|
rt_device_control(rd->fbdev, FBIO_WAITFORVSYNC, RT_NULL);
|
|
}
|
|
|
|
void render_select_cursor(enum cursor shape)
|
|
{
|
|
void *cursor;
|
|
rt_size_t color_size;
|
|
int font_width, font_height;
|
|
rt_uint32_t foreground, background;
|
|
struct render *rd = &_render;
|
|
typeof(rd->set_pixel) set_pixel_handler = rd->set_pixel;
|
|
|
|
cursor = rd->cursor;
|
|
font_width = rd->psf.width;
|
|
font_height = rd->psf.height;
|
|
foreground = rd->foreground;
|
|
background = rd->background;
|
|
color_size = rd->color_size;
|
|
|
|
switch (shape)
|
|
{
|
|
case CURSOR_HLINE:
|
|
for (int y = 0; y < font_height; ++y)
|
|
{
|
|
for (int x = 0; x < font_width; ++x)
|
|
{
|
|
if (y + 1 < font_height)
|
|
{
|
|
set_pixel_handler(cursor, background);
|
|
}
|
|
else
|
|
{
|
|
set_pixel_handler(cursor, foreground);
|
|
}
|
|
|
|
cursor += color_size;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CURSOR_VLINE:
|
|
for (int i = 0; i < font_height; ++i)
|
|
{
|
|
set_pixel_handler(cursor, foreground);
|
|
cursor += color_size;
|
|
|
|
for (int x = 1; x < font_width; ++x)
|
|
{
|
|
set_pixel_handler(cursor, background);
|
|
|
|
cursor += color_size;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CURSOR_BLOCK:
|
|
for (int y = 0; y < font_height; ++y)
|
|
{
|
|
for (int x = 0; x < font_width; ++x)
|
|
{
|
|
set_pixel_handler(cursor, foreground);
|
|
|
|
cursor += color_size;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
cursor_update();
|
|
}
|
|
|
|
void render_move_cursor(struct render_point *position)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
if (position)
|
|
{
|
|
cursor_leave();
|
|
|
|
if (position->row > rd->end_point.row || position->col > rd->end_point.col)
|
|
{
|
|
return;
|
|
}
|
|
|
|
rt_memcpy(&rd->position, position, sizeof(rd->position));
|
|
}
|
|
else
|
|
{
|
|
rt_memset(&rd->position, 0, sizeof(rd->position));
|
|
}
|
|
|
|
cursor_update();
|
|
}
|
|
|
|
void render_reset_cursor(struct render_point *out_position)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
cursor_leave();
|
|
|
|
rd->position.col = rd->start_point.col;
|
|
|
|
cursor_update();
|
|
render_current_cursor(out_position);
|
|
}
|
|
|
|
void render_return_cursor(struct render_point *out_position)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
cursor_leave();
|
|
|
|
rd->position.col = rd->start_point.col;
|
|
|
|
if (rd->position.row >= rd->end_point.row)
|
|
{
|
|
roll_display();
|
|
}
|
|
else
|
|
{
|
|
++rd->position.row;
|
|
}
|
|
|
|
cursor_update();
|
|
render_current_cursor(out_position);
|
|
}
|
|
|
|
void render_current_cursor(struct render_point *out_position)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
if (out_position)
|
|
{
|
|
rt_memcpy(out_position, &rd->position, sizeof(rd->position));
|
|
}
|
|
}
|
|
|
|
void render_put_char(char ch)
|
|
{
|
|
struct render *rd = &_render;
|
|
|
|
draw_block((void *)rd->psf.font_data + ch * rd->font_size, rd->hook_ptr);
|
|
|
|
++rd->position.col;
|
|
|
|
if (rd->position.col > rd->end_point.col)
|
|
{
|
|
rd->position.col = rd->start_point.col;
|
|
|
|
if (rd->position.row >= rd->end_point.row)
|
|
{
|
|
roll_display();
|
|
}
|
|
else
|
|
{
|
|
++rd->position.row;
|
|
}
|
|
}
|
|
|
|
cursor_update();
|
|
}
|