


#include "threshold.h"

#define THRESH_IMAGE(IMTYPE)                                                           \
  {                                                                                    \
    IMTYPE im = (IMTYPE) img;                                                          \
    typeof (*(im->data)) scan, end;                                                    \
                                                                                       \
    if (two_thresh) {                                                                  \
      typeof (**(im->data)) tmp;                                                       \
      typeof (*(im->data))  xend;                                                      \
                                                                                       \
      /* Note:  carefully skip borders */                                              \
                                                                                       \
      scan  = imGetStore(im) + width + 1;                                              \
      end   = imGetStore(im) + imGetStoreLen(im) - width - 1;                          \
      rscan = imGetStore(result) + width + 1;                                          \
                                                                                       \
      for ( ; scan < end; scan += 2, rscan += 2) {                                     \
        for (xend = scan + width - 2; scan < xend; scan++, rscan++) {                  \
          if ((tmp = *scan) >= thresh2) {    /* definitely */                          \
            *rscan = 1;                                                                \
            *(stktop++) = rscan;                                                       \
          }                                                                            \
          else if (tmp >= thresh1) {	     /* maybe */                               \
            *rscan = 2;                                                                \
          }                                                                            \
          else {                             /* nope */                                \
            *rscan = 0;                                                                \
          }                                                                            \
        }                                                                              \
      }                                                                                \
                                                                                       \
      /* now for the hysteresis: flood fill 1's into 2's */                            \
      while (stktop > stack) {		                                               \
	rscan = *--stktop;                                                             \
                                                                                       \
        /* look at neighbors. if a neighbor is 2 make it 1 and add to stack */         \
        /* continue until stack empty */                                               \
                                                                                       \
	if (*(rscan -= width + 1) == 2)      /* upper-left */                          \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(++rscan) == 2)                 /* upper-middle */                        \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(++rscan) == 2)                 /* upper-right */                         \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(rscan += width) == 2)          /* middle-right */                        \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(rscan -= 2) == 2)              /* middle-left */                         \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(rscan += width) == 2)          /* lower-left */                          \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(++rscan) == 2)                 /* lower-middle */                        \
	  *rscan = 1, *(stktop++) = rscan;                                             \
	if (*(++rscan) == 2)                 /* lower-right */                         \
	  *rscan = 1, *(stktop++) = rscan;                                             \
      }                                                                                \
                                                                                       \
      rscan = imGetStore(result);                                                      \
      rend  = rscan + imGetStoreLen(result);                                           \
      for ( ; rscan < rend; rscan++) {                                                 \
	if (*rscan == 2) {                                                             \
	  *rscan = 0;                                                                  \
	}                                                                              \
      }                                                                                \
    }                                                                                  \
    else {                                                                             \
      scan  = imGetStore(im);                                                          \
      end   = scan + imGetStoreLen(im);                                                \
      rscan = imGetStore(result);                                                      \
                                                                                       \
      for ( ; scan < end; scan++, rscan++) {                                           \
	*rscan = (*scan >= thresh1);                                                   \
      }                                                                                \
    }                                                                                  \
  }
				       

BinaryImage thresh_Image_(AnyImage img, float thresh1, float thresh2)
{
  BinaryImage result;
  char *rscan, *rend;
  char **stack = (char **) NULL, **stktop = (char **) NULL;
  boolean two_thresh;
  int width;
  extern int isnan(double x);

  if (img == (AnyImage) NULL) {
    return (BinaryImage) NULL;
  }

  width = imGetWidth(img);

  two_thresh = !isnan(thresh2);
  if (two_thresh) {
    float tmp = MAX(thresh1, thresh2);

    thresh1 = MIN(thresh1, thresh2);
    thresh2 = tmp;
  }

  if ((result = (BinaryImage) imNewOffset(IMAGE_BINARY,
					  imGetWidth(img), imGetHeight(img),
					  imGetXBase(img), imGetYBase(img))) == NULL) {
    return (BinaryImage) NULL;
  }

  if (two_thresh) {
    if ((stack = (char **) malloc(imGetWidth(img) * imGetHeight(img) *
				  sizeof(char *))) == NULL) {
      return (BinaryImage) NULL;
    }

    stktop = stack;
  }
  
  switch (imGetType(img)) {
  case IMAGE_GRAY:    THRESH_IMAGE(GrayImage);     break;    
  case IMAGE_FLOAT:   THRESH_IMAGE(FloatImage);    break; 
  case IMAGE_DOUBLE:  THRESH_IMAGE(DoubleImage);   break; 
  case IMAGE_SHORT:   THRESH_IMAGE(ShortImage);    break; 
  case IMAGE_LONG:    THRESH_IMAGE(LongImage);     break; 


  default:
    return (BinaryImage) NULL;
  }
  
  if (two_thresh)
    free(stack);

  return result;
}

#undef THRESH_IMAGE



#define MUTATE_IMAGE(IMTYPE, ZERO)                                                     \
  {                                                                                    \
    IMTYPE im = (IMTYPE) img;                                                          \
    typeof (*(im->data)) scan, end;                                                    \
    char *bscan;                                                                       \
                                                                                       \
    scan  = imGetStore(im);                                                            \
    end   = scan + imGetStoreLen(im);                                                  \
    bscan = imGetStore(bim);                                                           \
                                                                                       \
    for ( ; scan < end; scan++, bscan++) {                                             \
      if (*bscan == 0) {                                                               \
        *scan = ZERO;                                                                  \
      }                                                                                \
    }                                                                                  \
  }


int thresh_ImageMutate_(AnyImage img, float thresh1, float thresh2)
{
  BinaryImage bim;


  if ((bim = thresh_Image_(img, thresh1, thresh2)) == NULL) {
    return -1;
  }

  switch (imGetType(img)) {
  case IMAGE_GRAY:    MUTATE_IMAGE(GrayImage,   ((uchar) 0));     break;    
  case IMAGE_FLOAT:   MUTATE_IMAGE(FloatImage,  0.0);             break; 
  case IMAGE_DOUBLE:  MUTATE_IMAGE(DoubleImage, 0.0);             break; 
  case IMAGE_SHORT:   MUTATE_IMAGE(ShortImage,  0);               break; 
  case IMAGE_LONG:    MUTATE_IMAGE(LongImage,   0);               break; 


  default:
    return -1;
  }

  imFree(bim);  

  return 0;
}

#undef MUTATE_IMAGE

