

/*
guardare DAFARE
write_lut_image ??? quando ?

*/

#include "autoadjust.h"
#include "cleanupcommon.h"

#define TO_ABS(x)                                                              \
  {                                                                            \
    if ((x) < 0) (x) = -(x);                                                   \
  }

typedef struct big { UINT lo, hi; } BIG;
#define CLEAR_BIG(B) ((B).lo = 0, (B).hi = 0, (B))
#define ADD_BIG(B, X)                                                          \
  ((B).lo += (UINT)(X), (B).hi += (B).lo >> 30, (B).lo &= 0x3fffffff, (B))
#define ADD_BIG_BIG(B1, B2)                                                    \
  ((B1).lo += (B2).lo, (B1).hi += (B2).hi, (B1).hi += (B1).lo >> 30,           \
   (B1).lo &= 0x3fffffff, (B1))
#define BIG_TO_DOUBLE(B) ((double)(B).hi * (double)0x40000000 + (double)(B).lo)

static int Black = 0;

static int Ref_cum[256];
static int Ref_edgelen;

/*---------------------------------------------------------------------------*/

void autoadj_set_black_value(int black) { Black = black; }

/*===========================================================================*/

static void smooth_func256(float func[256], int rad) {
  int i, j, k;
  float smooth[256];

  for (i = 0; i < 256; i++) {
    k = i - rad;
    notLessThan(0, k);
    smooth[i] = func[k] / 2;
    k         = i + rad;
    notMoreThan(255, k);
    smooth[i] += func[k] / 2;
    for (j = i - rad + 1; j <= i + rad - 1; j++) {
      k = j;
      notLessThan(0, k);
      notMoreThan(255, k);
      smooth[i] += func[k];
    }
    smooth[i] /= 2 * rad;
  }
  for (i = 0; i < 256; i++) func[i] = smooth[i];
}

/*---------------------------------------------------------------------------*/

static void build_cum(int histo[256], int cum[256]) {
  int i;

  cum[0] = histo[0];
  for (i = 1; i < 256; i++) cum[i] = cum[i - 1] + histo[i];
}

/*===========================================================================*/

static int Window_x0, Window_y0, Window_x1, Window_y1;

void set_autoadjust_window(int x0, int y0, int x1, int y1) {
  Window_x0 = x0;
  Window_y0 = y0;
  Window_x1 = x1;
  Window_y1 = y1;
  if (Window_x0 > Window_x1) tswap(Window_x0, Window_x1);
  if (Window_y0 > Window_y1) tswap(Window_y0, Window_y1);
}

/*---------------------------------------------------------------------------*/

static void get_virtual_buffer(const TRasterImageP &image, int *p_lx, int *p_ly,
                               int *p_wrap, UCHAR **p_buffer) {
  int x0, y0, x1, y1;
  int x_margin, y_margin;
  int lx, ly, wrap;
  UCHAR *buffer;

  TRasterGR8P ras8(image->getRaster());

  assert(ras8);
  double xdpi, ydpi;
  image->getDpi(xdpi, ydpi);

  /* BORDO DI MEZZO CENTIMETRO */
  x_margin = troundp(mmToPixel(5.0, xdpi));
  y_margin = troundp(mmToPixel(5.0, ydpi));
  x0       = Window_x0 + x_margin;
  y0       = Window_y0 + y_margin;
  x1       = Window_x1 - x_margin;
  y1       = Window_y1 - y_margin;
  notLessThan(x0 + 9, x1);
  notLessThan(y0 + 9, y1);
  notLessThan(0, x0);
  notMoreThan(ras8->getLx() - 1, x0);
  notLessThan(0, y0);
  notMoreThan(ras8->getLy() - 1, y0);
  notLessThan(0, x1);
  notMoreThan(ras8->getLx() - 1, x1);
  notLessThan(0, y1);
  notMoreThan(ras8->getLy() - 1, y1);

  lx     = x1 - x0 + 1;
  ly     = y1 - y0 + 1;
  wrap   = ras8->getWrap();
  buffer = (UCHAR *)ras8->getRawData() + x0 + y0 * wrap;

  *p_lx     = lx;
  *p_ly     = ly;
  *p_wrap   = wrap;
  *p_buffer = buffer;
}

/*===========================================================================*/

void black_eq_algo(const TRasterImageP &image) {
  int lx, ly, wrap;
  int x, y, grey /*, width*/;
  int d_histo[256][256];
  int d, dd, m;
  UCHAR *buffer, *pix, *north, *south;
  UCHAR prev, darkest;
  long n;
  float mean_d[256];
  BIG s;
  int max_d_grey;
  float max_d;
  float fac;
  int val;
  UCHAR lut[256];
  image->getRaster()->lock();

  get_virtual_buffer(image, &lx, &ly, &wrap, &buffer);

  for (grey = 0; grey < 256; grey++) {
    for (d = 0; d < 256; d++) d_histo[grey][d] = 0;
  }
  for (y = 1; y < ly - 1; y++) {
    pix   = buffer + y * wrap + 1;
    north = pix + wrap;
    south = pix - wrap;
    for (x = 1; x < lx - 1; x++, pix++, north++, south++) {
      m = 2 * ((int)north[0] + (int)pix[-1] + 2 * (int)pix[0] + (int)pix[1] +
               (int)south[0]) +
          (int)north[-1] + (int)north[1] + (int)south[-1] + (int)south[1];
      m = (m + 8) >> 4;
      d = (int)north[-1] + (int)north[0] * 2 + (int)north[1] - (int)south[-1] -
          (int)south[0] * 2 - (int)south[1];
      TO_ABS(d)
      dd = (int)north[-1] + (int)pix[-1] * 2 + (int)south[-1] - (int)north[1] -
           (int)pix[1] * 2 - (int)south[1];
      TO_ABS(dd)
      if (dd > d) d = dd;
      dd = (int)pix[-1] + (int)north[-1] * 2 + (int)north[0] - (int)pix[1] -
           (int)south[1] * 2 - (int)south[0];
      TO_ABS(dd)
      if (dd > d) d = dd;
      dd = (int)pix[1] + (int)north[1] * 2 + (int)north[0] - (int)pix[-1] -
           (int)south[-1] * 2 - (int)south[0];
      TO_ABS(dd)
      if (dd > d) d = dd;
      d             = (d + 2) >> 2;
      d_histo[m][d]++;
    }
  }
  for (grey = 0; grey < 256; grey++) {
    n = 0;
    CLEAR_BIG(s);
    for (d = 0; d < 256; d++) {
      ADD_BIG(s, d_histo[grey][d] * d);
      n += d_histo[grey][d];
    }
    if (n)
      mean_d[grey] = (float)(BIG_TO_DOUBLE(s) / n);
    else
      mean_d[grey] = 0;
  }
  smooth_func256(mean_d, 5);
  max_d_grey = 0;
  max_d      = 0.0;
  for (grey = 0; grey < 256; grey++) {
    if (max_d < mean_d[grey]) {
      max_d      = mean_d[grey];
      max_d_grey = grey;
    }
  }

  n = 0;
  CLEAR_BIG(s);
  for (y = 0; y < ly; y++) {
    pix     = buffer + y * wrap;
    prev    = 255;
    darkest = 255;
    for (x = 0; x < lx; x++, pix++) {
      if (*pix < max_d_grey) {
        if (prev < max_d_grey)
          notMoreThan(*pix, darkest);
        else
          darkest = *pix;
      } else {
        if (prev < max_d_grey) {
          ADD_BIG(s, darkest);
          n++;
        }
      }
      prev = *pix;
    }
  }
  darkest = troundp(BIG_TO_DOUBLE(s) / n);

  /*
for (grey = 0; grey < darkest; grey++)
lut[grey] = 0;
fac = (float)255 / (float)(255 - darkest);
for (grey = darkest; grey < 256; grey++)
lut[grey] = 255 - troundp ((255 - grey) * fac);
*/
  notLessThan(0, Black);
  ;
  notMoreThan(255, Black);
  fac = (float)(255 - Black) / (float)(255 - darkest);
  for (grey = 0; grey < 256; grey++) {
    val = 255 - troundp((255 - grey) * fac);
    notLessThan(0, val);
    notMoreThan(255, val);
    lut[grey] = val;
  }

  apply_lut(image, lut);
  image->getRaster()->unlock();
}

/*===========================================================================*/

#define MAX_WIDTH 100

/*---------------------------------------------------------------------------*/

void build_lw(const TRasterImageP &image, float lw[256]) {
  int lx, ly, wrap;
  int x, y, grey, width;
  int d_histo[256][256];
  int d, dd;
  int lw_histo[256][MAX_WIDTH + 1];
  int x_start[256];
  static int *y_start[256];
  static int y_start_alloc = 0;
  UCHAR *buffer, *pix, *north, *south;
  UCHAR cur_grey, x_prev_grey, x_next_grey, y_prev_grey, y_next_grey;
  int x_grad, y_grad;
  long n;
  float mean_d[256];
  BIG s;
  int max_d_grey;
  float max_d;
  long max_d_n;

  get_virtual_buffer(image, &lx, &ly, &wrap, &buffer);

  if (y_start_alloc < lx) {
    for (grey = 0; grey < 256; grey++) {
      delete[] y_start[grey];
      y_start[grey] = new int[lx];
      // TREALLOC (y_start[grey], lx);
    }
    y_start_alloc = lx;
  }
  for (grey = 0; grey < 256; grey++) {
    for (d = 0; d < 256; d++) d_histo[grey][d] = 0;
    for (x = 0; x < lx; x++) y_start[grey][x] = -MAX_WIDTH - 1;
    for (width = 0; width <= MAX_WIDTH; width++) lw_histo[grey][width] = 0;
  }

  for (y = 1; y < ly - 1; y++) {
    for (grey = 0; grey < 256; grey++) x_start[grey] = -MAX_WIDTH - 1;
    pix                                              = buffer + y * wrap + 1;
    north                                            = pix + wrap;
    south                                            = pix - wrap;
    for (x = 1; x < lx - 1; x++, pix++, north++, south++) {
      d = (int)north[-1] + (int)north[0] * 2 + (int)north[1] - (int)south[-1] -
          (int)south[0] * 2 - (int)south[1];
      TO_ABS(d)
      dd = (int)north[-1] + (int)pix[-1] * 2 + (int)south[-1] - (int)north[1] -
           (int)pix[1] * 2 - (int)south[1];
      TO_ABS(dd)
      if (dd > d) d = dd;
      dd = (int)pix[-1] + (int)north[-1] * 2 + (int)north[0] - (int)pix[1] -
           (int)south[1] * 2 - (int)south[0];
      TO_ABS(dd)
      if (dd > d) d = dd;
      dd = (int)pix[1] + (int)north[1] * 2 + (int)north[0] - (int)pix[-1] -
           (int)south[-1] * 2 - (int)south[0];
      TO_ABS(dd)
      if (dd > d) d = dd;
      d >>= 3;
      d_histo[*pix][d]++;

      cur_grey    = pix[0];
      x_prev_grey = pix[-1];
      x_next_grey = pix[1];
      y_prev_grey = south[0];
      y_next_grey = north[0];
      x_grad      = (int)x_next_grey - (int)x_prev_grey;
      y_grad      = (int)y_next_grey - (int)y_prev_grey;
      if (cur_grey < x_prev_grey) {
        if (x_grad < 0 && x_grad < y_grad && x_grad < -y_grad)
          for (grey = cur_grey; grey < x_prev_grey; grey++) x_start[grey] = x;
        else
          for (grey       = cur_grey; grey < x_prev_grey; grey++)
            x_start[grey] = -MAX_WIDTH - 1;
      } else if (cur_grey > x_prev_grey) {
        if (x_grad > 0 && x_grad > y_grad && x_grad > -y_grad)
          for (grey = x_prev_grey; grey < cur_grey; grey++) {
            width = x - x_start[grey];
            if (width <= MAX_WIDTH) lw_histo[grey][width]++;
          }
      }
      if (cur_grey < y_prev_grey) {
        if (y_grad < 0 && y_grad < x_grad && y_grad < -x_grad)
          for (grey          = cur_grey; grey < y_prev_grey; grey++)
            y_start[grey][x] = y;
        else
          for (grey          = cur_grey; grey < y_prev_grey; grey++)
            y_start[grey][x] = -MAX_WIDTH - 1;
      } else if (cur_grey > y_prev_grey) {
        if (y_grad > 0 && y_grad > x_grad && y_grad > -x_grad)
          for (grey = y_prev_grey; grey < cur_grey; grey++) {
            width = y - y_start[grey][x];
            if (width <= MAX_WIDTH) lw_histo[grey][width]++;
          }
      }
    }
  }
  for (grey = 0; grey < 256; grey++) {
    n = 0;
    CLEAR_BIG(s);
    for (width = 0; width <= MAX_WIDTH; width++) {
      ADD_BIG(s, lw_histo[grey][width] * width);
      n += lw_histo[grey][width];
    }
    if (n)
      lw[grey] = (float)(BIG_TO_DOUBLE(s) / n);
    else
      lw[grey] = 0.0;
  }
  max_d_grey = 0;
  max_d      = 0.0;
  for (grey = 0; grey < 256; grey++) {
    n = 0;
    CLEAR_BIG(s);
    for (d = 0; d < 256; d++) {
      ADD_BIG(s, d_histo[grey][d] * d);
      n += d_histo[grey][d];
    }
    if (n)
      mean_d[grey] = (float)(BIG_TO_DOUBLE(s) / n);
    else
      mean_d[grey] = 0.0;
    if (max_d < mean_d[grey]) {
      max_d      = mean_d[grey];
      max_d_grey = grey;
    }
  }

  max_d_n = 0;
  for (width = 0; width <= MAX_WIDTH; width++)
    max_d_n += lw_histo[max_d_grey][width];
  for (grey = max_d_grey - 1; grey >= 0; grey--) {
    if (!lw[grey]) break;
    n = 0;
    for (width = 0; width <= MAX_WIDTH; width++) n += lw_histo[grey][width];
    if (n < max_d_n / 10) break;
  }
  for (; grey >= 0; grey--) lw[grey] = 0.0;
  for (grey = max_d_grey + 1; grey < 256; grey++) {
    if (!lw[grey]) break;
    if (255 - grey < (255 - max_d_grey) / 2) break;
    if (mean_d[grey] < max_d / 2.0) break;
  }
  for (; grey < 256; grey++) lw[grey] = 0.0;
}

/*---------------------------------------------------------------------------*/

void build_lw_lut(float ref_lw[256], float lw[256], UCHAR lut[256]) {
  /* crea una lut tale che l'immagine con il profilo di linewidths lw
venga mappata in un'immagine con il profilo di riferimento ref_lw.
Le lw sono non decrescenti e delimitate da 0.0 li' dove non sono valide
*/

  int i, j;
  float bot_ref_lw, top_ref_lw, bot_lw, top_lw;
  int bot_ref_gr, top_ref_gr, bot_gr, top_gr;
  float min_lw, max_lw;
  int min_ref_gr, max_ref_gr, min_gr, max_gr;
  float fac;

  for (i = 0; !ref_lw[i]; i++) {
  }
  bot_ref_lw = ref_lw[i];
  bot_ref_gr = i;
  for (i = 255; !ref_lw[i]; i--) {
  }
  top_ref_lw = ref_lw[i];
  top_ref_gr = i;
  for (i = 0; !lw[i]; i++) {
  }
  bot_lw = lw[i];
  bot_gr = i;
  for (i = 255; !lw[i]; i--) {
  }
  top_lw = lw[i];
  top_gr = i;

  min_lw = std::max(bot_ref_lw, bot_lw);
  max_lw = std::min(top_ref_lw, top_lw);

  if (min_lw >= max_lw) {
    for (i = 0; i < 256; i++) lut[i] = i;
    return;
  }

  for (i = bot_ref_gr; ref_lw[i] < min_lw; i++) {
  }
  min_ref_gr = i;
  for (i = top_ref_gr; ref_lw[i] > max_lw; i--) {
  }
  max_ref_gr = i;
  for (i = bot_gr; lw[i] < min_lw; i++) {
  }
  min_gr = i;
  for (i = top_gr; lw[i] > max_lw; i--) {
  }
  max_gr = i;

  j = min_ref_gr;
  for (i = min_gr; i <= max_gr; i++) {
    while (ref_lw[j] < lw[i] && j < max_ref_gr) j++;
    lut[i] = j;
  }
  fac = (float)min_ref_gr / (float)min_gr;
  for (i = 0; i < min_gr; i++) lut[i] = troundp(i * fac);
  fac = (float)(255 - max_ref_gr) / (float)(255 - max_gr);
  for (i = 255; i > max_gr; i--) lut[i] = 255 - troundp((255 - i) * fac);

  /***
printf ("-----\n\lut:\n\n");
for (i = 255; i >= 0; i--)
printf ("%4d :%4u\n", i, lut[i]);
printf ("\n");
***/
}

/*===========================================================================*/

void build_gr_cum(const TRasterImageP &image, int cum[256]) {
  int lx, ly, wrap, true_lx, true_ly;
  int i, x, y;
  UCHAR *pix, *buffer;
  int histo[256], raster_is_savebox;

  get_virtual_buffer(image, &lx, &ly, &wrap, &buffer);

  for (i = 0; i < 256; i++) histo[i] = 0;
  for (y = 0; y < ly; y++) {
    pix = buffer + y * wrap;
    for (x = 0; x < lx; x++) histo[*pix++]++;
  }

  raster_is_savebox = 1;
  TRect saveBox     = image->getSavebox();
  if ((saveBox.getLx() > 0 && saveBox.getLx() < image->getRaster()->getLx()) ||
      (saveBox.getLy() > 0 && saveBox.getLy() < image->getRaster()->getLy()))
    raster_is_savebox = 0;

  if (raster_is_savebox) {
    true_lx = saveBox.getLx() ? saveBox.getLx() : image->getRaster()->getLx();
    true_ly = saveBox.getLy() ? saveBox.getLy() : image->getRaster()->getLy();
  } else {
    true_lx = image->getRaster()->getLx();
    true_ly = image->getRaster()->getLy();
  }
  histo[255] += true_lx * true_ly - lx * ly;

  build_cum(histo, cum);
}

/*---------------------------------------------------------------------------*/

void build_gr_lut(int ref_cum[256], int cum[256], UCHAR lut[256]) {
  /* crea una lut tale che l'immagine con la distribuzione cumulativa cum
venga mappata in un'immagine con la cumulativa di riferimento ref_cum
*/
  int i, j, n;

  j = 0;
  for (i = 0; i < 256; i++) {
    n = cum[i];
    while (ref_cum[j] < n) j++;
    lut[i] = j;
  }
}

/*===========================================================================*/

struct edge_config {
  const char *str;
  int val;
};

static struct edge_config Edge_base[] = {
    {"     "
     "  X  "
     "     ",
     0},

    {"X    "
     "  X  "
     "     ",
     0},

    {"  X  "
     "  X  "
     "     ",
     0},

    {"X X  "
     "  X  "
     "     ",
     5},

    {"X   X"
     "  X  "
     "     ",
     20},

    {"X    "
     "  X X"
     "     ",
     24},

    {"X    "
     "  X  "
     "    X",
     28},

    {"  X  "
     "  X X"
     "     ",
     7},

    {"  X  "
     "  X  "
     "  X  ",
     20},

    {"X X X"
     "  X  "
     "     ",
     10},

    {"X X  "
     "  X X"
     "     ",
     12},

    {"X X  "
     "  X  "
     "    X",
     26},

    {"X X  "
     "  X  "
     "  X  ",
     22},

    {"X X  "
     "  X  "
     "X    ",
     22},

    {"X X  "
     "X X  "
     "     ",
     5},

    {"X   X"
     "  X  "
     "    X",
     34},

    {"X   X"
     "  X  "
     "  X  ",
     34},

    {"X    "
     "  X X"
     "  X  ",
     24},

    {"  X  "
     "  X X"
     "  X  ",
     10},

    {"X X X"
     "  X X"
     "     ",
     12},

    {"X X X"
     "  X  "
     "    X",
     24},

    {"X X X"
     "  X  "
     "  X  ",
     24},

    {"X X  "
     "  X X"
     "    X",
     14},

    {"X X  "
     "  X X"
     "  X  ",
     12},

    {"X X  "
     "  X X"
     "X    ",
     22},

    {"X X  "
     "X X X"
     "     ",
     10},

    {"X X  "
     "  X  "
     "  X X",
     24},

    {"X X  "
     "  X  "
     "X   X",
     32},

    {"X X  "
     "X X  "
     "    X",
     24},

    {"X X  "
     "  X  "
     "X X  ",
     20},

    {"X   X"
     "  X  "
     "X   X",
     40},

    {"  X  "
     "X X X"
     "  X  ",
     0},

    {"X X X"
     "X X X"
     "     ",
     10},

    {"X X X"
     "X X  "
     "    X",
     22},

    {"X X  "
     "X X X"
     "    X",
     12},

    {"X   X"
     "X X X"
     "    X",
     22},

    {"  X X"
     "X X X"
     "    X",
     12},

    {"X X X"
     "  X X"
     "    X",
     14},

    {"X X  "
     "X X X"
     "  X  ",
     0},

    {"X   X"
     "X X X"
     "  X  ",
     10},

    {"X   X"
     "X X  "
     "  X X",
     20},

    {"X   X"
     "X X  "
     "X   X",
     30},

    {"X X X"
     "X X X"
     "    X",
     12},

    {"X X X"
     "X X X"
     "  X  ",
     0},

    {"X X X"
     "X X  "
     "  X X",
     10},

    {"  X X"
     "X X X"
     "X X  ",
     0},

    {"X X X"
     "X X  "
     "X   X",
     20},

    {"X   X"
     "X X X"
     "X   X",
     20},

    {"X X X"
     "X X X"
     "  X X",
     0},

    {"X X X"
     "X X X"
     "X   X",
     10},

    {"X X X"
     "X X X"
     "X X X",
     0},

    {0, 0},
};

static int Edge_value[256];
static int Edge_init_done = 0;

static int edge_int(const char x[8]);
static void edge_rotate(char x[8]);
static void edge_mirror(char x[8]);

/*---------------------------------------------------------------------------*/

static void edge_init(void) {
  int b;
  const char *str;
  int val;
  char x[8];

  for (b = 0; Edge_base[b].str; b++) {
    str  = Edge_base[b].str;
    val  = Edge_base[b].val;
    x[0] = str[0];
    x[1] = str[2];
    x[2] = str[4];
    x[3] = str[5];
    x[4] = str[9];
    x[5] = str[10];
    x[6] = str[12];
    x[7] = str[14];

    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    edge_mirror(x);
    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    Edge_value[edge_int(x)] = val;
    edge_rotate(x);
    Edge_value[edge_int(x)] = val;
  }
  Edge_init_done = 1;
}

/*---------------------------------------------------------------------------*/

static int edge_int(const char x[8]) {
  return (x[0] != ' ') << 7 | (x[1] != ' ') << 6 | (x[2] != ' ') << 5 |
         (x[3] != ' ') << 4 | (x[4] != ' ') << 3 | (x[5] != ' ') << 2 |
         (x[6] != ' ') << 1 | (x[7] != ' ');
}

/*---------------------------------------------------------------------------*/

static void edge_rotate(char x[8]) {
  char tmp;

  tmp  = x[0];
  x[0] = x[2];
  x[2] = x[7];
  x[7] = x[5];
  x[5] = tmp;
  tmp  = x[1];
  x[1] = x[4];
  x[4] = x[6];
  x[6] = x[3];
  x[3] = tmp;
}

/*---------------------------------------------------------------------------*/

static void edge_mirror(char x[8]) {
  char tmp;

  tmp  = x[0];
  x[0] = x[2];
  x[2] = tmp;
  tmp  = x[3];
  x[3] = x[4];
  x[4] = tmp;
  tmp  = x[5];
  x[5] = x[7];
  x[7] = tmp;
}

/*---------------------------------------------------------------------------*/

void histo_l_algo(const TRasterImageP &image, int reference) {
  int lx, ly, wrap;
  int x, y, grey, ref_grey, m, d, dd;
  UCHAR *buffer, *pix, *north, *south;
  int g_histo[256];
  int d_histo[256][256];
  int cum[256];
  long n;
  BIG s;
  float mean_d[256];
  int max_d_grey;
  float max_d;
  int conf;
  int edgelen;
  float fac;
  UCHAR lut[256];

  if (!Edge_init_done) edge_init();

  get_virtual_buffer(image, &lx, &ly, &wrap, &buffer);

  for (grey = 0; grey < 256; grey++) {
    g_histo[grey] = 0;
    for (d = 0; d < 256; d++) d_histo[grey][d] = 0;
  }

  /* istogramma dei grigi e istogramma delle derivate */
  for (y = 1; y < ly - 1; y++) {
    pix   = buffer + y * wrap + 1;
    north = pix + wrap;
    south = pix - wrap;
    for (x = 1; x < lx - 1; x++, pix++, north++, south++) {
      m = 2 * ((int)north[0] + (int)pix[-1] + 2 * (int)pix[0] + (int)pix[1] +
               (int)south[0]) +
          (int)north[-1] + (int)north[1] + (int)south[-1] + (int)south[1];
      m = (m + 8) >> 4;
      d = (int)north[-1] + (int)north[0] * 2 + (int)north[1] - (int)south[-1] -
          (int)south[0] * 2 - (int)south[1];
      TO_ABS(d)
      dd = (int)north[-1] + (int)pix[-1] * 2 + (int)south[-1] - (int)north[1] -
           (int)pix[1] * 2 - (int)south[1];
      TO_ABS(dd)
      if (dd > d) d = dd;
      dd = (int)pix[-1] + (int)north[-1] * 2 + (int)north[0] - (int)pix[1] -
           (int)south[1] * 2 - (int)south[0];
      TO_ABS(dd)
      if (dd > d) d = dd;
      dd = (int)pix[1] + (int)north[1] * 2 + (int)north[0] - (int)pix[-1] -
           (int)south[-1] * 2 - (int)south[0];
      TO_ABS(dd)
      if (dd > d) d = dd;
      d             = (d + 2) >> 2;
      d_histo[m][d]++;
      g_histo[*pix]++;
    }
  }
  build_cum(g_histo, cum);

  /* costruzione pendenze medie */
  for (grey = 0; grey < 256; grey++) {
    n = 0;
    CLEAR_BIG(s);
    for (d = 0; d < 256; d++) {
      ADD_BIG(s, d_histo[grey][d] * d);
      n += d_histo[grey][d];
    }
    if (n)
      mean_d[grey] = (float)(BIG_TO_DOUBLE(s) / n);
    else
      mean_d[grey] = 0.0;
  }
  smooth_func256(mean_d, 5);

  /* determinazione grigio di massima pendenza */
  max_d_grey = 0;
  max_d      = 0.0;
  for (grey = 0; grey < 256; grey++) {
    if (max_d < mean_d[grey]) {
      max_d      = mean_d[grey];
      max_d_grey = grey;
    }
  }

  /* stima della lunghezza dei bordi */
  edgelen = 0;
  for (y = 1; y < ly - 1; y++) {
    pix   = buffer + y * wrap + 1;
    north = pix + wrap;
    south = pix - wrap;
    conf  = -1;
    for (x = 1; x < lx - 1; x++, pix++, north++, south++) {
      if (pix[0] <= max_d_grey) {
        if (conf >= 0) {
          conf = ((conf << 1) & ((1 << 7) | (1 << 6) | (1 << 2) | (1 << 1))) |
                 ((north[1] <= max_d_grey) << 5) | ((1) << 4) |
                 ((pix[1] <= max_d_grey) << 3) | ((south[1] <= max_d_grey));
        } else {
          conf =
              ((north[-1] <= max_d_grey) << 7) |
              ((north[0] <= max_d_grey) << 6) |
              ((north[1] <= max_d_grey) << 5) | ((pix[-1] <= max_d_grey) << 4) |
              ((pix[1] <= max_d_grey) << 3) | ((south[-1] <= max_d_grey) << 2) |
              ((south[0] <= max_d_grey) << 1) | ((south[1] <= max_d_grey));
        }
        edgelen += Edge_value[conf];
      } else
        conf = -1;
    }
  }

  if (reference) {
    for (grey = 0; grey < 256; grey++) Ref_cum[grey] = cum[grey];
    Ref_edgelen                                      = edgelen;

    return;
  }

  /* normalizza la cumulativa per il numero di linee */
  fac = (float)Ref_edgelen / edgelen;

  /***
printf ("fac: %f\n", fac);
printf ("cum prima:\n");
for (grey = 0; grey < 256; grey++)
printf ("%9d\n", cum[grey]);
***/

  for (grey = 0; grey < 255; grey++) {
    cum[grey] = (int)(cum[grey] * fac);
    notMoreThan(cum[255], cum[grey]);
  }

  /***
printf ("cum dopo:\n");
for (grey = 0; grey < 256; grey++)
printf ("%9d\n", cum[grey]);
printf ("Ref_cum:\n");
for (grey = 0; grey < 256; grey++)
printf ("%9d\n", Ref_cum[grey]);
***/

  /* equalizza l'istogramma */
  ref_grey = 0;
  for (grey = 0; grey < 255; grey++) {
    while (Ref_cum[ref_grey] < cum[grey]) ref_grey++;
    lut[grey] = ref_grey;
  }
  lut[255] = 255;
  /* DAFARE
if (Wl_flag)
write_lut_image (lut);
*/
  apply_lut(image, lut);
}

/*===========================================================================*/
/*===========================================================================*/

#define MAX_N_CHAINS 100

#define MAX_WIDTH 100

#define MIN_COUNT 100

#define MAX_HGREY (244 >> 1)

/*---------------------------------------------------------------------------*/
#ifdef DAFARE
static int build_th_histo(const TRasterImageP &image,
                          int histo[256 >> 1][MAX_WIDTH + 1]) {
  int lx, ly, wrap, x, y, hgrey, width;
  UCHAR *buffer, *cur, *x_prev, *y_prev, *x_next, *y_next;
  UCHAR cur_hgrey, x_prev_hgrey, y_prev_hgrey, x_next_hgrey, y_next_hgrey;
  int x_grad, y_grad;
  int hgrey_histo[256 >> 1];
  int x_start[256 >> 1];
  static int *y_start[256 >> 1];
  static int y_start_alloc = 0;

  get_virtual_buffer(image, &lx, &ly, &wrap, &buffer);

  if (y_start_alloc < lx) {
    for (hgrey = 0; hgrey<256>> 1; hgrey++) {
      delete[] y_start[hgrey];
      y_start[hgrey] = new int[lx];
      //    TREALLOC (y_start[hgrey], lx);
    }
    y_start_alloc = lx;
  }
  for (hgrey = 0; hgrey<256>> 1; hgrey++) {
    for (x = 0; x < lx; x++) y_start[hgrey][x] = -MAX_WIDTH - 1;
    for (width = 0; width <= MAX_WIDTH; width++) histo[hgrey][width] = 0;
    hgrey_histo[hgrey]                                               = 0;
  }

  for (y = 1; y < ly - 1; y++) {
    for (hgrey = 0; hgrey<256>> 1; hgrey++) x_start[hgrey] = -MAX_WIDTH - 1;
    cur    = buffer + y * wrap + 1;
    x_prev = cur - 1;
    y_prev = cur - wrap;
    x_next = cur + 1;
    y_next = cur + wrap;
    for (x = 1; x < lx - 1;
         x++, cur++, x_prev++, y_prev++, x_next++, y_next++) {
      cur_hgrey    = *cur >> 1;
      x_prev_hgrey = *x_prev >> 1;
      x_next_hgrey = *x_next >> 1;
      y_prev_hgrey = *y_prev >> 1;
      y_next_hgrey = *y_next >> 1;
      hgrey_histo[cur_hgrey]++;
      x_grad = (int)x_next_hgrey - (int)x_prev_hgrey;
      y_grad = (int)y_next_hgrey - (int)y_prev_hgrey;
      if (cur_hgrey < x_prev_hgrey) {
        if (x_grad < 0 && x_grad < y_grad && x_grad < -y_grad)
          for (hgrey       = cur_hgrey; hgrey < x_prev_hgrey; hgrey++)
            x_start[hgrey] = x;
        else
          for (hgrey       = cur_hgrey; hgrey < x_prev_hgrey; hgrey++)
            x_start[hgrey] = -MAX_WIDTH - 1;
      } else if (cur_hgrey > x_prev_hgrey) {
        if (x_grad > 0 && x_grad > y_grad && x_grad > -y_grad)
          for (hgrey = x_prev_hgrey; hgrey < cur_hgrey; hgrey++) {
            width = x - x_start[hgrey];
            if (width <= MAX_WIDTH) histo[hgrey][width]++;
          }
      }
      if (cur_hgrey < y_prev_hgrey) {
        if (y_grad < 0 && y_grad < x_grad && y_grad < -x_grad)
          for (hgrey          = cur_hgrey; hgrey < y_prev_hgrey; hgrey++)
            y_start[hgrey][x] = y;
        else
          for (hgrey          = cur_hgrey; hgrey < y_prev_hgrey; hgrey++)
            y_start[hgrey][x] = -MAX_WIDTH - 1;
      } else if (cur_hgrey > y_prev_hgrey) {
        if (y_grad > 0 && y_grad > x_grad && y_grad > -x_grad)
          for (hgrey = y_prev_hgrey; hgrey < cur_hgrey; hgrey++) {
            width = y - y_start[hgrey][x];
            if (width <= MAX_WIDTH) histo[hgrey][width]++;
          }
      }
    }
  }
  return 1;
}
#endif
/*---------------------------------------------------------------------------*/
#ifdef DAFARE
// SEMBRA NON ESSERE UTILIZZATO!
int eval_image_th(const TRasterImageP &image, int *threshold,
                  float *linewidth) {
#define MIN_COUNT 100
#define MAX_HGREY (244 >> 1)
  int histo[256 >> 1][MAX_WIDTH + 1];
  char is_max[256 >> 1][MAX_WIDTH + 1];
  int chain_map[256 >> 1][MAX_WIDTH + 1];
  int chain_n[MAX_N_CHAINS];
  int n_chains;
  int peak[256 >> 1];
  float mean_width[256 >> 1];
  int min_hgrey, max_hgrey, min_chain_hgrey, max_chain_hgrey;
  int hgrey, width, chain, c, last;
  long n, nx;
  float delta, min_delta;

  TRasterGR8P ras8(image->getRaster());

  if (!ras8) {
    TERROR("image is not RAS_GR8");
    return 0;
  }
  if (!(*threshold != 0 && *linewidth == 0 ||
        *threshold == 0 && *linewidth != 0))
    return 0;

  build_th_histo(image, histo);

  if (*threshold != 0) {
    min_hgrey = *threshold >> 1;
    max_hgrey = *threshold >> 1;
    notLessThan(0, min_hgrey);
    notMoreThan<int>(MAX_HGREY, max_hgrey);
  } else {
    for (hgrey = 0; hgrey<256>> 1; hgrey++)
      for (width = 1; width < MAX_WIDTH; width++)
        if (histo[hgrey][width] >= MIN_COUNT) goto endfor;
  endfor:
    if (hgrey == 256 >> 1) return 0;

    min_hgrey = hgrey;
    max_hgrey = MAX_HGREY;
  }
  if (min_hgrey > max_hgrey) return 0;

  /* costruisci la matrice dai massimi */
  for (hgrey = min_hgrey; hgrey <= max_hgrey; hgrey++) {
    is_max[hgrey][0]         = 0;
    is_max[hgrey][MAX_WIDTH] = 0;
    for (width = 1; width < MAX_WIDTH; width++) {
      is_max[hgrey][width] = histo[hgrey][width] > histo[hgrey][width - 1] &&
                             histo[hgrey][width] > histo[hgrey][width + 1] &&
                             histo[hgrey][width] >= MIN_COUNT;
    }
  }
  /* costruisci la matrice delle dorsali */
  n_chains = 0;
  for (width = 0; width <= MAX_WIDTH; width++) {
    if (is_max[min_hgrey][width] && n_chains < MAX_N_CHAINS) {
      chain_map[min_hgrey][width]          = n_chains++;
      chain_n[chain_map[min_hgrey][width]] = histo[min_hgrey][width];
    } else {
      chain_map[min_hgrey][width] = -1;
    }
  }
  for (hgrey = min_hgrey + 1; hgrey <= max_hgrey; hgrey++) {
    for (width = 0; width <= MAX_WIDTH; width++) {
      if (is_max[hgrey][width]) {
        if (chain_map[hgrey - 1][width] >= 0) {
          chain_map[hgrey][width] = chain_map[hgrey - 1][width];
          chain_n[chain_map[hgrey][width]] += histo[hgrey][width];
        } else if (chain_map[hgrey - 1][width - 1] >= 0 &&
                   chain_map[hgrey - 1][width - 1] !=
                       chain_map[hgrey][width - 2]) {
          chain_map[hgrey][width] = chain_map[hgrey - 1][width - 1];
          chain_n[chain_map[hgrey][width]] += histo[hgrey][width];
        } else if (chain_map[hgrey - 1][width + 1] >= 0) {
          chain_map[hgrey][width] = chain_map[hgrey - 1][width + 1];
          chain_n[chain_map[hgrey][width]] += histo[hgrey][width];
        } else if (n_chains < MAX_N_CHAINS) {
          chain_map[hgrey][width]          = n_chains++;
          chain_n[chain_map[hgrey][width]] = histo[hgrey][width];
        } else {
          chain_map[hgrey][width] = -1;
        }
      } else {
        chain_map[hgrey][width] = -1;
      }
    }
  }
  if (!n_chains) return 0;

  /* scegli la dorsale principale */
  chain = 0;
  for (c                                   = 1; c < n_chains; c++)
    if (chain_n[c] > chain_n[chain]) chain = c;

  /* costruisci il vettore dei massimi */
  for (hgrey = min_hgrey; hgrey <= max_hgrey; hgrey++)
    for (width = 1; width < MAX_WIDTH; width++)
      if (chain_map[hgrey][width] == chain) goto chain_start_found;
chain_start_found:
  min_chain_hgrey       = hgrey;
  peak[min_chain_hgrey] = width;
  for (hgrey = min_chain_hgrey + 1; hgrey <= max_hgrey; hgrey++) {
    if (chain_map[hgrey][width] == chain) {
    } else if (chain_map[hgrey][width + 1] == chain)
      width++;
    else if (chain_map[hgrey][width - 1] == chain)
      width--;
    else
      break;
    peak[hgrey] = width;
  }
  max_chain_hgrey = hgrey - 1;

  /* costruisci il vettore delle medie */
  for (hgrey = min_chain_hgrey; hgrey <= max_chain_hgrey; hgrey++) {
    width = peak[hgrey];
    n     = histo[hgrey][width];
    nx    = n * width;
    last  = n;
    for (width = peak[hgrey] - 1; width > 0; width--) {
      if (histo[hgrey][width] > last) break;
      n += histo[hgrey][width];
      nx += histo[hgrey][width] * width;
      last = histo[hgrey][width];
    }
    last = histo[hgrey][peak[hgrey]];
    for (width = peak[hgrey] + 1; width <= MAX_WIDTH; width++) {
      if (width > peak[hgrey] * 5) break;
      if (histo[hgrey][width] > last) break;
      n += histo[hgrey][width];
      nx += histo[hgrey][width] * width;
      last = histo[hgrey][width];
    }
    mean_width[hgrey] = (float)nx / (float)n;
  }

  if (*threshold != 0) {
    *linewidth = mean_width[*threshold >> 1];
  } else {
    min_delta = 123456789.0;
    for (hgrey = min_chain_hgrey; hgrey <= max_chain_hgrey; hgrey++) {
      delta                = mean_width[hgrey] - *linewidth;
      if (delta < 0) delta = -delta;
      if (delta < min_delta) {
        min_delta  = delta;
        *threshold = hgrey << 1;
      }
    }
  }

  return 1;
}
#endif
/*---------------------------------------------------------------------------*/
#ifdef DAFARE
// capire che cosa fa !?? chi la usa ?
// sembra prendere una gr8, modificare il buffer e cambiare il tipo in BW
void thresh_image(const TRasterImageP &image, int threshold,
                  int oversample_factor) {
  UCHAR *buffer, *cur, *xnext, *ynext, *xynext;
  UCHAR *bigline, *out;
  UCHAR tmp;
  int lx, ly, wrap, outwrap_bytes, outwrap_pix;
  int x, y, bit;
  UINT thresh, thresh_2, thresh_4;

  TRasterGR8P ras8(image->getRaster());

  assert(ras8);
  thresh        = threshold;
  thresh_2      = thresh * 2;
  thresh_4      = thresh * 4;
  buffer        = ras8->getRawData();
  lx            = ras8->getLx();
  ly            = ras8->getLy();
  wrap          = ras8->getWrap();
  outwrap_pix   = lx * oversample_factor;
  outwrap_bytes = (outwrap_pix + 7) / 8;
  TMALLOC(bigline, outwrap_bytes * oversample_factor)

  switch (oversample_factor) {
  case 1:
    for (y = 0; y < ly; y++) {
      cur = buffer + y * wrap;
      out = y == 0 ? bigline : buffer + y * outwrap_bytes;
      tmp = 0;
      bit = 7;
      for (x = 0; x < lx; x++, cur++) {
        if (*cur <= thresh) tmp |= 1 << bit;
        bit--;
        if (bit < 0) {
          *out++ = tmp;
          tmp    = 0;
          bit    = 7;
        }
      }
      if (bit != 7) *out++ = tmp;
      if (y == 0) memcpy(buffer, bigline, outwrap_bytes);
    }
    break;

  case 2:
    for (y = 0; y < ly; y++) {
      cur   = buffer + y * wrap;
      xnext = cur + 1;
      out   = y == 0 ? bigline : buffer + y * 2 * outwrap_bytes;
      tmp   = 0;
      bit   = 7;
      for (x = 0; x < lx; x++, cur++, xnext += x < lx - 1) {
        if (*cur <= thresh) tmp |= 1 << bit;
        bit--;
        if (*cur + *xnext <= thresh_2) tmp |= 1 << bit;
        bit--;
        if (bit < 0) {
          *out++ = tmp;
          tmp    = 0;
          bit    = 7;
        }
      }
      if (bit != 7) *out++ = tmp;

      cur    = buffer + y * wrap;
      xnext  = cur + 1;
      ynext  = y == ly - 1 ? cur : cur + wrap;
      xynext = ynext + 1;
      out    = y == 0 ? bigline + outwrap_bytes
                   : buffer + (y * 2 + 1) * outwrap_bytes;
      tmp = 0;
      bit = 7;
      for (x = 0; x < lx;
           x++, cur++, xnext += x < lx - 1, ynext++, xynext += x < lx - 1) {
        if (*cur + *ynext <= thresh_2) tmp |= 1 << bit;
        bit--;
        if (*cur + *xnext + *ynext + *xynext <= thresh_4) tmp |= 1 << bit;
        bit--;
        if (bit < 0) {
          *out++ = tmp;
          tmp    = 0;
          bit    = 7;
        }
      }
      if (bit != 7) *out++ = tmp;
      if (y == 0) memcpy(buffer, bigline, outwrap_bytes * 2);
    }
    break;

  default:
    assert(0);
  }
  image->ras.type = RAS_WB;
  image->ras.lx   = lx * oversample_factor;
  image->ras.ly   = ly * oversample_factor;
  image->ras.wrap = outwrap_pix;
  image->x_dpi *= oversample_factor;
  image->y_dpi *= oversample_factor;
  image->sb_x0 *= oversample_factor;
  image->sb_y0 *= oversample_factor;
  image->sb_or_img_lx *= oversample_factor;
  image->sb_or_img_ly *= oversample_factor;

  TFREE(bigline)
}
#endif
/*===========================================================================*/
/*===========================================================================*/

void apply_lut(const TRasterImageP &image, UCHAR lut[256]) {
  int x, y, lx, ly, wrap;
  UCHAR *buffer, *pix;

  TRasterGR8P ras8(image->getRaster());
  assert(ras8);

  lx   = ras8->getLx();
  ly   = ras8->getLy();
  wrap = ras8->getWrap();
  ras8->lock();
  buffer = (UCHAR *)ras8->getRawData();

  for (y = 0; y < ly; y++) {
    pix = buffer + y * wrap;
    for (x = 0; x < lx; x++, pix++) *pix = lut[*pix];
  }
  ras8->unlock();
}
