/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */
/*
 * medianfilter.c - perform a median filtering
 *
 * Author:      Raul Rivero
 *              Mathematics Dept.
 *              University of Oviedo
 * Date:        Sat Feb 8 1992
 * Copyright (c) 1992, Raul Rivero
 *
 */

#include <lug.h>
#include <lugfnts.h>

extern int LUGverbose;

medianfilter(inbitmap, outbitmap)
bitmap_hdr *inbitmap;
bitmap_hdr *outbitmap;
{

  if ( inbitmap->magic != LUGUSED )
    error( 19 );

  /* We need an RGB image */
  if ( inbitmap->depth < 24 && !isagrayscaled(inbitmap) )
    error( 7 );

  /* Fill new header */
  outbitmap->magic = LUGUSED;
  outbitmap->xsize = inbitmap->xsize;
  outbitmap->ysize = inbitmap->ysize;
  outbitmap->depth = inbitmap->depth;
  outbitmap->colors = inbitmap->colors;

  /* Perform the filter */
  VPRINTF(stderr, "Filtering R channel\n");
  outbitmap->r = (byte *) filternoise( inbitmap->r, outbitmap->xsize, outbitmap->ysize);
  if ( outbitmap->depth > 8 ) {
    VPRINTF(stderr, "Filtering G channel\n");
    outbitmap->g = (byte *) filternoise( inbitmap->g, outbitmap->xsize, outbitmap->ysize);
    VPRINTF(stderr, "Filtering B channel\n");
    outbitmap->b = (byte *) filternoise( inbitmap->b, outbitmap->xsize, outbitmap->ysize);
  }else {
    outbitmap->cmap = (byte *) Malloc( outbitmap->colors * 3 );
    bcopy( inbitmap->cmap, outbitmap->cmap, outbitmap->colors * 3 );
  }
}

byte *filternoise(buffer, xsize, ysize)
byte *buffer;
int xsize, ysize;
{
  register int i;
  byte *nbuffer;
  int totalsize = xsize * ysize;
  register byte *fin;
  register byte *base, *line_before;    /* pointers to new image */
  register byte *last, *next_line;      /* pointers to old image */

  /* Allocate memory for new image */
  nbuffer= (byte *) Malloc( totalsize );

  /*
   * First and last rows, and first and last pixels in
   * each row are not modified, so we copy image into new
   * buffer and keep all information.
   */
  bcopy(buffer, nbuffer, totalsize );

  /* Skip first and last row */
  ysize--;
  for ( i = 1; i < ysize; i++ ) {
    /* Skip first pixel of current line */
    base = nbuffer + i * xsize + 1;
    /* Point to curren line */
    last = buffer + i * xsize + 1;
    /* Point to line before */
    line_before= last - xsize;
    /* Point to next line  */
    next_line= last + xsize;
    /*
     * Pointer to last pixel to be modified of the current
     * line ( skip last pixel ).
     */
    fin= base + xsize - 1;
    while (base < fin) {
      *base++ = med3x3( line_before-1, last-1, next_line-1 );
      /* Update pinters */
      next_line++;
      line_before++;
      last++;
    }
  }

  return( nbuffer );
}


/*
 * Median Finding on a 3-by-3 Grid.
 *
 * Author:      Alan W. Paeth
 *              Computer Science Dept.
 *              University of Waterloo
 * Date:        ???
 * Copyright (c) 19??, Alan W. Paeth
 *
 * From Graphics Gems I.
 */

#define s2(a,b) {register int t; if ((t=b-a)<0) {a+=t; b-=t;}}
#define mn3(a,b,c) s2(a,b); s2(a,c);
#define mx3(a,b,c) s2(b,c); s2(a,c);
#define mnmx3(a,b,c) mx3(a,b,c); s2(a,b);
#define mnmx4(a,b,c,d) s2(a,b); s2(c,d); s2(a,c); s2(b,d);
#define mnmx5(a,b,c,d,e) s2(a,b); s2(c,d); mn3(a,c,e); mx3(b,d,e);
#define mnmx6(a,b,c,d,e,f) s2(a,d); s2(b,e); s2(c,f);\
                            mn3(a,b,c); mx3(d,e,f);

med3x3(b1, b2, b3)
byte *b1, *b2, *b3;
{
  register int r1, r2, r3, r4, r5, r6;

  r1 = *b1++; r2 = *b1++; r3 = *b1++;
  r4 = *b2++; r5 = *b2++; r6 = *b2++;
  mnmx6(r1, r2, r3, r4, r5, r6);
  r1 = *b3++;
  mnmx5(r1, r2, r3, r4, r5);
  r1 = *b3++;
  mnmx4(r1, r2, r3, r4);
  r1 = *b3++;
  mnmx3(r1, r2, r3);

  return r2;
}
