/* pbmtogem.c - read a portable bitmap and produce a GEM .img file
**
** Author: David Beckemeyer (bdt!david)
**
** Much of the code for this program was taken from other
** pbmto* programs.  I just modified the code to produce
** a .img header and generate .img "Bit Strings".
**
** Thanks to Diomidis D. Spinellis for the .img header format.
**
** Copyright (C) 1988 by David Beckemeyer (bdt!david) and Jef Poskanzer.
**
** Modified by Johann Haider to produce Atari ST compatible files
**
** 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.
*/
/*
*  92/07/11 jh
*
*  Changes made to this program:
*  changed header length to count words to conform with Atari ST documentation
*  removed rounding of the imagewidth to the next word boundary
*  removed arbitrary limit to imagewidth
*  changed pattern length to 1 to simplify locating of compressable parts
*	in real world images
*  add solid run and pattern run compression
*
*  Deficiencies:
*  Compression of repeated scanlines not added
*  
*	Johann Haider (jh@fortec.tuwien.ac.at)
*
* 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
* Changed to remove architecture dependencies
* Added compression of repeated scanlines
*/

#include <stdio.h>
#include "pbm.h"

#define SOLID_0 0
#define SOLID_1 0xff
#define MINRUN 4
#define putsolid(v,c) putc((v&0x80)|c, stdout)
#define putpattern(v,c) putc(0, stdout);putc(c, stdout);putc(v, stdout)

static void putinit ARGS ((int rows, int cols));
static void putbit ARGS(( bit b ));
static void putitem ARGS(( void ));
static void putrow ARGS(( void ));
static void flushrow ARGS ((void));
static void putstring ARGS((register char *p, register int n));

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    FILE* ifp;
    bit* bitrow;
    register bit* bP;
    int rows, cols, format, row, col;

    pbm_init( &argc, argv );

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

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

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

    bitrow = pbm_allocrow( cols );

    putinit (rows, cols);
    for ( row = 0; row < rows; ++row )
	{
#ifdef DEBUG
	fprintf (stderr, "row %d\n", row);
#endif
	pbm_readpbmrow( ifp, bitrow, cols, format );
        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
	    putbit( *bP );
        putrow( );
        }
    flushrow ();

    pm_close( ifp );


    exit( 0 );
    }

static short item;
static int outcol, outmax;
static short bitsperitem, bitshift;
static short linerepeat;
static unsigned char *outrow, *lastrow;

static void
putinit (rows, cols)
     int rows, cols;
{
  if (pm_writebigshort (stdout, (short) 1) == -1 /* Image file version */
      || pm_writebigshort (stdout, (short) 8) == -1 /* Header length */
      || pm_writebigshort (stdout, (short) 1) == -1 /* Number of planes */
      || pm_writebigshort (stdout, (short) 1) == -1 /* Pattern length */
      || pm_writebigshort (stdout, (short) 372) == -1 /* Pixel width */
      || pm_writebigshort (stdout, (short) 372) == -1 /* Pixel height */
      || pm_writebigshort (stdout, (short) cols) == -1
      || pm_writebigshort (stdout, (short) rows) == -1)
    pm_error ("write error");
  item = 0;
  bitsperitem = 0;
  bitshift = 7;
  outcol = 0;
  outmax = (cols + 7) / 8;
  outrow = (unsigned char *) pm_allocrow (outmax, sizeof (unsigned char));
  lastrow = (unsigned char *) pm_allocrow (outmax, sizeof (unsigned char));
  linerepeat = -1;
}

#if __STDC__
static void
putbit( bit b )
#else /*__STDC__*/
static void
putbit( b )
    bit b;
#endif /*__STDC__*/
    {
    if ( bitsperitem == 8 )
	putitem( );
    ++bitsperitem;
    if ( b == PBM_BLACK )
	item += 1 << bitshift;
    --bitshift;
    }

static void
putitem( )
    {
    outrow[outcol++] = item;
    item = 0;
    bitsperitem = 0;
    bitshift = 7;
    }

static void
putstring (p, n)
register char *p;
register int n;
{
#ifdef DEBUG
    fprintf (stderr, "Bitstring, length: %d, pos %d\n", n, outcol);
#endif
    (void) putc(0x80, stdout);		/* a Bit string */
    (void) putc(n, stdout);	/* count */
    fwrite( p, n, 1, stdout );
}

static void
putrow( )
{
  if (bitsperitem > 0)
    putitem ();
  outcol = 0;
  if (linerepeat == -1 || linerepeat == 255
      || bcmp (outrow, lastrow, outmax) != 0)
    {
      unsigned char *temp;
      if (linerepeat != -1) /* Unless first line */
	flushrow ();
      /* Swap the pointers */
      temp = outrow; outrow = lastrow; lastrow = temp;
      linerepeat = 1;
    }
  else
    /* Repeated line */
    linerepeat++;
}

static void
flushrow( )
    {
    register unsigned char *outp, *p, *q;
    register int count;
    int col = outmax;

    if (linerepeat > 1)
      {
	/* Put out line repeat count */
	fwrite ("\0\0\377", 3, 1, stdout);
	putchar (linerepeat);
      }
    for (outp = p = lastrow; col > 0;)
    {
	    for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
	    if (count > MINRUN)
	    {
		if (p > outp)
		{
		    putstring (outp, p-outp);
		    outp = p;
		}
		col -= count;
		switch (*p)
		{
		case SOLID_0:
#ifdef DEBUG
/*			if (outcol > 0) */
			fprintf (stderr, "Solid run 0, length: %d\n", count);
#endif
			putsolid (SOLID_0, count);
			break;

		case SOLID_1:
#ifdef DEBUG
			fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
#endif
			putsolid (SOLID_1, count);
			break;
		default:
#ifdef DEBUG
			fprintf (stderr, "Pattern run, length: %d\n", count);
#endif
			putpattern (*p, count);
			break;
		}
		outp = p = q;
	    }
	    else
	    {
		p++;
		col--;
	    }
    }		
    if (p > outp)
         putstring (outp, p-outp);
    if (ferror (stdout))
      pm_error ("write error");
}

