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

#include "SDL.h"
#include "SDL_image.h"

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    int rmask  = 0xff000000;
    int gmask  = 0x00ff0000;
    int bmask  = 0x0000ff00;
    int amask  = 0x000000ff;
#define RMASK 0xff000000;
#define GMASK 0x00ff0000;
#define BMASK 0x0000ff00;
#define AMASK 0x000000ff;
#defibe RSHIFT 24
#define GSHIFT 16
#define BSHIFT 8
#define ASHIFT 0

#else
    int rmask = 0x000000ff;
    int gmask = 0x0000ff00;
    int bmask = 0x00ff0000;
    int amask = 0xff000000;
#define RMASK 0x000000ff;
#define GMASK 0x0000ff00;
#define BMASK 0x00ff0000;
#define AMASK 0xff000000;
#define RSHIFT 0
#define GSHIFT 8
#define BSHIFT 16
#define ASHIFT 24
#endif

int edge_filter[3][3]={{-1,-1,-1},{-1,8,-1},{-1,-1,-1}};
int emboss_filter[3][3]={{0,0,0},{0,1,0},{0,0,-1}};
int sharp_filter[3][3]={{-1,-1,-1},{-1,9,-1},{-1,-1,-1}};


void edge_detect(int width, int height, Uint32 *old, Uint32 *new) {
   
    int x,y,k,l,r,g,b;
    Uint32 color;
    int sumr,sumg,sumb;
   
    for(x=1;x<width-1;x++) {
       for(y=1;y<height-1;y++) {
		  
	  sumr=0;
	  sumb=0;
	  sumg=0;
		  
	  for(k=-1;k<2;k++) {
	     for(l=-1;l<2;l++) {
		color=old[((y+l)*width)+(x+k)];
		r=(color>>RSHIFT)&0xff;
		g=(color>>GSHIFT)&0xff;
		b=(color>>BSHIFT)&0xff;
		sumr+=r*edge_filter[k+1][l+1];
		sumg+=g*edge_filter[k+1][l+1];
		sumb+=b*edge_filter[k+1][l+1];
	     }
	  }
	  sumr/=8;
	  sumg/=8;
	  sumb/=8;

	  if (sumr<0) sumr=0;
	  if (sumb<0) sumb=0;
	  if (sumg<0) sumg=0;

	  if (sumr>255) sumr=255;
	  if (sumb>255) sumb=255;
	  if (sumg>255) sumg=255;
		  
	  color=((sumr&0xff)<<RSHIFT)+
	        ((sumg&0xff)<<GSHIFT)+
	        ((sumb&0xff)<<BSHIFT)+
		(0xff<<ASHIFT);
	  new[(y*width)+x]=color;
       }
    }
}

void sharp(int width, int height, Uint32 *old, Uint32 *new) {
   
    int x,y,k,l,r,g,b;
    Uint32 color;
    int sumr,sumg,sumb;
   
    for(x=1;x<width-1;x++) {
       for(y=1;y<height-1;y++) {
	  sumr=0;
	  sumb=0;
	  sumg=0;
		  
	  for(k=-1;k<2;k++) {
	     for(l=-1;l<2;l++) {
		color=old[((y+l)*width)+(x+k)];
		r=(color>>RSHIFT)&0xff;
		g=(color>>GSHIFT)&0xff;
	        b=(color>>BSHIFT)&0xff;
		sumr+=r*sharp_filter[k+1][l+1];
		sumg+=g*sharp_filter[k+1][l+1];
		sumb+=b*sharp_filter[k+1][l+1];
	     }
	  }
	  if (sumr>255) sumr=255;
	  if (sumg>255) sumg=255;
	  if (sumb>255) sumb=255;
		  
	  if (sumr<0) sumr=0;
	  if (sumg<0) sumg=0;
	  if (sumb<0) sumb=0;

	  color=((sumr&0xff)<<RSHIFT)+
	        ((sumg&0xff)<<GSHIFT)+
	        ((sumb&0xff)<<BSHIFT)+
		(0xff<<ASHIFT);
	  new[(y*width)+x]=color;
       }
    }
}

void emboss(int width, int height, Uint32 *old, Uint32 *new) {
   
    int x,y,k,l,r,g,b;
    Uint32 color;
    int sumr,sumg,sumb;
   
    for(x=1;x<width-1;x++) {
       for(y=1;y<height-1;y++) {

	  sumr=0;
	  sumb=0;
	  sumg=0;

	  for(k=-1;k<2;k++) {
	     for(l=-1;l<2;l++) {
		color=old[((y+l)*width)+(x+k)];
		r=(color>>RSHIFT)&0xff;
		g=(color>>GSHIFT)&0xff;
		b=(color>>BSHIFT)&0xff;
	        sumr+=r*emboss_filter[k+1][l+1];
		sumg+=g*emboss_filter[k+1][l+1];
		sumb+=b*emboss_filter[k+1][l+1];
	     }
	  }

	  if (sumr<0) sumr=0;
	  if (sumb<0) sumb=0;
	  if (sumg<0) sumg=0;

	  if (sumr>255) sumr=255;
	  if (sumb>255) sumb=255;
	  if (sumg>255) sumg=255;
		  
	  color=((sumr&0xff)<<RSHIFT)+
	        ((sumg&0xff)<<GSHIFT)+
	        ((sumb&0xff)<<BSHIFT)+
		 (0xff<<ASHIFT);
	  new[(y*width)+x]=color;
       }
    }
}

int main(int argc, char **argv) {
   
    SDL_Surface *screen,*image,*new_image,*new_image2;
    int depth,done,flags,normal=1;
    SDL_Event event;
   
   
       /* Check command line usage */
    if (argc<2) {
       fprintf(stderr,"Usage: %s image_file\n",argv[0]);
       return -1;
    }
   
       /* Initialize SDL */
    if (SDL_Init(SDL_INIT_VIDEO)< 0) {
       fprintf(stderr, "Could not initialize SDL: %s\n",SDL_GetError());
       return -1;
    }
   
    flags = SDL_SWSURFACE;
   
    image = IMG_Load(argv[1]);
    if (image==NULL) {
       fprintf(stderr, "Could not load %s: %s\n",argv[1], SDL_GetError());
       return -1;
    }
   
    printf("Loaded image %d by %d\n",image->w,image->h);
    printf("Pixel format: %d bpp\n",image->format->BitsPerPixel);
   
    printf("Converting to 32 bpp\n");
    new_image2=SDL_CreateRGBSurface(flags, image->w, image->h,
				    32, rmask,gmask,bmask,amask);
   
    new_image=SDL_ConvertSurface(image, new_image2->format, flags);
   
    printf("Loaded image %d by %d\n",new_image->w,new_image2->h);
    printf("Pixel format: %d bpp\n",new_image->format->BitsPerPixel);
   
    SDL_WM_SetCaption(argv[1],"convolve");
   
       /* Create a display for the image */
    depth = SDL_VideoModeOK(new_image->w, new_image->h, 32, flags);
   
    if (depth==0) {
       if (image->format->BytesPerPixel>1) {
	  depth = 32;
       } else {
	  fprintf(stderr,"We don't suport 8bpp mode!\n");
	  return -1;
       }
    }
   
    screen = SDL_SetVideoMode(new_image->w, new_image->h, depth, flags);
    if (screen==NULL) {
       fprintf(stderr,"Couldn't set %dx%dx%d video mode: %s\n",
		      new_image->w, new_image->h,
		      depth, SDL_GetError());
       return -1;
    }
   
       /* Display the image */
    SDL_BlitSurface(new_image, NULL, screen, NULL);
    SDL_UpdateRect(screen, 0, 0, 0, 0);
   
    done = 0;
    while (!done ) {
       if (SDL_PollEvent(&event)) {
	  switch (event.type) {
	     case SDL_KEYUP:
		  switch (event.key.keysym.sym) {
		     case SDLK_ESCAPE:
		     case SDLK_q:
			  done=1;
			  break;
						    
		     case SDLK_e:
			  edge_detect(new_image->w,new_image->h,
				      new_image->pixels,new_image2->pixels);
			  /* Display the image */
			  normal=0;
			  break;
		     case SDLK_s:
			  sharp(new_image->w,new_image->h,
				new_image->pixels,new_image2->pixels);
			  /* Display the image */
			  normal=0;
			  break;
		     case SDLK_m:
			  emboss(new_image->w,new_image->h,
				 new_image->pixels,new_image2->pixels);
			  /* Display the image */
			  normal=0;
			  break;
		     case SDLK_n:
		     case SDLK_SPACE:
		     default:
			  normal=1;
			  break;
		  }
		  break;
	     case SDL_QUIT:
		  done = 1;
		  break;
	     default:
		  break;
	  }
	  if (normal) {
	     SDL_BlitSurface(image, NULL, screen, NULL);
	  }
	  else {
	     SDL_BlitSurface(new_image2, NULL, screen, NULL);
	  }
	  SDL_UpdateRect(screen, 0, 0, 0, 0);		    
       } else {
	  SDL_Delay(10);
       }
    }
   
    SDL_FreeSurface(new_image);
   
    SDL_Quit();
    return 0;
}
