| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- /*
- * 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();
- }
|