/* hpcdtoppm (Hadmut's pcdtoppm) v0.6
*  Copyright (c) 1992, 1993, 1994 by Hadmut Danisch (danisch@ira.uka.de).
*  Permission to use and distribute this software and its
*  documentation for noncommercial use 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. It is not allowed to sell this software in 
*  any way. This software is not public domain.
*/

#include "hpcdtoppm.h"
#define DITH_NEUTR 128

FLTPT PAPER_LEFT    =DEF_PAPER_LEFT;
FLTPT PAPER_BOTTOM  =DEF_PAPER_BOTTOM;
FLTPT PAPER_WIDTH   =DEF_PAPER_WIDTH;
FLTPT PAPER_HEIGHT  =DEF_PAPER_HEIGHT;
FLTPT PRINTER_XDPI  =DEF_DPI;
FLTPT PRINTER_YDPI  =DEF_DPI;
FLTPT PRINTER_FAK   =1.0;
sINT  PSIZE_SET=0,DPI_SET=0,FAK_SET=0;


static char pshdr[]="%%Creator: hpcdtoppm v0.6\n";
static char hex[]="0123456789ABCDEF";
#define HEX(x) {fputc(hex[((x)>>4)&0xf],fout);fputc(hex[(x)&0xf],fout);}


/* find an appropriate scaling coefficient and generate a ps header
 * including a BoundingBox comment and a translate/scale sequence to 
 * fit the pixw*pixh image into a square on paper with PS user coordinates 
 * x,y,w,h 
 */
static void size_dependant(FILE *fout,sINT pixw,sINT pixh,
                           FLTPT x,FLTPT y,FLTPT w,FLTPT h)
{ FLTPT scale=(FLTPT)w/pixw,scaley=(FLTPT)h/pixh;

  if(scale>scaley) scale=scaley;
  x+=w/2.0;y+=h/2.0;
  fprintf(fout,"%%%%BoundingBox: %.8g %.8g %.8g %.8g\n",
	 x-scale*pixw/2.0,y-scale*pixh/2.0,
	 x+scale*pixw/2.0,y+scale*pixh/2.0);
  fprintf(fout,"%s",pshdr);
  fputs("%%Pages: 1 1\n",fout);

  if(pcdname) fprintf(fout,"%%%%Title: %s\n",pcdname);
  fputs("%%EndComments\n\n",fout);

  fprintf(fout,"%f %f translate\n",x-scale*pixw/2.0,y-scale*pixh/2.0);
  fprintf(fout,"%f %f scale\n",scale*pixw,scale*pixh);
}



static void end_postscript(FILE *fout)
 {
  fputs("%%EOF\n\n",fout);
 }




static void sub_psgrey(FILE *fout,dim w,dim h, uBYTE *ptr,sdim zeil,sdim pix)
{ dim x,y;
  register uBYTE *p;
  sINT c;

  size_dependant(fout,w,h,PAPER_LEFT,PAPER_BOTTOM,PAPER_WIDTH,PAPER_HEIGHT);
  fprintf(fout,"%d string\n",w);
  fprintf(fout,"%d %d 8\n",w,h);	/* always 8 bit per channel */
  fprintf(fout,"[%d 0 0 %d 0 %d]\n",w,-h,h);
  fputs("{currentfile 1 index readhexstring pop} image\n",fout);
  
  c=0;
  for(y=0;y<h;y++,ptr+=zeil)
    for(p=ptr,x=0;x<w;x++,p+=pix)
      {HEX(*p);
       if(!(++c % 36)) fputs("\n",fout);
      }

  fputs("\npop\n\n",fout);
}







void write_epsgrey(FILE *fout,dim w,dim h, uBYTE *ptr,sdim zeil,sdim pix)
{ 
  fputs("%!PS-Adobe-2.0 EPSF-2.0\n",fout);
  sub_psgrey(fout,w,h, ptr,zeil,pix);
  end_postscript(fout);
}

void write_psgrey(FILE *fout,dim w,dim h, uBYTE *ptr,sdim zeil,sdim pix)
{ 
  fputs("%!PS-Adobe-2.0\n",fout);
  sub_psgrey(fout,w,h, ptr,zeil,pix);
  fputs("showpage\n",fout);
  end_postscript(fout);
}











static void sub_psrgb(FILE *fout,dim w,dim h, 
                      uBYTE *rptr,sdim rzeil,sdim rpix,  
		      uBYTE *gptr,sdim gzeil,sdim gpix,  
		      uBYTE *bptr,sdim bzeil,sdim bpix) 
{ dim x,y;
  register uBYTE *pr,*pg,*pb;
  sINT c;

  size_dependant(fout,w,h,PAPER_LEFT,PAPER_BOTTOM,PAPER_WIDTH,PAPER_HEIGHT);
  fprintf(fout,"%d string\n",w*3);
  fprintf(fout,"%d %d 8\n",w,h);	/* always 8 bit per channel */
  fprintf(fout,"[%d 0 0 %d 0 %d]\n",w,-h,h);
  fputs("{currentfile 1 index readhexstring pop} false 3 colorimage\n",fout);
  
  c=0; 
  for(y=0;y<h;y++,rptr+=rzeil,gptr+=gzeil,bptr+=bzeil)
    for(pr=rptr,pg=gptr,pb=bptr,x=0;x<w;x++,pr+=rpix,pg+=gpix,pb+=bpix)
      {HEX(*pr);HEX(*pg);HEX(*pb);
       if(!(++c % 12)) fputs("\n",fout);
      }

  fputs("\npop\n\n",fout);
}


void write_epsrgb(FILE *fout,dim w,dim h, 
                      uBYTE *rptr,sdim rzeil,sdim rpix,  
		      uBYTE *gptr,sdim gzeil,sdim gpix,  
		      uBYTE *bptr,sdim bzeil,sdim bpix) 
{ 
  fputs("%!PS-Adobe-2.0 EPSF-2.0\n",fout);
  sub_psrgb(fout,w,h, rptr,rzeil,rpix,gptr,gzeil,gpix,bptr,bzeil,bpix);
  end_postscript(fout);
}



void write_psrgb(FILE *fout,dim w,dim h, 
                      uBYTE *rptr,sdim rzeil,sdim rpix,  
		      uBYTE *gptr,sdim gzeil,sdim gpix,  
		      uBYTE *bptr,sdim bzeil,sdim bpix) 
{ 
  fputs("%!PS-Adobe-2.0\n",fout);
  sub_psrgb(fout,w,h, rptr,rzeil,rpix,gptr,gzeil,gpix,bptr,bzeil,bpix);
  fputs("showpage\n",fout);
  end_postscript(fout);
}









typedef sINT dt;
extern sINT dithtab[];
#define DS 4
#define DA (1<<(DS-1))
#define DT 127

static void fakcopy(dim worig,dim horig, uBYTE *ptr1,sdim zeil,sdim pix,
                    sdim wx,sINT zn,dt *dest)
 {FLTPT owf,ohf,wbruch,hbruch,m1,m2,ha,hb;
  dim   owd,ohd,x;
  uBYTE *ptr2;
  sINT  md;
  
  ohf=zn/PRINTER_FAK;
  ohd=(dim)ohf;
  hbruch=ohf-(FLTPT)ohd;
  if(ohd>=horig) error(E_INTERN);

  ptr1+=zeil*ohd;
  ptr2= (ohd < horig - 1) ? ptr1+zeil : ptr1;

  dest[-1]=DITH_NEUTR;

  for(x=0;x<wx;x++)
   {owf=x/PRINTER_FAK;
    owd=(dim)owf;
    wbruch=owf-(FLTPT)owd;
    if(owd>=worig) error(E_INTERN);

    if(owd<worig-1)
     {ha=(FLTPT)ptr1[owd*pix];
      hb=(FLTPT)ptr1[(owd+1)*pix];
      m1=ha+wbruch*(hb-ha);

      ha=(FLTPT)ptr2[owd*pix];
      hb=(FLTPT)ptr2[(owd+1)*pix];
      m2=ha+wbruch*(hb-ha);
     }
    else
     { m1=(FLTPT)ptr1[owd*pix];
       m2=(FLTPT)ptr2[owd*pix];
     }
    md=(sINT)(m1+hbruch*(m2-m1));
    if(md<0 || md>255) {fprintf(stderr,"md %d\n",md); error(E_INTERN);}
    *(dest++)=dithtab[md];

   }
  
  dest[0]=DITH_NEUTR;
 }

static void sub_psdith(FILE *fout,dim worig,dim horig, uBYTE *ptr,sdim zeil,sdim pix)
{ register uBYTE *p;
  sINT c,i,ii,j,reg,bit;
  dt new,diff;
  dt *dP1,*dP2,*akt,*nex,*help,*rrun;
  dim ww=0,wl=0,wr=0,hh=0,ho=0,hu=0,wx=0,hx=0;
  int ccase;
  FLTPT PW=0.0,PH=0.0;

#define copy(n,d) {if(FAK_SET) fakcopy(worig,horig,ptr,zeil,pix,wx,n,d); \
                   else{p=ptr+((n)*zeil); rrun=d;\
                        for(ii=0;ii<wx;ii++,p+=pix,rrun++) *rrun = dithtab[*p]; \
                        d[-1]=d[wx]=DITH_NEUTR; }}


#define pr(x) { reg= (reg<<1) | x; bit++;    \
     if(bit==8) {HEX(reg);   \
                 if(!(++c % 36)) fputs("\n",fout);\
                 bit=reg=0;}}

#define flush { while(bit) pr(1); }
#define fill pr(1)



#define MakeFit(wi,he) {  ww=(wi+7) & (~7);   wl=(ww-wi)/2;  wr=ww-wi-wl; \
                          hh=(he+7) & (~7);   ho=(hh-he)/2;  hu=hh-he-ho; }


  
  ccase=( FAK_SET   ? 4 : 0 ) |
        ( DPI_SET   ? 2 : 0 ) |
        ( PSIZE_SET ? 1 : 0 ) ;

 
  switch (ccase)
   {case 0: /*  no option or -dpi option */
    case 2: hx=horig;
            wx=worig;
            MakeFit(wx,hx);
            PW=ww*72.0/PRINTER_XDPI;
            PH=hh*72.0/PRINTER_YDPI;
            break;

    case 1: /* paper size set with -pw and/or -ph */
            hx=horig;
            wx=worig;
            MakeFit(wx,hx);
            PW=PAPER_WIDTH;
            PH=PAPER_HEIGHT;
            break;

    case 6: /* -fak option (and perhaps -dpi) */
    case 4: hx=PRINTER_FAK*horig+0.5;
            wx=PRINTER_FAK*worig+0.5;
            MakeFit(wx,hx);
            PW=ww*72.0/PRINTER_XDPI;
            PH=hh*72.0/PRINTER_YDPI; 
            break;

    case 5: /* -fak and papersize */
            hx=PRINTER_FAK*horig+0.5;
            wx=PRINTER_FAK*worig+0.5;
            MakeFit(wx,hx);
            PW=PAPER_WIDTH;
            PH=PAPER_HEIGHT;
            break;

    case 3: /* papersize and dpi set, probably the most important case */
            PW=PAPER_WIDTH;
            PH=PAPER_HEIGHT;

            FAK_SET=1;
            {FLTPT fw,fh;
             fw=PW/72.0*PRINTER_XDPI/worig;
             fh=PH/72.0*PRINTER_YDPI/horig;
             PRINTER_FAK=MIN(fw,fh);
            }
            hx=PRINTER_FAK*horig+0.5;
            wx=PRINTER_FAK*worig+0.5;
            MakeFit(wx,hx);
            PW=ww*72.0/PRINTER_XDPI;
            PH=hh*72.0/PRINTER_YDPI; 
            break;


    case 7: /* size, dpi and factor set, this case should have been
               cought earlier... */
    default: error(E_INTERN);
   };

  if(FAK_SET && (PRINTER_FAK<=0.0  || PRINTER_FAK >=1000.0)) error(E_PRPAR);
  if(PW<=0.0 || PH<=0.0) error(E_PRPAR);

  if(wx < 4 || hx < 4) error(E_PRPAR);


  if (!(dP1=(dt *)malloc((wx+2)*sizeof(dt)))) error(E_MEM);
  if (!(dP2=(dt *)malloc((wx+2)*sizeof(dt)))) error(E_MEM);



  size_dependant(fout,ww,hh,PAPER_LEFT,PAPER_BOTTOM,PW,PH);

  fprintf(fout,"%d string\n",ww);
  fprintf(fout,"%d %d 1\n",ww,hh);	/* always 8 bit per channel */
  fprintf(fout,"[%d 0 0 %d 0 %d]\n",ww,-hh,hh);
  fputs("{currentfile 1 index readhexstring pop} image\n",fout);
  
  c=bit=reg=0;
  akt=dP1+1;
  nex=dP2+1;

  for(i=0;i<ho;i++)
    for(j=0;j<ww;j++)
      fill;

  copy(0,nex);
  for(i=0;i<hx;i++)
   {help=akt; akt=nex; nex=help;
    if(i<hx-1) copy(i+1,nex);

    if(i&1)
      for(j=0;j<wx;j++)
       { new=akt[j]>DT ? 255 : 0;
         diff = akt[j]-new;
         akt[j]=new;
         akt[j+1]+=(diff*7 + DA)>>DS;
         nex[j+1]+=(diff   + DA)>>DS;
         nex[j  ]+=(diff*5 + DA)>>DS;
         nex[j-1]+=(diff*3 + DA)>>DS;
       }
    else
      for(j=wx-1;j>=0;j--)
       { new=akt[j]>DT ? 255 : 0;
         diff = akt[j]-new;
         akt[j]=new;
         akt[j-1]+=(diff*7 + DA)>>DS;
         nex[j-1]+=(diff   + DA)>>DS;
         nex[j  ]+=(diff*5 + DA)>>DS;
         nex[j+1]+=(diff*3 + DA)>>DS;
       }


    for(j=0;j<wl;j++) fill;

    for(j=0;j<wx;j++)
     {if (akt[j]>DT) { pr(1); }
      else           { pr(0); }
     }

    for(j=0;j<wr;j++) fill;

    flush;
   }

  for(i=0;i<hu;i++)
    for(j=0;j<ww;j++)
      fill;
  flush;

  fputs("\npop\n\n",fout);
  free(dP1);
  free(dP2);
}


void write_epsdith(FILE *fout,dim w,dim h, uBYTE *ptr,sdim zeil,sdim pix)
{ 
  fputs("%!PS-Adobe-2.0 EPSF-2.0\n",fout);
  sub_psdith(fout,w,h, ptr,zeil,pix);
  end_postscript(fout);
}

void write_psdith(FILE *fout,dim w,dim h, uBYTE *ptr,sdim zeil,sdim pix)
{ 
  fputs("%!PS-Adobe-2.0\n",fout);
  sub_psdith(fout,w,h, ptr,zeil,pix);
  fputs("showpage\n",fout);
  end_postscript(fout);
}



