
/**********************************************************/
/* CONVOLVE.C - Turbo C 2.0 implementation of image       */
/*                          convolution                   */
/* ----------   by Wesley G. Faler.  All code is "as is". */
/* There is NO copyright.  Use this code as you will, and */
/* if you make money at it, good for you.                 */
/**********************************************************/

#include<stdlib.h>
#include<stdio.h>
#include<graphics.h>
#include<alloc.h>
#include<ctype.h>

int load_cut(char *fname);
int load_convolution_matrix(char *fname);
int convolve_image(void);
int swap_pictures(void);

int minx,maxx,miny,maxy;
int LOADPAGE=0;
int ENHANCEPAGE=1;
int *cmat, *pmat, *vmat;
int cmx,cmy,cmnum;

struct palettetype palette,newpal;
int driver,mode;

int cleancut=-1;

int init_graphics(void)
{
 driver=DETECT; mode=0;
 detectgraph(&driver,&mode);
 if(driver==VGA) mode=VGAMED;
 initgraph(&driver,&mode,"");
 getpalette(&palette);
 getpalette(&newpal);
}

int cleanup_image(void)
{
 int i,j,num,x,y,k;

 if(cleancut<0) return;
 setactivepage(LOADPAGE);
 setvisualpage(ENHANCEPAGE);
 for(x=minx;x<maxx;x++) {
  for(y=miny;y<maxy;y++) {
   if(getpixel(x,y)!=0) num=-1;
    else num=0;
   for(j=-1;j<2;j++) {
    for(i=-1;i<2;i++) {
     if(getpixel(x+i,y+j)!=0) num++;
    }
   }
   if(num>cleancut) {
    k=getpixel(x,y);
    setactivepage(ENHANCEPAGE);
    putpixel(x,y,k);
    setactivepage(LOADPAGE);
   }
  }
 }
 k=ENHANCEPAGE; ENHANCEPAGE=LOADPAGE; LOADPAGE=k;
}

void show_test_image(void)
{
 int i;

 minx=cmx; miny=cmy;
 maxx=100+minx; maxy=100+miny;
 setcolor(1);
 moveto(minx,miny);
 randomize();
 for(i=0;i<20;i++)
  lineto(random(100)+minx,random(100)+miny);
 for(i=0;i<10;i++)
  fillellipse(random(50)+25+minx,random(50)+25+miny,
              random(25),random(25));
}

main()
{
 char fname[50];
 int flag=0;

 load_convolution_matrix("matrix.dat");
 printf(".CUT file (1) or test image (0)?");
 scanf("%d",&flag);
 flag= flag? 1:0;
 if(flag) {
  fflush(stdin);
  printf("filename to process:");
  gets(fname);
 }

 printf("Delete pixels with x or fewer neighbors. x=");
 scanf("%d",&cleancut);
 if(cleancut>8) cleancut=8;

 init_graphics();
 setactivepage(1); cleardevice();
 setactivepage(0); cleardevice();

 setactivepage(LOADPAGE); setvisualpage(LOADPAGE);
 if(flag) load_cut(fname);
  else show_test_image();
 cleanup_image();

 setvisualpage(ENHANCEPAGE);
 convolve_image();

 swap_pictures();
 restorecrtmode();
}

int toggle_colors(char c)
{
 c=tolower(c);
 c=c-'a';
 if(c<0 || c>=palette.size) return 0;
 newpal.colors[c]= palette.colors[c]-newpal.colors[c];
 setpalette(c,newpal.colors[c]);
 return 1;
}

int swap_pictures(void)
{
 int mode=0;
 char a;

 setvisualpage(LOADPAGE);
 for(;;) {
  a=getch();
  if(a==27) return;
  if(toggle_colors(a)) continue;
  if(mode==0) setvisualpage(ENHANCEPAGE);
  if(mode==1) setvisualpage(LOADPAGE);
  mode=1-mode;
 }
}

int convolve_image(void)
{
 int i,j,k,nval;
 int *vx, *vy, *c;
 int colmax,offset,end,midy;
 char **lines=NULL;
 char *temp=NULL;

 offset=-minx+(cmx/2);
 end=cmy-1; midy=cmy/2;
 lines=(char **)malloc(cmy*sizeof(char *));
 for(i=0;i<cmy;i++)
     lines[i]=(char *)malloc(sizeof(char)*(maxx-minx+cmx+1));
 setactivepage(LOADPAGE);
 for(j=-cmy/2;j<cmy/2;j++) {
  for(i=minx-cmx/2;i<(maxx+cmx/2+1);i++) {
   lines[j+midy][i+offset]=getpixel(i,j+miny);
  }
 }
 colmax=getmaxcolor();
 for(j=miny;j<maxy;j++) {
  setactivepage(LOADPAGE);
  for(i=j+cmy/2,k=minx-cmx/2,nval=maxx+cmx/2;k<nval;k++)
   lines[end][k+offset]=getpixel(k,i);
  for(i=minx;i<maxx;i++) {
   /* Load & multiply neighbors into matrix */
   setactivepage(LOADPAGE);
   vx=vmat; vy=vmat+1; c=cmat; nval=0;
   for(k=0;k<cmnum;k++) {
    if(*c) nval+= lines[(*vy)+midy][i+(*vx)+offset]*(*c);
    /* if(*c) nval+= getpixel(i+(*vx),j+(*vy)) * (*c); */
    c++;
    vx+=2; vy+=2;
   }
   /* Cut off values too high or too low */
   if(nval<0) nval=0;
   if(nval>colmax) nval=colmax;
   /* Place new pixel value */
   setactivepage(ENHANCEPAGE);
   putpixel(i,j,nval);
  }
  if(kbhit()) { getch(); break; }
  /* rotate line pointers */
  temp=lines[0];
  for(i=1;i<cmy;i++) lines[i-1]=lines[i];
  lines[end]=temp;
 }
 for(i=0;i<cmy;i++) {
  if(lines[i]!=NULL) free(lines[i]);
 }
 if(lines!=NULL) {
  free(lines);
 }
 return;
}

int build_offset_vectors(void)
{
 int *t;
 int il,im,jl,jm,i,j;

 il=-cmx/2; im=cmx+il;
 jl=-cmy/2; jm=cmy+jl;
 t=vmat;
 for(j=jl;j<jm;j++) {
  for(i=il;i<im;i++) {
   *t++=i; *t++=j;
  }
 }
}

int load_convolution_matrix(char *fname)
{
 /* Layout of matrix file:
     #x #y
     x0y0 x1y0 ... xny1
     .... .... ... ....
     x0ym x1ym ... xnym
 */
 FILE *mf;
 int *t;
 int i,j,im,jm;

 if( (mf=fopen(fname,"rt"))==NULL ) {
  printf("Cannot load matrix file.\n");
  abort();
 }
 fscanf(mf,"%d%d",&im,&jm);
 if( (im&1)==0 || (jm&1)==0 ) {
  printf("Convolution matrix MUST have a center point.\n");
  abort();
 }
 if( (cmat=(int *)calloc(im*jm,sizeof(int)))==NULL ) {
  printf("Unable to calloc convolution matrix.\n");
  abort();
 }
 if( (vmat=(int *)calloc(2*im*jm,sizeof(int)))==NULL ) {
  printf("Unable to calloc offset vector matrix.\n");
  abort();
 }
 cmx=im; cmy=jm; cmnum=im*jm;
 t=cmat;
 for(j=0;j<jm;j++) {
  for(i=0;i<im;i++) {
   if( fscanf(mf,"%d",t++)!=1 ) {
    printf("Unable to read matrix.\n");
    abort();
   }
  }
 }
 fclose(mf);
 build_offset_vectors();
}

int load_cut(char *fname)
{
 static unsigned char st[3000];
 char *sp=st,*spend;
 int stp=0;
 int width,height;
 FILE *fp;
 int x,y,xl,yl;
 int i,n,len,d,j;

 fp=fopen(fname,"rb");
 width=getw(fp); height=getw(fp);
 xl=cmx; yl=cmy;
 minx=xl; miny=yl;
 maxx=xl+width; maxy=yl+height;
 if(maxy>(getmaxy()-cmy)) {
  maxy=getmaxy()-cmy;
  height=maxy-yl;
 }
 getw(fp);
 y=yl-1;
 for(sp=st,n=0;n<height;n++) {
  stp=getw(fp);
  for(sp=st,spend=st+stp;sp<spend;) *sp++=getc(fp);
  sp=st; spend=sp+stp; x=xl; y++;
  while(sp<spend) {
   if(*((unsigned char *)sp)>0x80) {
    len=(*sp++) & 0x7f;
    if(!(*sp)) { x+=len; continue; }
    setcolor(*sp++);
    moveto(x,y);
    linerel(len,0);
    x+=len;
    continue;
   } else {
    len=*sp++;
    for(j=0;j<len;j++) putpixel(x++,y,*sp++);
    continue;
   }
  }
 }
 fclose(fp);
}
