/* ppmtoleaf.c - read a PPM and produce a ileaf img file
 *
 * Copyright (C) 1994 by Bill O'Donnell.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  This software is provided "as is" without express or
 * implied warranty.
 *
 * Known problems: pgms are not converted to leaf grayscales; they are
 * converted to 8-bit color images with all gray for colors.
 * 
 */

#include <stdio.h>

#include "ppm.h"

#define MAXCOLORS 256

pixel ** pixels;
colorhash_table cht;

static int Red[MAXCOLORS], Green[MAXCOLORS], Blue[MAXCOLORS];



static int
colorstobpp(unsigned int const colors) {

    int bpp;
    
    if (colors <= 2)
        bpp = 1;
    else if (colors <= 256)
        bpp = 8;
    else
        bpp = 24;

    return bpp;
}



static int
GetPixel(int const x,
         int const y) {

    int color;
    
    color = ppm_lookupcolor(cht, &pixels[y][x]);
    
    return color;
}




static void
leaf_writeimg(unsigned int const width,
              unsigned int const height,
              unsigned int const depth,
              unsigned int const ncolors,
              pixval       const maxval) {

    /* OK, this routine is not wicked efficient, but it is simple to follow
       and it works.
    */

    /* NOTE: byte order in ileaf img file fmt is big-endian, always! */
    
    /* magic */
    fputc(0x89, stdout);
    fputc(0x4f, stdout);
    fputc(0x50, stdout);
    fputc(0x53, stdout);
    
    /* version 4 */
    fputc(0x00, stdout);
    fputc(0x04, stdout);
    
    /* h resolution: pixels/inch: say 75=screen resolution */
    fputc(0x00, stdout);
    fputc(75, stdout);
    
    /* v resolution: pixels/inch: say 75=screen resolution */
    fputc(0x00, stdout);
    fputc(75, stdout);
    
    /* unique id, could be anything */
    fputc(0x01, stdout);
    fputc(0x02, stdout);
    fputc(0x03, stdout);
    fputc(0x04, stdout);
    
    /* x offset, always zero */
    fputc(0x00, stdout);
    fputc(0x00, stdout);
    
    /* y offset, always zero */
    fputc(0x00, stdout);
    fputc(0x00, stdout);
    
    /* dimensions 64k x 64k max */
    fputc((unsigned char)((width >> 8) & 0x00ff), stdout);
    fputc((unsigned char)(width  & 0x00ff), stdout);
    fputc((unsigned char)((height >> 8) & 0x00ff), stdout);
    fputc((unsigned char)(height  & 0x00ff), stdout);
    
    /* depth */
    fputc(0x00, stdout);
    fputc((unsigned char)depth, stdout);
    
    /* compressed, 0=uncompressed, 1=compressed */
    fputc(0x00, stdout);
    
    /* format, mono/gray = 0x20000000, RGB=0x29000000 */
    if (depth == 1)
        fputc(0x20, stdout);
    else
        fputc(0x29, stdout);

    fputc(0x00, stdout);
    fputc(0x00, stdout);
    fputc(0x00, stdout);
    
    /* colormap size */
    if (depth == 8) {
        unsigned int i;
        unsigned int row;

        fputc((unsigned char)((ncolors >> 8) & 0x00ff), stdout);
        fputc((unsigned char)(ncolors  & 0x00ff), stdout);
        for (i = 0; i < 256; ++i)
            fputc((unsigned char) Red[i]*255/maxval, stdout);
        for (i=0; i < 256; ++i)
            fputc((unsigned char) Green[i]*255/maxval, stdout);
        for (i = 0; i < 256; ++i)
            fputc((unsigned char) Blue[i]*255/maxval, stdout);
    
        for (row=0; row < height; ++row) {
            unsigned int col;
            for (col = 0; col < width; ++col) 
                fputc(GetPixel(col, row), stdout);
            if ((width % 2) != 0)
                fputc(0x00, stdout); /* pad to 2-bytes */
        }
    } else if (depth == 1) {
        /* mono image */
        /* no colormap */

        unsigned int row;

        fputc(0x00, stdout);
        fputc(0x00, stdout);

        for (row = 0; row < height; ++row) {
            unsigned char bits;
            unsigned int col;
            bits = 0;
            for (col = 0; col < width; ++col) {
                if (GetPixel(col,row))
                    bits |= (unsigned char) (0x0080 >> (col % 8));
                if (((col + 1) % 8) == 0)  {
                    fputc(bits, stdout);
                    bits = 0;
                }
            }
            if ((width % 8) != 0)
                fputc(bits, stdout);
            if ((width % 16) && (width % 16) <= 8)
                fputc(0x00, stdout);  /* 16 bit pad */
        }
    } else {
        /* no colormap, direct or true color (24 bit) image */

        unsigned int row;

        fputc(0x00, stdout);
        fputc(0x00, stdout);
        
        for (row = 0; row < height; ++row) {
            unsigned int col;
            for (col = 0; col < width; ++col) 
                fputc(pixels[row][col].r * 255 / maxval, stdout);
            if (width % 2 != 0)
                fputc(0x00, stdout); /* pad to 2-bytes */
            for (col = 0; col < width; ++col) 
                fputc(pixels[row][col].g * 255 / maxval, stdout);
            if (width % 2 != 0)
                fputc(0x00, stdout); /* pad to 2-bytes */
            for (col = 0; col < width; ++col) 
                fputc(pixels[row][col].b * 255 / maxval, stdout);
            if (width % 2 != 0)
                fputc(0x00, stdout); /* pad to 2-bytes */
        }
    }
}



int
main(int argc, const char * argv[]) {

    FILE * ifP;
    int argn;
    int rows, cols;
    unsigned int BitsPerPixel;
    int colors;
    pixval maxval;
    colorhist_vector chv;
    const char * const usage = "[ppmfile]";
    
    pm_proginit(&argc, argv);
    
    argn = 1;
    
    if (argn < argc) {
        ifP = pm_openr(argv[argn]);
        argn++;
    } else
        ifP = stdin;
    
    if (argn != argc)
        pm_usage(usage);
    
    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
    
    pm_close(ifP);
    
    /* Figure out the colormap. */
    pm_message("Computing colormap...");
    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
    if (chv) {
        unsigned int i;

        pm_message("... Done.  %u colors found.", colors);
    
        for (i = 0; i < colors; ++i) {
            Red[i]   = (int) PPM_GETR( chv[i].color);
            Green[i] = (int) PPM_GETG( chv[i].color);
            Blue[i]  = (int) PPM_GETB( chv[i].color);
        }
        BitsPerPixel = colorstobpp(colors);
    
        /* And make a hash table for fast lookup. */
        cht = ppm_colorhisttocolorhash(chv, colors);
        ppm_freecolorhist(chv);
    } else {
        BitsPerPixel = 24;
        pm_message("  ... Done.  24-bit true color %u color image.", colors);
    }
    
    leaf_writeimg(cols, rows, BitsPerPixel, colors, maxval);
    
    return 0;
}