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