/* lispmtopgm.c - read a file written by the tv:write-bit-array-file function
** of TI Explorer and Symbolics Lisp Machines, and write a PGM.
**
** Written by Jamie Zawinski based on code (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.
**
**   When one writes a multi-plane bitmap with tv:write-bit-array-file, it is
**   usually a color image; but a color map is not written in the file, so we
**   treat this as a graymap instead.  Since the pgm reader can also read pbms,
**   this doesn't matter if you're using only single plane images.
*/

#include <stdio.h>
#include <string.h>

#include "nstring.h"
#include "pgm.h"

#define LISPM_MAGIC  "This is a BitMap file"

static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
static int depth_to_word_size ARGS(( int depth ));
static unsigned int getval ARGS(( FILE* file ));

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    FILE* ifp;
    gray* grayrow;
    register gray* gP;
    short rows, cols, padright, row, col;
    short depth;
    int maxval;


    pgm_init( &argc, argv );

    if ( argc > 2 )
	pm_usage( "[lispmfile]" );

    if ( argc == 2 )
        ifp = pm_openr( argv[1] );
    else
	ifp = stdin;

    getinit( ifp, &cols, &rows, &depth, &padright );
    maxval = 1 << depth;

    if ( maxval > PGM_OVERALLMAXVAL )
        pm_error( "depth (%d bits) is too large", depth);

    pgm_writepgminit( stdout, cols, rows, (gray) maxval, 0 );
    overflow_add(cols, 7);
    grayrow = pgm_allocrow( ( cols + 7 ) / 8 * 8 );

    for ( row = 0; row < rows; ++row )
	{
        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
	    *gP = getval( ifp );
	pgm_writepgmrow( stdout, grayrow, cols, (gray) maxval, 0 );
	}
    pm_close( ifp );
    pm_close( stdout );
    exit( 0 );
    }

static long item, bitmask;
static unsigned int bitsperitem, maxbitsperitem, bitshift;

static void
getinit( file, colsP, rowsP, depthP, padrightP )
    FILE* file;
    short* colsP;
    short* rowsP;
    short* padrightP;
    short* depthP;
    {
    short cols_32;
    char magic[sizeof(LISPM_MAGIC)];
    int i;

    for ( i = 0; i < sizeof(magic)-1; ++i )
        magic[i] = getc( file );
    magic[i]='\0';
    if (!streq(LISPM_MAGIC, magic))
        pm_error( "bad id string in Lispm file" );
    
    if ( pm_readlittleshort( file, colsP ) == -1 )
        pm_error( "EOF / read error" );
    if ( pm_readlittleshort( file, rowsP ) == -1 )
        pm_error( "EOF / read error" );
    if ( pm_readlittleshort( file, &cols_32 ) == -1 )
        pm_error( "EOF / read error" );
    *depthP = getc( file );
    
    if ( *depthP == 0 )
	*depthP = 1;	/* very old file */

    overflow_add((int)colsP, 31);
    
    *padrightP = ( ( *colsP + 31 ) / 32 ) * 32 - *colsP;
    
    if ( *colsP != (cols_32 - *padrightP) ) {
/*    pm_message( "inconsistent input: Width and Width(mod32) fields don't agree" );  */
/*    *padrightP = cols_32 - *colsP;   */ /*    hmmmm....   */
      /* This is a dilemma.  Usually the output is rounded up to mod32, but not always.
       * For the Lispm code to not round up, the array size must be the same size as the
       * portion being written - that is, the array itself must be an odd size, not just
       * the selected portion.  Since arrays that are odd sizes can't be handed to bitblt,
       * such arrays are probably not image data - so punt on it for now.
       *
       * Also, the lispm code for saving bitmaps has a bug, in that if you are writing a
       * bitmap which is not mod32 across, the file may be up to 7 bits too short!  They
       * round down instead of up.
       *
       * The code in 'pgmtolispm.c' always rounds up to mod32, which is totally reasonable.
       */
      }
    bitsperitem = 0;
    maxbitsperitem = depth_to_word_size( *depthP );
    bitmask = ( 1 << maxbitsperitem ) - 1;		/* for depth=3, mask=00000111 */

    for ( i = 0; i < 9; ++i )
	getc( file );	/* discard bytes reserved for future use */
    }

static int
depth_to_word_size (depth)	
     int depth;			
     /* Lispm architecture specific - if a bitmap is written    */
     /* out with a depth of 5, it really has a depth of 8, and  */
     /* is stored that way in the file.			   */
{				
    if (depth==0 || depth==1)	return ( 1);
    else if (depth ==  2)	return ( 2);
    else if (depth <=  4)	return ( 4);
    else if (depth <=  8)	return ( 8);
    else if (depth <= 16)	return (16);
    else if (depth <= 32)	return (32);
    else {
      pm_error( "depth was %d, which is not in the range 1-32.", depth );
      /* Should never reach here */
      return(-1);
  }
}



static unsigned int
getval( file )
    FILE* file;
    {
    unsigned int b;

    if ( bitsperitem == 0 )
	{
	if ( pm_readlittlelong( file, &item ) == -1 )
	    pm_error( "EOF / read error" );
	bitsperitem = 32;
	bitshift = 0;
	item = ~item;
	}
    b = ( ( item >> bitshift ) & bitmask );
    bitsperitem = bitsperitem - maxbitsperitem;
    bitshift = bitshift + maxbitsperitem;
    return b;
    }
