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 }