/*--------------------------------------------------------------
	Binary image reduction              breduce v1.8a
		Written  by H.Goto , Sep. 1994
				(original "decim" created)
		Modified by H.Goto , Jan. 1995
		Modified by H.Goto , Oct. 1995
		Modified by H.Goto , Feb. 1997
				(renamed to "breduce")
		Modified by H.Goto , Apr. 1997
		E-Mail: hgot@ecip.tohoku.ac.jp
--------------------------------------------------------------*/

/*--------------------------------------------------------------------
  Copyright (C) 1994-1997  Hideaki Goto

        All Rights Reserved

  Permission to use, copy, modify, and distribute this software and
  its documentation for any purpose is hereby granted without fee,
  provided that (i) the above copyright notice and this permission
  notice appear in all copies and in supporting documentation, (ii)
  the name of the author, Hideaki Goto, may not be used in any
  advertising or otherwise to promote the sale, use or other
  dealings in this software without prior written authorization
  from the author, (iii) this software may not be used for
  commercial products without prior written permission from the
  author, and (iv) the notice of the modification is specified in
  case of that the modified copies of this software are distributed.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
  THE AUTHOR WILL NOT BE RESPONSIBLE FOR ANY DAMAGE CAUSED BY THIS
  SOFTWARE.
--------------------------------------------------------------------*/


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

#include	<utypes.h>
#include	<ufilep.h>



/*------------------------------------------------------
	Image processor class
------------------------------------------------------*/

class IMPR {
    private:
	void		div2(uchar *buf,uchar *dst,int size);
	void		div4(uchar *buf,uchar *dst,int size);
    public:
	void		lreduce(uchar *buf,uchar *dst,int size);
	int		lreduce(uchar *buf,uchar *dst,int srcsize,int dstsize);
};


void IMPR :: div2(uchar *src,uchar *dst,int size){
	int	i;
	for ( i=0 ; i<size ; i++ ){
		dst[i] &= src[2 * i] & src[2 * i +1];
	}
}


void IMPR :: div4(uchar *src,uchar *dst,int size){
	int	i;
	for ( i=0 ; i<size ; i++ ){
		dst[i] &= src[4 * i] & src[4 * i +1] & src[4 * i +2] & src[4 * i +3];
	}
}


void IMPR :: lreduce(uchar *buf,uchar *dst,int size){
	int	ix;
	for ( ix = size/2 ; ix > 0 ; ix-- ){
		*dst++ = *buf & *(buf+1);
		buf += 2;
	}
}


int IMPR :: lreduce(uchar *buf,uchar *dst,int srcsize,int dstsize){
	int	i;
	uchar	*lbuf;
	lbuf = dst;
	for ( i=0 ; i<dstsize ; i++ )  lbuf[i] = 0xff;
	if ( srcsize == (dstsize * 2) ){
		div2(buf,lbuf,dstsize);
	}
	else{	if ( srcsize == (dstsize * 4) ){
			div4(buf,lbuf,dstsize);
		}
		else{
			for ( i=0 ; i<srcsize ; i++ ){
				lbuf[ (i * dstsize) / srcsize ] &= buf[i];
			}
		}
	}
	return(0);
}




/*------------------------------------------------------
	Image filter class
------------------------------------------------------*/

class IMGF {
    private:
	uchar		fgcol,bgcol;
    public:
	void		convert_1to8(uchar *src,uchar *dst,int size);
	void		setFgcol(int col);
	void		setBgcol(int col);
			IMGF(void);
};


IMGF :: IMGF(void){
	bgcol = 0xff;
	fgcol = 0x00;
}


void IMGF :: convert_1to8(uchar *src,uchar *dst,int size){
	size /= 8;
	for( ; size > 0 ; --size ){
		if((*src&0x80)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x40)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x20)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x10)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x08)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x04)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x02)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		if((*src&0x01)==0) *(dst++)=bgcol; else *(dst++)=fgcol;
		++src;
	}
}


void IMGF :: setFgcol(int col){
	fgcol = (uchar)col;
}


void IMGF :: setBgcol(int col){
	bgcol = (uchar)col;
}




/*------------------------------------------------------
	Message class
------------------------------------------------------*/

class MSG {
    public:
	void		usage(void);
	void		switcherr(void);
	void		readerr(char *name);
	void		writeerr(char *name);
	void		nonsupported(char *name);
	void		memoryover(void);
};
    	
    	
void MSG :: usage(void){		// Display Usage
	fputs("Binary image reduction   breduce  v1.8a \n",stderr);
	fputs("Copyright (C) 1994-1997 Hideaki Goto \n",stderr);
	fputs("usage : breduce [-options] in_file|- [out_file] \n",stderr);
	fputs("	        -div N   : division factor (def. N=2) \n",stderr);
	fputs("		-quiet   : suppress messages \n",stderr);
}


void MSG :: switcherr(void){
	fputs("Switch error.\n",stderr);
}


void MSG :: readerr(char *name){
	fprintf(stderr,"Can't read %s.\n",name);
}


void MSG :: writeerr(char *name){
	fprintf(stderr,"Can't write %s.\n",name);
}


void MSG :: nonsupported(char *name){
	fprintf(stderr,"Non-supported file %s.\n",name);
}


void MSG :: memoryover(void){
	fputs("Not enough memory.\n",stderr);
}




/*------------------------------------------------------
	Argument / Option class
------------------------------------------------------*/

class ARGS {
    private:
	char		*fpath[16];
	int		fpcount;
    	int		sw_verbose;
    	int		sw_div;
    public:
	int		scanargv(int ac,char **av);
	int		SWverbose(void);
	int		SWdiv(void){  return(sw_div); }
	char		*infile(void);
	char		*outfile(void);
			ARGS(void);
};


ARGS :: ARGS(void){
	sw_verbose = -1;
	sw_div = 2;
}


int ARGS :: scanargv(int ac,char **av){
	int	i1,err,skip;
	char	*sw;
	fpcount = err = 0;
	skip = 0;
	--ac;
	for( i1 = 0 ; i1 < ac ; ++i1 ){
		if( skip ){  skip = 0;  continue;  }
		sw = av[i1+1];
		if( *sw == '-' && strlen(sw) != 1 ){
			++sw;
			if( 0 == strcmp(sw,"div") ){
				if((i1+1)<ac){
					sw_div = atoi(av[i1+2]);
					skip = -1;
					continue;
				}
				else{   err = -1;  break;  }
			}
			if( 0 == strcmp(sw,"quiet") ){
				sw_verbose = 0;
				continue;
			}
			if( 0 == strcmp(sw,"verbose") ){
				sw_verbose = -1;
				continue;
			}
			err = -1;  break;
		}
		else{	if ( fpcount < 16 ){
				if ( *sw == '-' )  fpath[fpcount++] = "";
				else               fpath[fpcount++] = sw;
			}
		}
	}
	return(err);
}


int ARGS :: SWverbose(void){
	return(sw_verbose);
}


char * ARGS :: infile(void){
	if ( fpcount < 1 )  return( "" );
	return( fpath[0] );
}


char * ARGS :: outfile(void){
	if ( fpcount < 2 )  return( "" );
	return( fpath[1] );
}




/*------------------------------------------------------
	Main routine
------------------------------------------------------*/

int main(int ac,char **av){
	ARGS		ARGS;
	MSG		MSG;
	PBMFILE		PBMLD,PBMSV;
	IMPR		IMPR;
	int		retcode,errflag;
	int		width,height;
	int		cvt_width,cvt_height;
	int		o_width,o_height,o_filetype;
	int		div;
	int		i,x,iy;
	uchar		*lbuf;
	uchar		*lbuf0;
	uchar		*lbuf2;

	if ( ac < 2 ){
		MSG.usage();
		exit(0);
	}
	if ( 0 != ARGS.scanargv(ac,av) ){
		MSG.switcherr();
		exit(1);
	}

	div = ARGS.SWdiv();
	if ( div < 1 ){
		MSG.switcherr();
		exit(1);
	}

	retcode = PBMLD.open(ARGS.infile(),"r");
	switch ( retcode ){
	  case 0:	break;
	  case -5:	MSG.nonsupported(ARGS.infile());  exit(2);
	  case -6:	MSG.memoryover();  exit(3);
	  default:	MSG.readerr(ARGS.infile());  exit(2);
	}

	width  = PBMLD.getwidth ();
	height = PBMLD.getheight();
	cvt_width  = ((width  + (div -1)) /div) *div;
	cvt_height = ((height + (div -1)) /div) *div;
	o_width  = cvt_width  / div;
	o_height = cvt_height / div;
	o_filetype = PBMLD.getfiletype();
	if ( ARGS.SWverbose() ){
		fprintf(stderr,"Input : %s is a %dx%d ", \
					ARGS.infile(),width,height );
		if ( PBMLD.getfiletype() == 0 ){
			fputs("RawBits PBM image\n",stderr);
		}
		else{	fputs("RawBits PGM image\n",stderr);
		}
		fprintf(stderr,"Output: %s is a %dx%d ", \
					ARGS.outfile(),o_width,o_height );
		if ( o_filetype == 0 ){
			fputs("RawBits PBM image\n",stderr);
		}
		else{	fputs("RawBits PGM image\n",stderr);
		}
	}

	if ( 0 == (lbuf = new uchar[cvt_width]) ){
		MSG.memoryover();
		exit(3);
	}
	if ( 0 == (lbuf0 = new uchar[2 * cvt_width]) ){
		MSG.memoryover();
		exit(3);
	}
	lbuf2 = lbuf0 + cvt_width;

	if ( o_filetype == 0 )
		PBMSV.setsize(o_width,o_height,1);
	else	PBMSV.setsize(o_width,o_height,8);
	retcode = PBMSV.open(ARGS.outfile(),"w");
	switch ( retcode ){
	  case 0:	break;
	  case -5:	MSG.nonsupported(ARGS.outfile());  exit(2);
	  case -6:	MSG.memoryover();  exit(2);
	  default:	MSG.writeerr(ARGS.outfile());  exit(2);
	}

	errflag = 0;
	for ( x=0 ; x<cvt_width ; x++ )  lbuf[x] = 0xff;
	for ( iy=0 ; iy<height ; iy+=div ){
		for ( x=0 ; x<o_width ; x++ )  lbuf0[x] = 0xff;
		for ( i=0 ; i<div ; i++ ){
			if ( (iy + i) >= height )  break;
			if ( 0 != PBMLD.readline_gray(-1,lbuf) ){
				errflag = -1;
				MSG.readerr(ARGS.infile());
				goto main_01;
			}
			IMPR.lreduce(lbuf,lbuf2,cvt_width,o_width);
			for ( x=0 ; x<o_width ; x++ )  lbuf0[x] &= lbuf2[x];
		}
		if ( o_filetype == 0 ){
			if ( 0 != PBMSV.writeline_bilevel(-1,lbuf0,128) ){ errflag = -1; break; }
		}
		else{	if ( 0 != PBMSV.writeline(-1,lbuf0) ){ errflag = -1; break; }
		}
	}

main_01:
	if ( !errflag ){
		if ( 0 != PBMSV.close() )  errflag = -1;
	}
	if ( errflag ){
		MSG.writeerr(ARGS.outfile());
		exit(2);
	}

	return(0);
}	
