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