drivers/video/fb.c: Add startup splashscreen option

Adds Kconfig-selected splashscreen options used when the driver is first registered

* Includes a new Python script in ./tools to create RLE bitmap files
* Includes default NS logo btimaps in 320x320, 160x160 and 80x80 resolutions along with their PNG files

Signed-off-by: Tim Hardisty  timh@jti.uk.com>
This commit is contained in:
Tim Hardisty 2025-04-15 19:48:54 +01:00 committed by Alan C. Assis
parent b333ad3ab5
commit ed0c18c66c
14 changed files with 614428 additions and 69 deletions

View File

@ -56,6 +56,102 @@ config VIDEO_FB_NPOLLWAITERS
depends on VIDEO_FB
default 2
config VIDEO_FB_SPLASHSCREEN
bool "Enable Splashscreen when Framebuffer Driver is registered"
depends on VIDEO_FB
default n
---help---
This feature allows a splashscreen image to be displayed during the
Framebuffer device driver register function. It provides the following
functionality:
- Choice of 80x80, 160x160 or 320x320 default "NX" logos
- Alternative "out-of-tree" image as splashscreen
- Configurable background colour (default black)
- Bit-per-pixel choice (32/24/16/8/Greyscale/Mono) to suit the LCD used
- Splashscreen can be set to remain displayed for 'n' seconds ('n' can be 0)
- Splashscreen can be cleared (to black) once framebuffer device register is
complete
- Python "splashscreen_converter.py" tool (in NuttX tools directory) can be used
to create custom splashscreens - note the tool restricts the number of colours
to 256.
if VIDEO_FB_SPLASHSCREEN
menu "Splashscreen Image Selection and Configuration"
choice
prompt "Select Splashscreen Image Source"
default VIDEO_FB_SPLASHSCREEN_NXLOGO
config VIDEO_FB_SPLASHSCREEN_NXLOGO
bool "Use default NuttX NX Logo"
config VIDEO_FB_SPLASHSCREEN_CUSTOM
bool "Use Custom file as splashscreen"
---help---
This must be a compiled C source file, such as fb_splash.o, created
as a c src file using the splashscreen_converter.py Python script,
available in the nuttx/tools directory, and compiled as part of the
build.
Typically this would be a source file of an out-of-tree custom board.
endchoice
choice
prompt "Select NXlogo bitmap size to use"
default VIDEO_FB_SPLASHSCREEN_NXLOGO_160
depends on VIDEO_FB_SPLASHSCREEN_NXLOGO
config VIDEO_FB_SPLASHSCREEN_NXLOGO_320
bool "320x320 pixels"
config VIDEO_FB_SPLASHSCREEN_NXLOGO_160
bool "160x160 pixels"
config VIDEO_FB_SPLASHSCREEN_NXLOGO_80
bool "80x80 pixels"
endchoice
choice
prompt "Select Splashscreen Bits-per-pixel (BPP)"
default VIDEO_FB_SPLASHSCREEN_BPP32
config VIDEO_FB_SPLASHSCREEN_BPP32
bool "32BPP (ARGB)"
config VIDEO_FB_SPLASHSCREEN_BPP24
bool "24BPP (RGB)"
config VIDEO_FB_SPLASHSCREEN_BPP16
bool "16BPP (RGB565)"
config VIDEO_FB_SPLASHSCREEN_MONO
bool "Monochrome"
config VIDEO_FB_SPLASHSCREEN_GREY
bool "Greyscale (8BPP)"
endchoice
config VIDEO_FB_SPLASHSCREEN_BG_COLOUR
hex "Hex (A)RGB background colour for splashscreen"
default 0
---help---
default is black
config VIDEO_FB_SPLASHSCREEN_DISP_TIME
int "Time to sleep once Splashscreen displayed"
default 1
config VIDEO_FB_SPLASHSCREEN_CLR_ON_EXIT
bool "Clear Framebuffer memory when driver registration is complete"
default y
endmenu # "Splashscreen Image Selection and Configuration"
endif # VIDEO_FB_SPLASHSCREEN
config VIDEO_STREAM
bool "Video Stream Support"
default n

View File

@ -32,6 +32,18 @@ ifeq ($(CONFIG_VIDEO_STREAM),y)
CSRCS += v4l2_core.c video_framebuff.c v4l2_cap.c v4l2_m2m.c
endif
ifeq ($(CONFIG_VIDEO_FB_SPLASHSCREEN),y)
ifeq ($(CONFIG_VIDEO_FB_SPLASHSCREEN_NXLOGO),y)
ifeq ($(CONFIG_VIDEO_FB_SPLASHSCREEN_NXLOGO_320),y)
CSRCS += nxlogo320.c
else ifeq ($(CONFIG_VIDEO_FB_SPLASHSCREEN_NXLOGO_160),y)
CSRCS += nxlogo160.c
else
CSRCS += nxlogo80.c
endif
endif
endif
# These video drivers depend on I2C support
ifeq ($(CONFIG_I2C),y)

View File

@ -45,11 +45,34 @@
#include <nuttx/clock.h>
#include <nuttx/wdog.h>
#include <nuttx/circbuf.h>
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
# include <nuttx/signal.h>
#endif
/****************************************************************************
* Pre-processor definitions
****************************************************************************/
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
# define SPLASH_BGCOL CONFIG_VIDEO_FB_SPLASHSCREEN_BG_COLOUR
# define SPLASH_SLEEP CONFIG_VIDEO_FB_SPLASHSCREEN_DISP_TIME
#
# if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32)
# define SPLASHSCREEN_FMT FB_FMT_RGBA32
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24)
# define SPLASHSCREEN_FMT FB_FMT_RGB24
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16)
# define SPLASHSCREEN_FMT FB_FMT_RGB16_565
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8)
# define SPLASHSCREEN_FMT FB_FMT_RGB8_332
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY)
# define SPLASHSCREEN_FMT FB_FMT_GREY
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO)
# define SPLASHSCREEN_FMT FB_FMT_MONO
# endif /* CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32 */
#
#endif /* CONFIG_VIDEO_FB_SPLASHSCREEN */
/****************************************************************************
* Private Types
****************************************************************************/
@ -73,10 +96,8 @@ struct fb_priv_s
struct fb_paninfo_s
{
FAR struct circbuf_s buf; /* Pan buffer queued list */
struct wdog_s wdog; /* VSync offset timer */
FAR struct circbuf_s buf; /* Pan buffer queued list */
struct wdog_s wdog; /* VSync offset timer */
FAR struct fb_chardev_s *dev;
};
@ -88,20 +109,20 @@ struct fb_paninfo_s
struct fb_chardev_s
{
FAR struct fb_vtable_s *vtable; /* Framebuffer interface */
uint8_t plane; /* Video plan number */
clock_t vsyncoffset; /* VSync offset ticks */
FAR struct fb_priv_s *head;
FAR struct fb_paninfo_s *paninfo; /* Pan info array */
size_t paninfo_count; /* Pan info count */
FAR struct fb_vtable_s *vtable; /* Framebuffer interface */
uint8_t plane; /* Video plan number */
clock_t vsyncoffset; /* VSync offset ticks */
FAR struct fb_priv_s *head;
FAR struct fb_paninfo_s *paninfo; /* Pan info array */
size_t paninfo_count; /* Pan info count */
};
struct fb_panelinfo_s
{
FAR void *fbmem; /* Start of frame buffer memory */
size_t fblen; /* Size of the framebuffer */
uint8_t fbcount; /* Count of frame buffer */
uint8_t bpp; /* Bits per pixel */
FAR void *fbmem; /* Start of frame buffer memory */
size_t fblen; /* Size of the framebuffer */
uint8_t fbcount; /* Count of frame buffer */
uint8_t bpp; /* Bits per pixel */
};
/****************************************************************************
@ -146,6 +167,14 @@ static int fb_munmap(FAR struct task_group_s *group,
FAR void *start, size_t length);
#endif
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
static int fb_splashscreen(FAR struct fb_videoinfo_s *vinfo,
FAR struct fb_planeinfo_s *pinfo);
static int fb_splash_fill(FAR struct fb_videoinfo_s *vinfo,
FAR struct fb_planeinfo_s *pinfo,
uint32_t colour);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
@ -163,10 +192,138 @@ static const struct file_operations g_fb_fops =
fb_poll /* poll */
};
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
extern const struct palette_bitmap_s g_splscr;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
static int fb_splash_fill(FAR struct fb_videoinfo_s *vinfo,
FAR struct fb_planeinfo_s *pinfo,
uint32_t colour)
{
FAR uint8_t *row;
int rgb_colour;
int y;
#if !defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO) && \
!defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY) && \
!defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32)
const int r = (colour >> 16) & 0xff;
const int g = (colour >> 8) & 0xff;
const int b = (colour) & 0xff;
#endif
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32
rgb_colour = colour;
#elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24)
rgb_colour = MKRGB(r, g, b);
#elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16)
rgb_colour = MKRGB(r, g, b);
#elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8)
rgb_colour = MKRGB(r, g, b);
#elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO) || \
defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY)
rgb_colour = colour & 0xff; /* No conversion needed */
#endif
row = (FAR uint8_t *)pinfo->fbmem;
for (y = 0; y < (vinfo->yres - 1); y++)
{
memset(row, rgb_colour, pinfo->stride);
row += pinfo->stride;
}
return OK;
}
static int fb_splashscreen(FAR struct fb_videoinfo_s *vinfo,
FAR struct fb_planeinfo_s *pinfo)
{
FAR const struct splscr_bitmap_s *record = &g_splscr.data[0];
FAR uint8_t *dst;
unsigned int row;
unsigned int nrun;
FAR fb_pixel_t *buf;
FAR fb_pixel_t colour;
int ret = OK;
DEBUGASSERT(SPLASHSCREEN_FMT == vinfo->fmt);
if (SPLASHSCREEN_FMT != vinfo->fmt)
{
gwarn("WARNING: Splashscreen format (%d) doesn't match LCD (%d)\n",
SPLASHSCREEN_FMT, vinfo->fmt);
}
DEBUGASSERT(g_splscr.width <= vinfo->xres);
if (g_splscr.width > vinfo->xres)
{
gwarn("Splashscreen width %d wider than the display width: %d\n",
(int)g_splscr.width, vinfo->xres);
return -EINVAL;
}
DEBUGASSERT(g_splscr.height <= vinfo->yres);
if (g_splscr.height > vinfo->yres)
{
gerr("ERROR: Splashscreen height %d taller than the display: %d\n",
(int)g_splscr.height, vinfo->yres);
return -EINVAL;
}
buf = kmm_malloc(sizeof(fb_pixel_t) * g_splscr.width);
if (buf == NULL)
{
gerr("ERROR: Failed to allocate memory for splashsceeb buffer\n");
return -ENOMEM;
}
/* Centre the image and set destination to start of area to be used */
dst = pinfo->fbmem + (pinfo->stride *
((vinfo->yres - g_splscr.height) / 2)) +
(((vinfo->xres - g_splscr.width) / 2) *
sizeof(fb_pixel_t));
/* Now output the rows */
for (row = 0; row < g_splscr.height; row++)
{
unsigned int width;
FAR fb_pixel_t *bufp = buf; /* Start address of the buffer */
/* Process each run-length encoded pixel in the image */
for (width = 0; width < g_splscr.width; record++)
{
nrun = (unsigned int)record->npixels; /* num pixels of the colour */
colour = g_splscr.lut[record->lookup];
width += nrun;
while (nrun-- > 0)
{
*bufp = colour; /* fill with the colour */
bufp++;
}
}
DEBUGASSERT(width == g_splscr.width);
gerr("ERROR: Splashscreen file RLE line length doe not match LCD\n");
/* copy the decoded/expanded pixel data for this row to framebuffer */
memcpy(dst, buf, sizeof(fb_pixel_t) * g_splscr.width);
/* Increment the vertical position */
dst += pinfo->stride;
}
return ret;
}
#endif /* CONFIG_VIDEO_FB_SPLASHSCREEN */
/****************************************************************************
* Name: fb_get_panbuf
****************************************************************************/
@ -316,13 +473,13 @@ err_fb:
static int fb_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s *priv;
FAR struct fb_priv_s *curr;
FAR struct fb_priv_s *prev;
irqstate_t flags;
int ret;
FAR struct fb_priv_s *priv;
FAR struct fb_priv_s *curr;
FAR struct fb_priv_s *prev;
irqstate_t flags;
int ret;
inode = filep->f_inode;
fb = inode->i_private;
@ -383,14 +540,14 @@ static int fb_close(FAR struct file *filep)
static ssize_t fb_read(FAR struct file *filep, FAR char *buffer, size_t len)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
size_t start;
size_t end;
size_t size;
int ret;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
size_t start;
size_t end;
size_t size;
int ret;
ginfo("len: %u\n", (unsigned int)len);
@ -441,14 +598,14 @@ static ssize_t fb_read(FAR struct file *filep, FAR char *buffer, size_t len)
static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer,
size_t len)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
size_t start;
size_t end;
size_t size;
int ret;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
size_t start;
size_t end;
size_t size;
int ret;
ginfo("len: %u\n", (unsigned int)len);
@ -504,12 +661,12 @@ static ssize_t fb_write(FAR struct file *filep, FAR const char *buffer,
static off_t fb_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
off_t newpos;
int ret;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
off_t newpos;
int ret;
ginfo("offset: %u whence: %d\n", (unsigned int)offset, whence);
@ -589,9 +746,9 @@ static off_t fb_seek(FAR struct file *filep, off_t offset, int whence)
static int fb_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
int ret = OK;
int ret = OK;
ginfo("cmd: %d arg: %ld\n", cmd, arg);
@ -1183,11 +1340,11 @@ static int fb_munmap(FAR struct task_group_s *group,
static int fb_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
int ret;
FAR struct fb_priv_s *priv;
struct fb_panelinfo_s panelinfo;
int ret;
/* Get the framebuffer instance */
@ -1236,14 +1393,14 @@ static int fb_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map)
static int fb_poll(FAR struct file *filep, struct pollfd *fds, bool setup)
{
FAR struct inode *inode;
FAR struct inode *inode;
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s *priv;
FAR struct circbuf_s *panbuf;
FAR struct pollfd **pollfds = NULL;
irqstate_t flags;
int ret = OK;
int i;
FAR struct fb_priv_s *priv;
FAR struct circbuf_s *panbuf;
FAR struct pollfd **pollfds = NULL;
irqstate_t flags;
int ret = OK;
int i;
/* Get the framebuffer instance */
@ -1525,7 +1682,7 @@ static void fb_pollnotify(FAR struct fb_chardev_s *fb, int overlay)
void fb_notify_vsync(FAR struct fb_vtable_s *vtable)
{
FAR struct fb_chardev_s *fb;
FAR struct fb_priv_s * priv;
FAR struct fb_priv_s *priv;
irqstate_t flags;
fb = vtable->priv;
@ -1562,10 +1719,10 @@ int fb_peek_paninfo(FAR struct fb_vtable_s *vtable,
FAR union fb_paninfo_u *info,
int overlay)
{
FAR struct circbuf_s *panbuf;
FAR struct circbuf_s *panbuf;
FAR struct fb_chardev_s *fb;
irqstate_t flags;
ssize_t ret;
irqstate_t flags;
ssize_t ret;
/* Prevent calling before getting the vtable. */
@ -1610,11 +1767,11 @@ int fb_peek_paninfo(FAR struct fb_vtable_s *vtable,
int fb_remove_paninfo(FAR struct fb_vtable_s *vtable, int overlay)
{
FAR struct circbuf_s *panbuf;
FAR struct circbuf_s *panbuf;
FAR struct fb_chardev_s *fb;
irqstate_t flags;
ssize_t ret;
bool full;
irqstate_t flags;
ssize_t ret;
bool full;
fb = vtable->priv;
if (fb == NULL)
@ -1671,10 +1828,10 @@ int fb_remove_paninfo(FAR struct fb_vtable_s *vtable, int overlay)
int fb_paninfo_count(FAR struct fb_vtable_s *vtable, int overlay)
{
FAR struct circbuf_s *panbuf;
FAR struct circbuf_s *panbuf;
FAR struct fb_chardev_s *fb;
irqstate_t flags;
ssize_t ret;
irqstate_t flags;
ssize_t ret;
/* Prevent calling before getting the vtable. */
@ -1727,12 +1884,15 @@ int fb_register_device(int display, int plane,
FAR struct fb_vtable_s *vtable)
{
FAR struct fb_chardev_s *fb;
struct fb_panelinfo_s panelinfo;
struct fb_videoinfo_s vinfo;
char devname[16];
int nplanes;
int ret;
ssize_t i;
struct fb_panelinfo_s panelinfo;
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
struct fb_planeinfo_s pinfo;
#endif
struct fb_videoinfo_s vinfo;
char devname[16];
int nplanes;
int ret;
ssize_t i;
/* Allocate a framebuffer state instance */
@ -1814,6 +1974,39 @@ int fb_register_device(int display, int plane,
goto errout_with_paninfo;
}
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
ret = fb_get_planeinfo(fb, &pinfo, 0);
if (ret < 0)
{
goto errout_with_paninfo;
}
ret = fb_splash_fill(&vinfo, &pinfo, SPLASH_BGCOL);
if (ret < 0)
{
goto errout_with_paninfo;
}
ret = fb_splashscreen(&vinfo, &pinfo);
if (ret < 0)
{
goto errout_with_paninfo;
}
if (SPLASH_SLEEP != 0)
{
nxsig_sleep(SPLASH_SLEEP);
}
# ifdef VIDEO_FB_SPLASHSCREEN_CLR_ON_EXIT
ret = fb_splash_fill(&vinfo, &pinfo, 0); /* Fill with black to clear LCD */
if (ret < 0)
{
goto errout_with_paninfo;
}
# endif
#endif
vtable->priv = fb;
return OK;

532740
drivers/video/new_logo.c Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

27636
drivers/video/nxlogo160.c Normal file

File diff suppressed because it is too large Load Diff

BIN
drivers/video/nxlogo160.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

41913
drivers/video/nxlogo320.c Normal file

File diff suppressed because it is too large Load Diff

BIN
drivers/video/nxlogo320.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

11439
drivers/video/nxlogo80.c Normal file

File diff suppressed because it is too large Load Diff

BIN
drivers/video/nxlogo80.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -33,6 +33,9 @@
#include <stdint.h>
#include <errno.h>
#include <debug.h>
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
# include <nuttx/video/rgbcolors.h>
#endif
#include <nuttx/compiler.h>
#include <nuttx/fs/ioctl.h>
@ -484,6 +487,18 @@
#define FB_ROTATE_UD 2
#define FB_ROTATE_CCW 3
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
# if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32)
# define MKRGB ARGBTO32
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24)
# define MKRGB RGBTO24
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16)
# define MKRGB RGBTO16
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8)
# define MKRGB RGBTO8
# endif /* CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32 */
#endif /* CONFIG_VIDEO_FB_SPLASHSCREEN */
/****************************************************************************
* Public Types
****************************************************************************/
@ -933,6 +948,53 @@ struct fb_var_screeninfo
uint32_t reserved[4]; /* Reserved for future compatibility */
};
#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN
# if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8) || \
defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO) || \
defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY)
typedef uint8_t fb_pixel_t;
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16)
typedef uint16_t fb_pixel_t;
#elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24)
typedef uint32_t fb_pixel_t;
# elif defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32)
typedef uint32_t fb_pixel_t;
# else
# error "Pixel depth is unknown"
#endif
/* Describes a point on the display */
struct fb_point_s
{
fb_coord_t x; /* X position, range: 0 to screen width - 1 */
fb_coord_t y; /* Y position, range: 0 to screen height - 1 */
};
struct fb_rect_s
{
struct fb_point_s pt1; /* Upper, left-hand corner */
struct fb_point_s pt2; /* Lower, right-hand corner */
};
/* This structure describes the splashscreen */
struct splscr_bitmap_s
{
uint8_t npixels; /* Number of pixels */
uint8_t lookup; /* Pixel RGB lookup index */
};
struct palette_bitmap_s
{
fb_coord_t width; /* Width in pixels */
fb_coord_t height; /* Height in rows */
FAR const fb_pixel_t *lut; /* Pointer to the palette (LUT) */
FAR const struct
splscr_bitmap_s *data; /* The RLE data */
};
#endif
/****************************************************************************
* Public Data
****************************************************************************/

View File

@ -33,6 +33,18 @@
/* Color Creation and Conversion Macros *************************************/
/* This macro creates RGB24 from 8:8:8:8 RGB */
#define ARGBTO32(a,r,g,b) \
((uint32_t)((a) & 0xff) << 24 | (uint32_t)((r) & 0xff) << 16 | (uint32_t)((g) & 0xff) << 8 | (uint32_t)((b) & 0xff))
/* And these macros perform the inverse transformation */
#define RGB32ALPHA(argb) (((argb) >> 24) & 0xff)
#define RGB32RED(argb) (((argb) >> 16) & 0xff)
#define RGB32GREEN(argb) (((argb) >> 8) & 0xff)
#define RGB32BLUE(argb) ( (argb) & 0xff)
/* This macro creates RGB24 from 8:8:8 RGB */
#define RGBTO24(r,g,b) \

256
tools/splashscreen_converter.py Executable file
View File

@ -0,0 +1,256 @@
#!/usr/bin/env python3
# tools/splashscreen_converter.py
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""This script converts from any image type supported by
Python imaging library to the RLE-encoded format used by
the framebuffer splashscreen feature.
"""
from PIL import Image
def get_palette(img, maxcolors=256):
"""Returns a list of colours. If there are too many colours in the image,
the least used are removed.
"""
colors = img.getcolors(65536)
colors.sort(key=lambda c: -c[0])
return [c[1] for c in colors[:maxcolors]]
def write_palette(outfile, palette, type):
"""Write the palette to the output file."""
if type == "ARGB":
outfile.write("#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN_BPP32\n")
outfile.write("static const fb_pixel_t palette[] =\n")
outfile.write("{\n")
for i in range(0, len(palette), 4):
for r, g, b, a in palette[i : i + 4]:
outfile.write(" MKRGB(%d, %d, %d, %d),\n" % (a, r, g, b))
outfile.write("};\n")
outfile.write("#endif\n\n")
elif type == "RGB":
outfile.write("#if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24) || \\\n")
outfile.write(" defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16) || \\\n")
outfile.write(" defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP8)\n")
outfile.write("static const fb_pixel_t palette[] =\n")
outfile.write("{\n")
for i in range(0, len(palette), 4):
for r, g, b in palette[i : i + 4]:
outfile.write(" MKRGB(%d, %d, %d),\n" % (r, g, b))
outfile.write("};\n")
outfile.write("#endif\n\n")
else:
outfile.write("#if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_GREY) || \\\n")
outfile.write(" defined(CONFIG_VIDEO_FB_SPLASHSCREEN_MONO)\n")
outfile.write("static const fb_pixel_t palette[] =\n{\n};\n")
outfile.write("#endif\n\n")
def quantize(color, palette):
"""Return the color index to closest match in the palette."""
try:
return palette.index(color)
except ValueError:
# No exact match, search for the closest
def distance(color2):
return sum([(a - b) ** 2 for a, b in zip(color, color2)])
return palette.index(min(palette, key=distance))
def encode_row(img, palette, y, type):
"""RLE-encode one row of image data."""
color = None
entries = []
repeats = 0
for x in range(0, img.width):
if type == "BPP32" or type == "BPP24" or type == "BPP16":
c = quantize(img.getpixel((x, y)), palette)
else:
c = img.getpixel((x, y))
if c == color and repeats < 255:
repeats += 1
else:
if color is not None:
entries.append((repeats, color))
repeats = 1
color = c
if color is not None:
entries.append((repeats, color))
return entries
def write_image(outfile, img, palette, suffix):
"""Write the image contents to the output file."""
if suffix == "BPP24" or suffix == "BPP16":
outfile.write("#if defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP24) || \\")
outfile.write("\n defined(CONFIG_VIDEO_FB_SPLASHSCREEN_BPP16)\n")
else:
outfile.write("#ifdef CONFIG_VIDEO_FB_SPLASHSCREEN_%s\n" % suffix)
outfile.write("static const struct splscr_bitmap_s bitmap[] =\n")
outfile.write("{")
for y in range(0, img.height):
entries = encode_row(img, palette, y, suffix)
for r, c in entries:
outfile.write("\n")
row = " {%d, %d}," % (r, c)
outfile.write(row)
outfile.write(("/* End of row %3d */" % (y + 1)).rjust(78 - len(row), " "))
outfile.write("\n};\n")
outfile.write("#endif /* CONFIG_VIDEO_FB_SPLASHSCREEN_%s */\n\n" % suffix)
def write_descriptor(outfile, name):
outfile.write("const struct palette_bitmap_s g_%s =\n" % name)
outfile.write("{\n")
lw = len(str(img.width))
lh = len(str(img.height))
outfile.write(" %d," % img.width)
outfile.write(
("/* width in pixels */\n").rjust(
76 - lw, " "
)
)
outfile.write(" %d," % img.height)
outfile.write(
("/* height in pixels */\n").rjust(
76 - lh, " "
)
)
outfile.write(
(
" palette, /* Colour palette */\n"
).rjust(76, " ")
)
outfile.write(
(
" bitmap, /* Pointer to the start of the RLE data */\n"
).rjust(76, " ")
)
outfile.write("};\n")
if __name__ == "__main__":
import os.path
import sys
if len(sys.argv) < 3:
print("Usage: splashscreen_converter.py image.xxx output_directory out.c")
print(
"\t- image.xxx\t\tis the image file (e.g. logo.png) in a format supported by PIL"
)
print("\t- output_directory\tis where the output file will be saved.")
print("\t- out.c\t\t\tis the name of the output file.")
print("\t\t\t\t- If out.c is not specified it will default to fb_splash.c,")
print("\t\t\t\t which is the name required for custom splashscreens")
sys.exit(1)
elif len(sys.argv) == 3:
path = sys.argv[2]
filename = "fb_splash.c"
else:
path = os.path.relpath(sys.argv[2])
filename = sys.argv[3]
img = Image.open(sys.argv[1]).convert("RGBA")
file = os.path.realpath(path + "/" + filename)
outfile = open(file, "w")
palette_argb = get_palette(img)
palette_rgb = get_palette(img.convert("RGB"))
outfile.write(
"""/****************************************************************************
* %(file)s
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Script-generated framebuffer splashscreen bitmap file.
* Generated from %(src)s
* by splashscreen_converter.py
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/video/fb.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
"""
% {"file": path + "/" + filename, "src": os.path.relpath(sys.argv[1])}
)
name = "splscr"
write_palette(outfile, palette_argb, "ARGB")
write_palette(outfile, palette_rgb, "RGB")
write_palette(outfile, None, None)
write_image(outfile, img, palette_argb, "BPP32")
write_image(outfile, img.convert("RGB"), palette_rgb, "BPP24")
write_image(outfile, img.convert("L"), None, "GREY")
write_image(outfile, img.convert("1"), None, "MONO")
write_descriptor(outfile, name)
outfile.write(
"""
/****************************************************************************
* Private Functions
****************************************************************************/
"""
)
print("Created %s from %s" % (file, sys.argv[1]))