1 /* 2 * Convert a logo in ASCII PNM format to C source suitable for inclusion in 3 * the Linux kernel 4 * 5 * (C) Copyright 2001-2003 by Geert Uytterhoeven <geert@linux-m68k.org> 6 * 7 * -------------------------------------------------------------------------- 8 * 9 * This file is subject to the terms and conditions of the GNU General Public 10 * License. See the file COPYING in the main directory of the Linux 11 * distribution for more details. 12 */ 13 14 #include <ctype.h> 15 #include <errno.h> 16 #include <stdarg.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 22 23 static const char *programname; 24 static const char *filename; 25 static const char *logoname = "linux_logo"; 26 static const char *outputname; 27 static FILE *out; 28 29 30 #define LINUX_LOGO_MONO 1 /* monochrome black/white */ 31 #define LINUX_LOGO_VGA16 2 /* 16 colors VGA text palette */ 32 #define LINUX_LOGO_CLUT224 3 /* 224 colors */ 33 #define LINUX_LOGO_GRAY256 4 /* 256 levels grayscale */ 34 35 static const char *logo_types[LINUX_LOGO_GRAY256+1] = { 36 [LINUX_LOGO_MONO] = "LINUX_LOGO_MONO", 37 [LINUX_LOGO_VGA16] = "LINUX_LOGO_VGA16", 38 [LINUX_LOGO_CLUT224] = "LINUX_LOGO_CLUT224", 39 [LINUX_LOGO_GRAY256] = "LINUX_LOGO_GRAY256" 40 }; 41 42 #define MAX_LINUX_LOGO_COLORS 224 43 44 struct color { 45 unsigned char red; 46 unsigned char green; 47 unsigned char blue; 48 }; 49 50 static const struct color clut_vga16[16] = { 51 { 0x00, 0x00, 0x00 }, 52 { 0x00, 0x00, 0xaa }, 53 { 0x00, 0xaa, 0x00 }, 54 { 0x00, 0xaa, 0xaa }, 55 { 0xaa, 0x00, 0x00 }, 56 { 0xaa, 0x00, 0xaa }, 57 { 0xaa, 0x55, 0x00 }, 58 { 0xaa, 0xaa, 0xaa }, 59 { 0x55, 0x55, 0x55 }, 60 { 0x55, 0x55, 0xff }, 61 { 0x55, 0xff, 0x55 }, 62 { 0x55, 0xff, 0xff }, 63 { 0xff, 0x55, 0x55 }, 64 { 0xff, 0x55, 0xff }, 65 { 0xff, 0xff, 0x55 }, 66 { 0xff, 0xff, 0xff }, 67 }; 68 69 70 static int logo_type = LINUX_LOGO_CLUT224; 71 static unsigned int logo_width; 72 static unsigned int logo_height; 73 static struct color **logo_data; 74 static struct color logo_clut[MAX_LINUX_LOGO_COLORS]; 75 static unsigned int logo_clutsize; 76 static int is_plain_pbm = 0; 77 78 static void die(const char *fmt, ...) 79 __attribute__((noreturn)) __attribute((format (printf, 1, 2))); 80 static void usage(void) __attribute((noreturn)); 81 82 83 static unsigned int get_number(FILE *fp) 84 { 85 int c, val; 86 87 /* Skip leading whitespace */ 88 do { 89 c = fgetc(fp); 90 if (c == EOF) 91 die("%s: end of file\n", filename); 92 if (c == '#') { 93 /* Ignore comments 'till end of line */ 94 do { 95 c = fgetc(fp); 96 if (c == EOF) 97 die("%s: end of file\n", filename); 98 } while (c != '\n'); 99 } 100 } while (isspace(c)); 101 102 /* Parse decimal number */ 103 val = 0; 104 while (isdigit(c)) { 105 val = 10*val+c-'0'; 106 /* some PBM are 'broken'; GiMP for example exports a PBM without space 107 * between the digits. This is Ok cause we know a PBM can only have a '1' 108 * or a '0' for the digit. 109 */ 110 if (is_plain_pbm) 111 break; 112 c = fgetc(fp); 113 if (c == EOF) 114 die("%s: end of file\n", filename); 115 } 116 return val; 117 } 118 119 static unsigned int get_number255(FILE *fp, unsigned int maxval) 120 { 121 unsigned int val = get_number(fp); 122 123 return (255*val+maxval/2)/maxval; 124 } 125 126 static void read_image(void) 127 { 128 FILE *fp; 129 unsigned int i, j; 130 int magic; 131 unsigned int maxval; 132 133 /* open image file */ 134 fp = fopen(filename, "r"); 135 if (!fp) 136 die("Cannot open file %s: %s\n", filename, strerror(errno)); 137 138 /* check file type and read file header */ 139 magic = fgetc(fp); 140 if (magic != 'P') 141 die("%s is not a PNM file\n", filename); 142 magic = fgetc(fp); 143 switch (magic) { 144 case '1': 145 case '2': 146 case '3': 147 /* Plain PBM/PGM/PPM */ 148 break; 149 150 case '4': 151 case '5': 152 case '6': 153 /* Binary PBM/PGM/PPM */ 154 die("%s: Binary PNM is not supported\n" 155 "Use pnmnoraw(1) to convert it to ASCII PNM\n", filename); 156 157 default: 158 die("%s is not a PNM file\n", filename); 159 } 160 logo_width = get_number(fp); 161 logo_height = get_number(fp); 162 163 /* allocate image data */ 164 logo_data = (struct color **)malloc(logo_height*sizeof(struct color *)); 165 if (!logo_data) 166 die("%s\n", strerror(errno)); 167 for (i = 0; i < logo_height; i++) { 168 logo_data[i] = malloc(logo_width*sizeof(struct color)); 169 if (!logo_data[i]) 170 die("%s\n", strerror(errno)); 171 } 172 173 /* read image data */ 174 switch (magic) { 175 case '1': 176 /* Plain PBM */ 177 is_plain_pbm = 1; 178 for (i = 0; i < logo_height; i++) 179 for (j = 0; j < logo_width; j++) 180 logo_data[i][j].red = logo_data[i][j].green = 181 logo_data[i][j].blue = 255*(1-get_number(fp)); 182 break; 183 184 case '2': 185 /* Plain PGM */ 186 maxval = get_number(fp); 187 for (i = 0; i < logo_height; i++) 188 for (j = 0; j < logo_width; j++) 189 logo_data[i][j].red = logo_data[i][j].green = 190 logo_data[i][j].blue = get_number255(fp, maxval); 191 break; 192 193 case '3': 194 /* Plain PPM */ 195 maxval = get_number(fp); 196 for (i = 0; i < logo_height; i++) 197 for (j = 0; j < logo_width; j++) { 198 logo_data[i][j].red = get_number255(fp, maxval); 199 logo_data[i][j].green = get_number255(fp, maxval); 200 logo_data[i][j].blue = get_number255(fp, maxval); 201 } 202 break; 203 } 204 205 /* close file */ 206 fclose(fp); 207 } 208 209 static inline int is_black(struct color c) 210 { 211 return c.red == 0 && c.green == 0 && c.blue == 0; 212 } 213 214 static inline int is_white(struct color c) 215 { 216 return c.red == 255 && c.green == 255 && c.blue == 255; 217 } 218 219 static inline int is_gray(struct color c) 220 { 221 return c.red == c.green && c.red == c.blue; 222 } 223 224 static inline int is_equal(struct color c1, struct color c2) 225 { 226 return c1.red == c2.red && c1.green == c2.green && c1.blue == c2.blue; 227 } 228 229 static void write_header(void) 230 { 231 /* open logo file */ 232 if (outputname) { 233 out = fopen(outputname, "w"); 234 if (!out) 235 die("Cannot create file %s: %s\n", outputname, strerror(errno)); 236 } else { 237 out = stdout; 238 } 239 240 fputs("/*\n", out); 241 fputs(" * DO NOT EDIT THIS FILE!\n", out); 242 fputs(" *\n", out); 243 fprintf(out, " * It was automatically generated from %s\n", filename); 244 fputs(" *\n", out); 245 fprintf(out, " * Linux logo %s\n", logoname); 246 fputs(" */\n\n", out); 247 fputs("#include <linux/linux_logo.h>\n\n", out); 248 fprintf(out, "static unsigned char %s_data[] __initdata = {\n", 249 logoname); 250 } 251 252 static void write_footer(void) 253 { 254 fputs("\n};\n\n", out); 255 fprintf(out, "const struct linux_logo %s __initconst = {\n", logoname); 256 fprintf(out, "\t.type\t\t= %s,\n", logo_types[logo_type]); 257 fprintf(out, "\t.width\t\t= %d,\n", logo_width); 258 fprintf(out, "\t.height\t\t= %d,\n", logo_height); 259 if (logo_type == LINUX_LOGO_CLUT224) { 260 fprintf(out, "\t.clutsize\t= %d,\n", logo_clutsize); 261 fprintf(out, "\t.clut\t\t= %s_clut,\n", logoname); 262 } 263 fprintf(out, "\t.data\t\t= %s_data\n", logoname); 264 fputs("};\n\n", out); 265 266 /* close logo file */ 267 if (outputname) 268 fclose(out); 269 } 270 271 static int write_hex_cnt; 272 273 static void write_hex(unsigned char byte) 274 { 275 if (write_hex_cnt % 12) 276 fprintf(out, ", 0x%02x", byte); 277 else if (write_hex_cnt) 278 fprintf(out, ",\n\t0x%02x", byte); 279 else 280 fprintf(out, "\t0x%02x", byte); 281 write_hex_cnt++; 282 } 283 284 static void write_logo_mono(void) 285 { 286 unsigned int i, j; 287 unsigned char val, bit; 288 289 /* validate image */ 290 for (i = 0; i < logo_height; i++) 291 for (j = 0; j < logo_width; j++) 292 if (!is_black(logo_data[i][j]) && !is_white(logo_data[i][j])) 293 die("Image must be monochrome\n"); 294 295 /* write file header */ 296 write_header(); 297 298 /* write logo data */ 299 for (i = 0; i < logo_height; i++) { 300 for (j = 0; j < logo_width;) { 301 for (val = 0, bit = 0x80; bit && j < logo_width; j++, bit >>= 1) 302 if (logo_data[i][j].red) 303 val |= bit; 304 write_hex(val); 305 } 306 } 307 308 /* write logo structure and file footer */ 309 write_footer(); 310 } 311 312 static void write_logo_vga16(void) 313 { 314 unsigned int i, j, k; 315 unsigned char val; 316 317 /* validate image */ 318 for (i = 0; i < logo_height; i++) 319 for (j = 0; j < logo_width; j++) { 320 for (k = 0; k < 16; k++) 321 if (is_equal(logo_data[i][j], clut_vga16[k])) 322 break; 323 if (k == 16) 324 die("Image must use the 16 console colors only\n" 325 "Use ppmquant(1) -map clut_vga16.ppm to reduce the number " 326 "of colors\n"); 327 } 328 329 /* write file header */ 330 write_header(); 331 332 /* write logo data */ 333 for (i = 0; i < logo_height; i++) 334 for (j = 0; j < logo_width; j++) { 335 for (k = 0; k < 16; k++) 336 if (is_equal(logo_data[i][j], clut_vga16[k])) 337 break; 338 val = k<<4; 339 if (++j < logo_width) { 340 for (k = 0; k < 16; k++) 341 if (is_equal(logo_data[i][j], clut_vga16[k])) 342 break; 343 val |= k; 344 } 345 write_hex(val); 346 } 347 348 /* write logo structure and file footer */ 349 write_footer(); 350 } 351 352 static void write_logo_clut224(void) 353 { 354 unsigned int i, j, k; 355 356 /* validate image */ 357 for (i = 0; i < logo_height; i++) 358 for (j = 0; j < logo_width; j++) { 359 for (k = 0; k < logo_clutsize; k++) 360 if (is_equal(logo_data[i][j], logo_clut[k])) 361 break; 362 if (k == logo_clutsize) { 363 if (logo_clutsize == MAX_LINUX_LOGO_COLORS) 364 die("Image has more than %d colors\n" 365 "Use ppmquant(1) to reduce the number of colors\n", 366 MAX_LINUX_LOGO_COLORS); 367 logo_clut[logo_clutsize++] = logo_data[i][j]; 368 } 369 } 370 371 /* write file header */ 372 write_header(); 373 374 /* write logo data */ 375 for (i = 0; i < logo_height; i++) 376 for (j = 0; j < logo_width; j++) { 377 for (k = 0; k < logo_clutsize; k++) 378 if (is_equal(logo_data[i][j], logo_clut[k])) 379 break; 380 write_hex(k+32); 381 } 382 fputs("\n};\n\n", out); 383 384 /* write logo clut */ 385 fprintf(out, "static unsigned char %s_clut[] __initdata = {\n", 386 logoname); 387 write_hex_cnt = 0; 388 for (i = 0; i < logo_clutsize; i++) { 389 write_hex(logo_clut[i].red); 390 write_hex(logo_clut[i].green); 391 write_hex(logo_clut[i].blue); 392 } 393 394 /* write logo structure and file footer */ 395 write_footer(); 396 } 397 398 static void write_logo_gray256(void) 399 { 400 unsigned int i, j; 401 402 /* validate image */ 403 for (i = 0; i < logo_height; i++) 404 for (j = 0; j < logo_width; j++) 405 if (!is_gray(logo_data[i][j])) 406 die("Image must be grayscale\n"); 407 408 /* write file header */ 409 write_header(); 410 411 /* write logo data */ 412 for (i = 0; i < logo_height; i++) 413 for (j = 0; j < logo_width; j++) 414 write_hex(logo_data[i][j].red); 415 416 /* write logo structure and file footer */ 417 write_footer(); 418 } 419 420 static void die(const char *fmt, ...) 421 { 422 va_list ap; 423 424 va_start(ap, fmt); 425 vfprintf(stderr, fmt, ap); 426 va_end(ap); 427 428 exit(1); 429 } 430 431 static void usage(void) 432 { 433 die("\n" 434 "Usage: %s [options] <filename>\n" 435 "\n" 436 "Valid options:\n" 437 " -h : display this usage information\n" 438 " -n <name> : specify logo name (default: linux_logo)\n" 439 " -o <output> : output to file <output> instead of stdout\n" 440 " -t <type> : specify logo type, one of\n" 441 " mono : monochrome black/white\n" 442 " vga16 : 16 colors VGA text palette\n" 443 " clut224 : 224 colors (default)\n" 444 " gray256 : 256 levels grayscale\n" 445 "\n", programname); 446 } 447 448 int main(int argc, char *argv[]) 449 { 450 int opt; 451 452 programname = argv[0]; 453 454 opterr = 0; 455 while (1) { 456 opt = getopt(argc, argv, "hn:o:t:"); 457 if (opt == -1) 458 break; 459 460 switch (opt) { 461 case 'h': 462 usage(); 463 break; 464 465 case 'n': 466 logoname = optarg; 467 break; 468 469 case 'o': 470 outputname = optarg; 471 break; 472 473 case 't': 474 if (!strcmp(optarg, "mono")) 475 logo_type = LINUX_LOGO_MONO; 476 else if (!strcmp(optarg, "vga16")) 477 logo_type = LINUX_LOGO_VGA16; 478 else if (!strcmp(optarg, "clut224")) 479 logo_type = LINUX_LOGO_CLUT224; 480 else if (!strcmp(optarg, "gray256")) 481 logo_type = LINUX_LOGO_GRAY256; 482 else 483 usage(); 484 break; 485 486 default: 487 usage(); 488 break; 489 } 490 } 491 if (optind != argc-1) 492 usage(); 493 494 filename = argv[optind]; 495 496 read_image(); 497 switch (logo_type) { 498 case LINUX_LOGO_MONO: 499 write_logo_mono(); 500 break; 501 502 case LINUX_LOGO_VGA16: 503 write_logo_vga16(); 504 break; 505 506 case LINUX_LOGO_CLUT224: 507 write_logo_clut224(); 508 break; 509 510 case LINUX_LOGO_GRAY256: 511 write_logo_gray256(); 512 break; 513 } 514 exit(0); 515 } 516