Skip to main content
Version: kirkstone_1-06-00

Kernel Logo and Splash-screen

When booting up a board with Clea OS two images are shown:

  • the Linux Kernel logo.
  • the Splash-screen (handled by PSplash)

Both of these images cannot be changed at runtime as they need to be compiled into binaries that are executed at boot time.

The logo shown at startup can be modified replacing the file drivers/video/logo/logo_linux_clut224.ppm with the desired image. The new logo must be a .ppm file and it must have maximum 224 colours and 3 bytes per pixel.

To process from the command line the .ppm image, the commands ppmquant and pnmnoraw are required. If they are missing install it with:

$ sudo apt install netpbm

Run the following commands to firstly reduce the numbers of colours to 224 and finally convert the file to ASCII:

$ ppmquant 224 <image name>.ppm > <image name>_224.ppm
$ pnmnoraw <image name>_224.ppm > logo_linux_clut224.ppm

To apply the new kernel logo:

  • create the folder recipes-kernel/linux/files and add the new logo_linux_clut244.ppm
  • create recipes-kernel/linux/linux-seco-imx_5.10.52.bbappend with the content shown below to apply the new logo:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += " \
file://logo_linux_clut224.ppm \
"

do_configure:prepend() {
# logo support, if you supply logo_linux_clut224.ppm in SRC_URI, then it's going to be used
if [ -e ${WORKDIR}/logo_linux_clut224.ppm ]; then
install -m 0644 ${WORKDIR}/logo_linux_clut224.ppm ${S}/drivers/video/logo/logo_linux_clut224.ppm
fi
}

Yocto build information

A possible usage of kernel's logo is to replace the image with the informations related to the Yocto build. An example for that could be the date, the version, the name of the release and so on. An example is the image shown here:

Yocto Build Information

An image like the one shown above can be created automatically at build time with a python script that creates the image with the build-time informations. The script used to create the image above is:

from PIL import Image, ImageDraw, ImageFont
import datetime
import argparse


def create_image(event, build_version, date, width, height, format, placement):
# Create a blank image with black background
background_color = (0, 0, 0)
image = Image.new('RGB', (width, height), color=background_color)
# Load a font. Set blue font color
font = ImageFont.load_default()
font_color = (255, 255, 255)
# Define text to display
text = f"{event}\nBuild Version: {build_version}\nDate: {date}"
# Calculate text size and position
draw = ImageDraw.Draw(image)
text_width, text_height = draw.textsize(text, font=font)
# Place the text according to the 'placement' argument
if ('bottom-right' == placement):
# Padding from the bottom-right edges
padding = 10
text_x = width - text_width - padding
text_y = height - text_height - padding
else:
text_x = (width - text_width) // 2
text_y = (height - text_height) // 2
# Add text to image
draw.multiline_text((text_x, text_y), text, fill=font_color, font=font)
# Save the image as a PPM file
output_file = "yocto_build." + format
image.save(output_file, format=format.upper())

print(f"{format.upper()} file '{output_file}' created successfully.")


def save_image_as_clut224_ascii_ppm(image_path, output_path):
original_image = Image.open(image_path)
# Convert the image to P mode with a palette of 224 colors
indexed_image = original_image.convert('P', palette=Image.ADAPTIVE, colors=224)
# Extract the palette (768 bytes: 256 * 3, but we'll use only 224 * 3)
palette = indexed_image.getpalette()[:224*3]
# Create the PPM header
width, height = indexed_image.size
ppm_header = f'P3\n{width} {height}\n255\n'
# Create the PPM pixel data
ppm_pixels = []
image_data = indexed_image.getdata()
for pixel in image_data:
r = palette[pixel * 3]
g = palette[pixel * 3 + 1]
b = palette[pixel * 3 + 2]
ppm_pixels.append(f'{r} {g} {b}')
# Combine header and pixel data. The final newl line is required.
ppm_content = ppm_header + '\n'.join(ppm_pixels) + '\n'
# Write the PPM file
with open(output_path, 'w') as f:
f.write(ppm_content)


# Argument parser to handle command line arguments
parser = argparse.ArgumentParser(description='Generate a PPM with Yocto build information.')
parser.add_argument('--event', type=str, required=True, help='Event name')
parser.add_argument('--build_version', type=str, required=True, help='Build version')
parser.add_argument('--placement', type=str, required=False, help='Center or bottom-right placement')
args = parser.parse_args()

# Build details
event = args.event
build_version = args.build_version
placement = args.placement
date = datetime.datetime.now().strftime("%Y-%m-%d")
width, height = 400, 100
input_image_path = 'yocto_build.ppm'
output_ppm_path = 'logo_linux_clut224.ppm'

create_image(event, build_version, date, width, height, 'png', placement)
create_image(event, build_version, date, width, height, 'ppm', placement)
save_image_as_clut224_ascii_ppm(input_image_path, output_ppm_path)

The script handles some strings as arguments and add the actual date to the image. It creates a .ppm image already formatted to be used as kernel's logo. It also creates a .png to be used for debug to see the result of the script without deploying the image onto a board. This script is called by the recipe recipe-custom/create-build-ppm.bb:

DESCRIPTION = "Recipe to create a build_ppm file with Yocto build information"
LICENSE = "CLOSED"
SRC_URI = "file://create_build_ppm.py"

inherit setuptools3

PIP_INSTALL_PACKAGE = "pillow"
PIP_INSTALL_DIST_PATH = "${S}/dist"

# Ensure Python3 is available in the target image
DEPENDS = "python3-native python3-pillow-native"
EVENT = "Yocto build test"
BUILD_VERSION = "Clea OS: kirkstone_1.03"
PLACEMENT = "center"

do_install() {
install -d ${D}${sysconfdir}
install -m 0644 ${B}/logo_linux_clut224.ppm ${D}${sysconfdir}/logo_linux_clut224.ppm
}

do_compile() {
# Run the python script to create the build information screen when booting.
# It creates also a png to view the outcome in recipe's artifacts.
python3 ${WORKDIR}/create_build_ppm.py --event "${EVENT}" --build_version "${BUILD_VERSION}" --placement "${PLACEMENT}"
}

# Ensure the file is staged to the sysroot
do_populate_sysroot() {
install -d ${SYSROOT_DESTDIR}${sysconfdir}
install -m 0644 ${D}${sysconfdir}/logo_linux_clut224.ppm ${SYSROOT_DESTDIR}${sysconfdir}/logo_linux_clut224.ppm
}

FILES_${PN} += "${sysconfdir}/logo_linux_clut224.ppm"

addtask cleansstate before do_fetch

The output of the script is placed in the kernel's sysroot ready be used by kernel's recipe. This recipe must be compiled everytime the image is compiled to have the correct date.

In order to the kernel to use the correct image, the file logo_linux_clut224.ppm must be available to the kernel's recipe before the task do_configure. For this reason the kernel's recipe must be modified adding the dependency, for example creating a recipes-kernel/linux/linux-seco-imx_5.10.52.bbappend:

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI:remove = "file://logo_linux_clut224.ppm"

DEPENDS += "create-build-ppm"
do_configure[depends] += "create-build-ppm:do_populate_sysroot"

do_configure:prepend() {
install -m 0644 ${STAGING_DIR_TARGET}${sysconfdir}/logo_linux_clut224.ppm ${S}/drivers/video/logo/logo_linux_clut224.ppm
}

addtask cleansstate before do_fetch

Splash-screen with PSplash

PSplash embeds the splash-image into the binary that is executed at boot time by its dedicated service. The image is not in a typical image format (such as .jpg, .png) but it is a C header file .h. To convert a .png file into the .h suitable for PSplash the package libgdk-pixbuf2.0-dev is required. It can be installed with:

$ sudo apt install libgdk-pixbuf2.0-dev

The .png file is converted with this script called make-image-header.sh:

#!/bin/sh
#
# SPDX-License-Identifier: GPL-2.0-or-later
#

set -e

imageh=`basename $1 .png`-img.h
name="${2}_IMG"
gdk-pixbuf-csource --macros $1 > $imageh.tmp
sed -e "s/MY_PIXBUF/${name}/g" -e "s/guint8/uint8/g" $imageh.tmp > $imageh && rm $imageh.tmp

to execute with:

$ ./make-image-header.sh psplash-custom-img.png POKY

The output of the script is a new file called psplash-custom-img.h suitable for PSplash.

To replace the splash-screen:

  • create in the target layer the directory recipes-core/psplash/ and add here the image psplash-custom-img.h
  • create in the target layer recipes-core/psplash/psplash_%.bbappend containing:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += " \
file://psplash-custom-img.h \
"

SPLASH_IMAGES = "file://psplash-custom-img.h;outsuffix=default"