home | blog | art | now | git gpg | email | rss

splashdown

Minimal bootsplash program
git clone https://pollux.codes/git/splashdown.git
Log | Files | Refs | README | LICENSE

splashdown.c (7484B)


      1 
      2 /*
      3  * splashdown - minimal bootsplash program
      4  * Copyright (C) 2026 Pollux <pollux@pollux.codes>
      5  * 
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 3 of the License, or
      9  * (at your option) any later version.
     10  * 
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  * 
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include <errno.h>
     21 #include <stdarg.h>
     22 #include <linux/fb.h>
     23 #include <stdlib.h>
     24 #include <stdint.h>
     25 #include <string.h>
     26 #include <sys/ioctl.h>
     27 #include <sys/mman.h>
     28 
     29 #include <stdio.h>
     30 
     31 #include <fcntl.h>
     32 #include <unistd.h>
     33 
     34 #include <jpeglib.h>
     35 
     36 #include <freetype/freetype.h>
     37 #include FT_FREETYPE_H
     38 
     39 #define LENGTH(X) (sizeof (X) / sizeof (X)[0])
     40 #define MIN(A, B) ((A) < (B) ? (A) : (B))
     41 
     42 struct image_buffer {
     43 	uint32_t        width;
     44 	uint32_t        height;
     45 	uint32_t       *data;
     46 };
     47 
     48 void            die(const char *fmt, ...);
     49 void            draw_buffer(struct image_buffer src, uint32_t srcx,
     50                             uint32_t srcy, uint32_t srcw, uint32_t srch,
     51                             struct image_buffer dest, uint32_t destx,
     52                             uint32_t desty);
     53 void            draw_glyph(struct image_buffer buffer, FT_Bitmap bitmap, int x,
     54                            int y, uint32_t color);
     55 void            draw_text(struct image_buffer buffer, int x, int y,
     56                           FT_Face face, uint32_t color, const char *text,
     57                           size_t len);
     58 int             mmap_fb(struct image_buffer *fb);
     59 int             read_jpeg(const char *path, struct image_buffer *buffer);
     60 
     61 #include "config.h"
     62 
     63 void
     64 die(const char *fmt, ...) {
     65 	va_list         ap;
     66 	int             saved_errno;
     67 
     68 	saved_errno = errno;
     69 
     70 	va_start(ap, fmt);
     71 	vfprintf(stderr, fmt, ap);
     72 	va_end(ap);
     73 
     74 	if(fmt[0] && fmt[strlen(fmt) - 1] == ':')
     75 		fprintf(stderr, " %s", strerror(saved_errno));
     76 	fputc('\n', stderr);
     77 	exit(1);
     78 }
     79 
     80 void
     81 draw_buffer(struct image_buffer src, uint32_t srcx, uint32_t srcy,
     82 	    uint32_t srcw, uint32_t srch, struct image_buffer dest,
     83 	    uint32_t destx, uint32_t desty) {
     84 
     85 	srcw = MIN(srcw, src.width);
     86 	srch = MIN(srch, src.height);
     87 
     88 	srcw = MIN(srcw, dest.width - destx);
     89 	srch = MIN(srch, dest.height - desty);
     90 
     91 	int             row_bytes = srcw * sizeof(uint32_t);
     92 
     93 	for(uint32_t y = 0; y < srch; y++) {
     94 		memcpy(dest.data + dest.width * (desty + y) + destx,
     95 		       src.data + src.width * (y + srcy) + srcx, row_bytes);
     96 	}
     97 }
     98 
     99 void
    100 draw_glyph(struct image_buffer buffer, FT_Bitmap bitmap, int x, int y,
    101 	   uint32_t color) {
    102 	for(uint32_t py = 0; py < bitmap.rows; py++) {
    103 		for(uint32_t px = 0; px < bitmap.width; px++) {
    104 			uint8_t         a =
    105 			        bitmap.buffer[bitmap.pitch * py + px];
    106 			uint32_t        bg =
    107 			        buffer.data[(y + py) * buffer.width + x + px];
    108 
    109 			uint16_t        r = bg >> 16 & 0xff;
    110 			uint16_t        g = bg >> 8 & 0xff;
    111 			uint16_t        b = bg >> 0 & 0xff;
    112 
    113 			uint16_t        r_t = color >> 16 & 0xff;
    114 			uint16_t        g_t = color >> 8 & 0xff;
    115 			uint16_t        b_t = color >> 0 & 0xff;
    116 
    117 			r = (r * (255 - a) + r_t * a) / 255;
    118 			g = (g * (255 - a) + g_t * a) / 255;
    119 			b = (b * (255 - a) + b_t * a) / 255;
    120 
    121 			buffer.data[(y + py) * buffer.width + x + px] =
    122 			        b | g << 8 | r << 16;
    123 		}
    124 	}
    125 }
    126 
    127 void
    128 draw_text(struct image_buffer buffer, int x, int y, FT_Face face,
    129 	  uint32_t color, const char *text, size_t len) {
    130 
    131 	int             e, pen_x = x;
    132 
    133 	for(uint32_t n = 0; n < len; n++) {
    134 		e = FT_Load_Char(face, text[n], FT_LOAD_RENDER);
    135 		if(e)
    136 			continue;
    137 
    138 		draw_glyph(buffer, face->glyph->bitmap,
    139 		           pen_x + face->glyph->bitmap_left,
    140 		           y - face->glyph->bitmap_top, color);
    141 
    142 		pen_x += face->glyph->advance.x >> 6;
    143 	}
    144 }
    145 
    146 int
    147 mmap_fb(struct image_buffer *fb) {
    148 	int             fd = open(FB_DEV, O_RDWR);
    149 	struct fb_var_screeninfo fb_info;
    150 	int             e = ioctl(fd, FBIOGET_VSCREENINFO, &fb_info);
    151 
    152 	if(e)
    153 		return -1;
    154 	fb->width = fb_info.xres;
    155 	fb->height = fb_info.yres;
    156 	uint32_t        fb_size = fb->width * fb->height * 4;
    157 
    158 	fb->data =
    159 	        mmap(NULL, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    160 	close(fd);
    161 	return 0;
    162 }
    163 
    164 int
    165 read_jpeg(const char *path, struct image_buffer *buffer) {
    166 	FILE           *fd;
    167 
    168 	fd = fopen(path, "rb");
    169 	if(fd == NULL)
    170 		return -1;
    171 
    172 	struct jpeg_error_mgr jerr;
    173 	struct jpeg_decompress_struct cinfo;
    174 
    175 	cinfo.err = jpeg_std_error(&jerr);
    176 
    177 	jpeg_create_decompress(&cinfo);
    178 	jpeg_stdio_src(&cinfo, fd);
    179 	if(jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
    180 		goto bail;
    181 	if(!jpeg_start_decompress(&cinfo))
    182 		goto bail;
    183 
    184 	buffer->width = cinfo.output_width;
    185 	buffer->height = cinfo.output_height;
    186 
    187 	int             pixels = buffer->width * buffer->height;
    188 
    189 	buffer->data = malloc(pixels * sizeof(uint32_t));
    190 
    191 	int             row_stride =
    192 	        cinfo.output_width * cinfo.output_components;
    193 
    194 	JSAMPARRAY      working_buffer =
    195 	        (cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
    196 	                                   row_stride, 1);
    197 
    198 	while(cinfo.output_scanline < cinfo.output_height) {
    199 		if(jpeg_read_scanlines(&cinfo, working_buffer, 1) != 1)
    200 			goto bail;
    201 		for(uint32_t x = 0; x < cinfo.output_width; x++)
    202 			buffer->data[buffer->width *
    203 		                     (cinfo.output_scanline - 1) + x] =
    204 		                (working_buffer[0][x * 3 + 2] & 0xff) |
    205 		                ((working_buffer[0][x * 3 + 1] & 0xff) << 8) |
    206 		                ((working_buffer[0][x * 3] & 0xff) << 16);
    207 	}
    208 
    209 	jpeg_finish_decompress(&cinfo);
    210 	jpeg_destroy_decompress(&cinfo);
    211 	return 0;
    212 
    213 	bail:
    214 	jpeg_destroy_decompress(&cinfo);
    215 	if(buffer->data)
    216 		free(buffer->data);
    217 	buffer->data = NULL;
    218 	return -1;
    219 }
    220 
    221 int
    222 main(const int argc, const char **argv) {
    223 
    224 	struct image_buffer splash_image = {.data = NULL };
    225 	struct image_buffer framebuffer = {.data = NULL };
    226 
    227 	const char     *err = NULL;
    228 
    229 	const char     *text = NULL;
    230 
    231 	if(argc > 1)
    232 		text = argv[1];
    233 
    234 	FT_Library      lib = NULL;
    235 	FT_Face         face = NULL;
    236 
    237 	if(FT_Init_FreeType(&lib))
    238 		die("Could not load freetype2.");
    239 	for(uint32_t i = 0; i < LENGTH(fonts); i++)
    240 		if(!FT_New_Face(lib, fonts[i], 0, &face))
    241 			break;
    242 	if(!face) {
    243 		err = "Could not locate font.";
    244 		goto cleanup;
    245 	}
    246 
    247 	if(FT_Set_Pixel_Sizes(face, 0, font_size)) {
    248 		err = "Could not set font properties.";
    249 		goto cleanup;
    250 	}
    251 
    252 	for(uint32_t i = 0; i < LENGTH(image_paths); i++)
    253 		if(read_jpeg(image_paths[i], &splash_image))
    254 			break;
    255 
    256 	if(!splash_image.data) {
    257 		err = "Could not open image.";
    258 		goto cleanup;
    259 	}
    260 
    261 	if(mmap_fb(&framebuffer)) {
    262 		err = "Could not open framebuffer.";
    263 		goto cleanup;
    264 	}
    265 
    266 	if(text)
    267 		draw_text(splash_image, margin_x, margin_y, face, font_color,
    268 	                  text, strlen(text));
    269 	draw_buffer(splash_image, 0, 0, splash_image.width, splash_image.height,
    270 	            framebuffer, 0, 0);
    271 
    272 	cleanup:
    273 	if(framebuffer.data)
    274 		munmap(framebuffer.data,
    275 	               framebuffer.width * framebuffer.height *
    276 	               sizeof(uint32_t));
    277 	if(splash_image.data)
    278 		free(splash_image.data);
    279 	if(face)
    280 		FT_Done_Face(face);
    281 	if(lib)
    282 		FT_Done_FreeType(lib);
    283 	if(err)
    284 		die(err);
    285 
    286 	return 0;
    287 }