/* pbmtoicon.c - read a PBM image and produce a Sun icon file
**
** Copyright (C) 1988 by Jef Poskanzer.
**
** 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.
*/

/* 2006.10 (afu)
   Changed bitrow from plain to raw, read function from pbm_readpbmrow() to
   pbm_readpbmrow_packed.  Applied wordint to scoop up 16 bit output items.
   putitem changed to better express the output format.
   Retired bitwise transformation functions.
*/

#include "wordaccess.h"
#include "pbm.h"

static unsigned short int itemBuff[8];
static unsigned int itemCnt;    /* takes values 0 to 8 */
FILE * putFp;



static void
putinit(FILE * const ofP) {
    putFp = ofP;
    itemCnt = 0;
}



static void
putitem(wordint const item) {

    if (itemCnt == 8 ) {
        /* Buffer is full.  Write out one line. */
        int rc;
    
        rc = fprintf(putFp,
                     "\t0x%04x,0x%04x,0x%04x,0x%04x,"
                     "0x%04x,0x%04x,0x%04x,0x%04x,\n",
                     itemBuff[0],itemBuff[1],itemBuff[2],itemBuff[3],
                     itemBuff[4],itemBuff[5],itemBuff[6],itemBuff[7]);
        if (rc < 0)        
           pm_error("fprintf() failed to write Icon bitmap");
           
        itemCnt = 0;
    }
    itemBuff[itemCnt++] = item & 0xffff;  /* Only lower 16 bits are used */
}



static void
putterm(void) {

    unsigned int i;

    for (i = 0; i < itemCnt; ++i) {
        int rc;
        rc = fprintf(putFp, "%s0x%04x%c", i == 0  ? "\t" : "", itemBuff[i],
                     i == itemCnt - 1 ? '\n' : ',');
        if (rc < 0)        
            pm_error("fprintf() failed to write Icon bitmap");
    }
}     



static void
writeIconHeader(FILE *       const ofP,
                unsigned int const width,
                unsigned int const height) {

    int rc;

    rc = fprintf(ofP,
                 "/* Format_version=1, Width=%u, Height=%u", width, height);
    if (rc < 0)
        pm_error("fprintf() failed to write Icon header");
        
    rc = fprintf(ofP, ", Depth=1, Valid_bits_per_item=16\n */\n");
    if (rc < 0)
        pm_error("fprintf() failed to write Icon header");
}



static void
writeIcon(FILE *       const ifP,
          unsigned int const cols,
          unsigned int const rows,
          int          const format,
          FILE *       const ofP) {

    unsigned int const wordintSize = sizeof(wordint) * 8;
        /* wordintSize is usually 32 or 64 bits.  Must be at least 24. */
    unsigned int const items = (cols + 15) / 16;
    unsigned int const bitrowBytes = pbm_packed_bytes(cols);
    unsigned int const pad = items * 16 - cols;
    /* 'padleft' is added to the output.  'padbyte' is for cleaning
       the input
    */
    unsigned int const padleft = pad / 2;
    unsigned int const padbyte = bitrowBytes * 8 - cols;
    unsigned int const shift   = (wordintSize - 24) + padleft;
    
    unsigned char * bitbuffer;
    unsigned char * bitrow;
    unsigned int row;

    bitbuffer = pbm_allocrow_packed(cols + wordintSize);
    bitrow = &bitbuffer[1];
    bitbuffer[0] = 0;
    bitrow[bitrowBytes] = 0;
    
    writeIconHeader(ofP, cols + pad, rows);

    putinit(ofP);

    for (row = 0; row < rows; ++row) {
        unsigned int itemSeq;
        pbm_readpbmrow_packed(ifP, bitrow, cols, format);

        /* Clear post-data junk in final partial byte */
        if (padbyte > 0) {
            bitrow[bitrowBytes-1] >>= padbyte;
            bitrow[bitrowBytes-1] <<= padbyte;
        }
        
        for (itemSeq = 0; itemSeq < items; ++itemSeq) {
            /* Scoop up bits, shift-align, send to format & print function.
    
               An item is 16 bits, typically spread over 3 bytes due to
               left-padding.  We use wordint here to scoop up 4 (or more)
               consecutive bytes.  An item always resides within the higher
               24 bits of each scoop.  It is essential to use wordint
               (or rather the wordaccess function bytesToWordInt() ); 
               simple long, uint_32t, etc. do not work for they are not
               shift-tolerant.
            */
            
            wordint const scoop = bytesToWordint(&bitbuffer[itemSeq*2]);
            putitem (scoop >> shift);
        }
    }
    putterm();    
}



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

    FILE * ifP;
    int rows, cols;
    int format;
    const char * inputFileName;

    pbm_init(&argc, argv);

    if (argc-1 > 1)
        pm_error("Too many arguments (%u).  "
                 "Only argument is optional input file", argc-1);
    if (argc-1 == 1)
        inputFileName = argv[1];
    else
        inputFileName = "-";
    
    ifP = pm_openr(inputFileName);

    pbm_readpbminit(ifP, &cols, &rows, &format);

    writeIcon(ifP, cols, rows, format, stdout);

    pm_close(ifP);

    return 0;
}

