
/******************************************************************************
* MODULE     : glyph_shrink.cpp
* DESCRIPTION: shrinking glyphs for anti-aliasing
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include "bitmap_font.hpp"
#include "ps_device.hpp"

static int
log2i (int i) {
  int l;
  if (i==1) return 0;
  for (l=1; l<30; l++)
    if (i <= (1 << l)) return l;
  fatal_error ("too large shrinking factor", "shrink", "shrink.cpp");
  return 0; // Because of bug in certain versions of g++
}

static int
my_div (int a, int b) {
  if (a>=0) return a/b;
  else return -((b-1-a)/b);
}

static int
my_mod (int a, int b) {
  if (a>=0) return a%b;
  else return b-1-((-1-a)%b);
}

static int
my_norm (int a, int m) {
  a= my_mod (a, m);
  if (a<=(m>>1)) return a;
  else return m-a;
}

int
get_hor_shift (glyph gl, int xfactor, int tx) {
  STACK_NEW_ARRAY (flag, bool, gl->width);

  // cout << "[";
  int x;
  for (x=0; x<gl->width; x++) {
    int max_count= 0, count=0, y;
    for (y=0; y<gl->height; y++)
      if (gl->get_1 (x,y)) count++;
      else {
	max_count = max (max_count, count);
	count     = 0;
      }
    max_count= max (max_count, count);
    flag[x]= (max_count>(gl->height>>1));
    // if (flag[x]) cout << "*"; else cout << " ";
  }
  // cout << "]   ";

  int first0=-1, first1=-1, last0=-1, last1=-1;
  for (x=0; x<gl->width; x++)
    if (flag[x]) {
      if (first0<0) first0= x;
      last0= x;
      while ((x<gl->width) && flag[x]) x++;
      if (first1<0) first1= x+ tx;
      last1= x+ tx;
      x--;
    }
  /* cout << first0 << ", " << first1 << ", "
     << last0  << ", " << last1  << "\n"; */

  STACK_DELETE_ARRAY (flag);

  if (first0==-1) return 0;
  if (first0==last0) return my_mod (gl->xoff- first0, xfactor);

  int first, last;
  int d00= my_norm (last0- first0, xfactor);
  int d01= my_norm (last1- first0, xfactor);
  int d10= my_norm (last0- first1, xfactor);
  int d11= my_norm (last1- first1, xfactor);
  if ((d00<=d01) && (d00<=d10) && (d00<=d11)) { first= first0; last= last0; }
  else if ((d01<=d10) && (d01<=d11)) { first= first0; last= last1; }
  else if (d10<=d11) { first= first1; last= last0; }
  else { first= first1; last= last1; }

  int middle, rest= my_mod (last- first, xfactor);
  if (rest <= (xfactor>>1)) middle= first+ (rest>>1);
  else middle= first- ((xfactor-rest)>>1);
  return my_mod (gl->xoff- middle, xfactor);
}

int
get_ver_shift (glyph gl, int yfactor, int ty) {
  STACK_NEW_ARRAY (flag, bool, gl->height);

  // cout << "[";
  int y;
  for (y=0; y<gl->height; y++) {
    int max_count= 0, count=0, x;
    for (x=0; x<gl->width; x++)
      if (gl->get_1 (x,y)) count++;
      else {
	max_count = max (max_count, count);
	count     = 0;
      }
    max_count= max (max_count, count);
    flag[y]= (max_count>(gl->width>>1));
    // if (flag[y]) cout << "*"; else cout << " ";
  }
  // cout << "]   ";

  int first0=-1, first1=-1, last0=-1, last1=-1;
  for (y=0; y<gl->height; y++)
    if (flag[y]) {
      if (first0<0) first0= y;
      last0= y;
      while ((y<gl->height) && flag[y]) y++;
      if (first1<0) first1= y+ ty;
      last1= y+ ty;
      y--;
    }
  /* cout << first0 << ", " << first1 << ", "
     << last0  << ", " << last1  << "\n"; */

  STACK_DELETE_ARRAY (flag);

  if (first0==-1) return 0;
  if (first0==last0)
    return my_mod (gl->height- gl->yoff- 1- first0, yfactor);

  int first, last;
  int d00= my_norm (last0- first0, yfactor);
  int d01= my_norm (last1- first0, yfactor);
  int d10= my_norm (last0- first1, yfactor);
  int d11= my_norm (last1- first1, yfactor);
  if ((d00<=d01) && (d00<=d10) && (d00<=d11)) { first= first0; last= last0; }
  else if ((d01<=d10) && (d01<=d11)) { first= first0; last= last1; }
  else if (d10<=d11) { first= first1; last= last0; }
  else { first= first1; last= last1; }

  int middle, rest= my_mod (last- first, yfactor);
  if (rest <= (yfactor>>1)) middle= first+ (rest>>1);
  else middle= first- ((yfactor-rest)>>1);
  return my_mod (gl->height- gl->yoff- 1- middle, yfactor);
}

glyph
shrink (glyph gl, int xfactor, int yfactor,
	int dx, int dy, int tx, int ty, SI& xo, SI& yo)
{
  /*
  cout << "------------------------------------------------------------------------------\n";
  cout << "Shift by " << dx << ", " << dy << "\n";
  cout << "------------------------------------------------------------------------------\n\n";
  cout << gl << "\n";
  */

  int x1= dx- gl->xoff;
  int x2= dx- gl->xoff+ gl->width+ tx;
  int X1= my_div (x1, xfactor);
  int X2= my_div (x2+xfactor-1, xfactor);

  int y1= dy+ gl->yoff+ 1- gl->height;
  int y2= dy+ gl->yoff+ 1+ ty;
  int Y1= my_div (y1, yfactor);
  int Y2= my_div (y2+yfactor-1, yfactor);

  int frac_x= (dx- gl->xoff- X1*xfactor);
  int frac_y= (dy+ gl->yoff- Y1*yfactor);
  SI  off_x = (((-X1) *xfactor+ dx)*PIXEL + ((tx*PIXEL)>>1))/xfactor;
  SI  off_y = (((Y2-1)*yfactor- dy)*PIXEL - ((ty*PIXEL)>>1))/yfactor;

  int i, j, x, y;
  int index, indey;
  int ww=(X2-X1)*xfactor, hh=(Y2-Y1)*yfactor;
  STACK_NEW_ARRAY (bitmap, int, ww*hh);
  for (i=0; i<ww*hh; i++) bitmap[i]=0;
  for (y=0, index= ww*frac_y+ frac_x; y<gl->height; y++, index-=ww)
    for (x=0; x<gl->width; x++)
      for (j=0, indey=ww*ty; j<=ty; j++, indey-=ww)
	for (i=0; i<=tx; i++) {
	  int entry= index+indey+x+i;
	  int value= gl->get_1(x,y);
	  if (value>bitmap[entry]) bitmap[entry]= value;
	}

  int X, Y, sum, nr= xfactor*yfactor;
  int new_depth= gl->depth+ log2i (nr);
  if (new_depth > 8) new_depth= 8;
  glyph CB (X2-X1, Y2-Y1, -X1, Y2-1, new_depth, gl->status);
  for (Y=Y1; Y<Y2; Y++)
    for (X=X1; X<X2; X++) {
      sum=0;
      indey= ((Y-Y1)*ww+ (X-X1))*xfactor;
      for (j=0, index= indey; j<yfactor; j++, index+=ww)
	for (i=0; i<xfactor; i++)
	  sum += bitmap[index+ i];
      if (nr >= 64) sum= (64 * sum) / nr;
      CB->set (X, Y, sum);
    }
  xo= off_x;
  yo= off_y;
  STACK_DELETE_ARRAY (bitmap);

  // cout << CB << "\n";
  return CB;
}

glyph
shrink (glyph gl, int xfactor, int yfactor, SI& xo, SI& yo) {
  if ((gl->width==0) || (gl->height==0))
    fatal_error ("zero size character", "shrink", "glyph.cpp");

  int tx= xfactor/3;
  int ty= yfactor/3;
  int dx=0, dy=0;
  if ((gl->status==0) && (xfactor>1)) dx= get_hor_shift (gl, xfactor, tx);
  // if ((gl->status==0) && (yfactor>1)) dy= get_ver_shift (gl, yfactor, ty);

  glyph ret= shrink (gl, xfactor, yfactor, dx, dy, tx, ty, xo, yo);
  if (ret->status != 0) {
    if (ret->status&1) ret->adjust_top ();
    if (ret->status&2) ret->adjust_bot ();
    ret->yoff= yo= 0;
  }
  return ret;
}
