/* TTF to BMP module */ 
/* Written by Lin YawJen (LYJ) 1994 */ 
/* edited by Wu LiangSheng (LSN) 1994 */ 
/* stolen by Yu-Chung Wang 1994 and modified for use with ttf2pk */
 
/* 
Input: TTF Font File 
Output: font_buf 1 bit per pixel array 
*/ 
 
#include <stdio.h> 
#include <math.h> 
#include <stdlib.h> 
#include <string.h> 
/* #include <malloc.h>  */
/* #include <memory.h>  */
#include <time.h> 
#include "ttf.h"
#include "font.h" 
#include "ttf2bmp.h"
 
#define __fastcall 
#define __max(a,b) ( a >= b ? a : b ) 
#define _kbhit kbhit 
#define _getch getch 

#define MAXXSIZE 4800
#define MAXYSIZE 4800

#define BOOL short 
#define FALSE 0 
#define TRUE 1 
#define EM 1024 /* It is better to get em from file */ 
 
typedef short fixp; /* May use long to prevent overflow */ 
/* Fix point : S_############.## bit, range +-4096 with 1/4 precision */ 
 
struct SCANLINE 
{ 
  short max, cur; 
  fixp *grid; 
  unsigned char *color;
} *rowarray, *colarray; 
 
/* drawdot and hline mask */ 
unsigned char mask_pixel1[8] = {0x80,0x40,0x20,0x10,8,4,2,1}; 
unsigned char mask_from1[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1}; 
unsigned char mask_to1[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; 

unsigned char mask_pixel2[4] = {0xa0,0x30,0x0a,0x03}; 
unsigned char mask_from2[4] = {0xff,0x3f,0x0f,0x03}; 
unsigned char mask_to2[4] = {0xc0,0xf0,0xfc,0xff}; 

unsigned char mask_pixel4[2] = {0xf0,0x0f}; 
unsigned char mask_from4[2] = {0xff,0x0f}; 
unsigned char mask_to4[2] = {0xf0,0xff}; 


 
void RENDER_Init(); 
void clear_data(); 
 
 
 
void RENDER_Init() /* Initial the data space */ 
{ 
  short i; 

  rowarray = (struct SCANLINE *)calloc(sizeof(struct SCANLINE),MAXYSIZE); 
  if (rowarray == NULL) 
  { 
    printf("Memory error when initial.\n"); 
    exit(1); 
  } 
  colarray = (struct SCANLINE *)calloc(sizeof(struct SCANLINE),MAXXSIZE); 
  if (colarray == NULL) 
  { 
    printf("Memory error when initial.\n"); 
    exit(1); 
  } 
  for (i = 0; i < MAXYSIZE; i++) 
  { 
    rowarray[i].max = 32;  /* from max of NTU_MM */ 
    rowarray[i].cur = 0; 
    rowarray[i].grid = (fixp *)calloc(sizeof(fixp),32); 
    rowarray[i].color = (unsigned char *)calloc(1,32); 
    if (rowarray[i].grid == NULL) 
    { 
      printf("Memory overflow when init rowarray.\n"); 
      exit(2); 
    } 
  } 
  for (i = 0; i < MAXXSIZE; i++) 
  { 
    colarray[i].max = 32; 
    colarray[i].cur = 0; 
    colarray[i].grid = (fixp *)calloc(sizeof(fixp),32); 
    colarray[i].color = (unsigned char *)calloc(1,32); 
    if (colarray[i].grid == NULL) 
    { 
      printf("Memory overflow when init colarray.\n"); 
      exit(2); 
    } 
  } 
} 

/* We must free all arrays and allocate new ones when we change the
   resolution. */ 
 
void clear_data(BITMAP *bit) 
{ 
  unsigned short i; 

  for (i = 0; i < BITMAP_ypoint(bit); i++) rowarray[i].cur = 0; 
  for (i = 0; i < BITMAP_xpoint(bit); i++) colarray[i].cur = 0; 
  memset(bit->map,0,BITMAP_len(bit)); 
} 
 
 
fixp lastx, lasty; 
 
#define Moveto(x, y)  {lastx = (x); lasty = (y);} 
void Lineto(fixp x, fixp y,unsigned char c); 
void Curveto(fixp x, fixp y,unsigned short ctrl_fst,unsigned short ctrl_last); 
void SplitCurve(fixp x1, fixp y1, fixp x2, fixp y2); /* Modify */ 
void Draw(BITMAP *); 
LONG *xcoor,*ycoor;
USHORT *epts_ctr;
BYTE *flags;
int num_ctr;
 
void convert(Font *f,BITMAP *bit) 
{ 
  short i; 
  unsigned short j, start_offpt, end_offpt=0, fst; 

  clear_data(bit);
  xcoor = f->glyf->xCoordinates;
  ycoor = f->glyf->yCoordinates;
  epts_ctr = f->glyf->endPtsOfContours;
  flags = f->glyf->flags;
  num_ctr = f->glyf->numberOfContours;

  for (i = 0, j = 0; i < num_ctr; i++) 
  { 
    fst = j; 
    Moveto(xcoor[j],ycoor[j]); 
    start_offpt = 0; /*start at least 1*/ 
    for (j++; j <= epts_ctr[i]; j++) 
    { 
      if (!(flags[j]&1)) 
      { 
        /*Off curve*/ 
        if (!start_offpt) 
          start_offpt = end_offpt = j;
        else 
          end_offpt++; 
      } 
      else
      { 
        /*On Curve*/ 
        if (start_offpt) 
        { 
          Curveto(xcoor[j],ycoor[j],start_offpt,end_offpt); 
          start_offpt = 0; 
        } 
        else 
          Lineto(xcoor[j],ycoor[j],1); 
      } 
    } 
    if (start_offpt) 
      Curveto(xcoor[fst],ycoor[fst],start_offpt,end_offpt); 
    else 
      Lineto(xcoor[fst],ycoor[fst],1); 
  } 
  Draw(bit); 
} 

void Lineto(fixp x, fixp y,unsigned char c) 
{ 
  short start, end; 
  long delta, sy, sx; /* 10 bit point */ 
  fixp *tmp; 

  if (x != lastx ) /* usual apply under 96X96 */ 
  { 
    /* calculate the slope of line */
    delta = ((long)(y-lasty))*1024/((long)(x-lastx)); 
    if (x < lastx) 
    { 
      /******************************************************/
      /* round up, the coordinates here is 14DF2            */
      /* (4*3+1)/4 to (4*3+4)/4 will round to 4 for example */
      /******************************************************/
      start = (x+3)/4; 
      end = (lastx+3)/4; 
      sy = ((long)y)*256 + (((long)start)*4-((long)x))*delta/4; 
    } 
    else
    { 
      start = (lastx+3)/4; 
      end = (x+3)/4; 
      sy = ((long)lasty)*256 + ((long)(start*4-lastx))*delta/4; 
    } 
    if (start < 0) 
    { 
      /* trunce to x = 0 */
      sy += delta*(-start); 
      start = 0;
    } 
    if (end >= MAXXSIZE) 
      end = MAXXSIZE-1; 
    /* add = for hack */
    for (; start < end; start++, sy += delta) 
    { 
      /* xadd(start,(fixp)(sy/256));*/ 
      if (colarray[start].cur == colarray[start].max) 
      {
        /* Growth 8 each time */ 
        tmp = (fixp *)realloc(colarray[start].grid, 
              (colarray[start].max+8)*sizeof(fixp)); 
        if (tmp == NULL) { printf("Memory over.\n"); continue; } 
        colarray[start].grid = tmp; 
        colarray[start].max += 8; 
      } 
      /************************************************/
      /* we scale first with 1024 but we calculate in */
      /* F14.2 so divide by 4                         */
      /*   1024/4 = 256                               */
      /************************************************/
      colarray[start].grid[colarray[start].cur] = (fixp)(sy/256); 
      colarray[start].color[colarray[start].cur++] = c; 
    } 
  } 

  if (y != lasty) 
  { 
    delta = ((long)(x-lastx))*1024/((long)(y-lasty)); 
    if (y < lasty) 
    { 
      start = (y+3)/4; 
      end = (lasty+3)/4; 
      sx = ((long)x)*256 + (((long)start)*4-((long)y))*delta/4; 
    } 
    else
    { 
      start = (lasty+3)/4; 
      end = (y+3)/4; 
      sx = ((long)lastx)*256 + ((long)(start*4-lasty))*delta/4; 
    } 
    if (start < 0) 
    { 
      sx += delta*(-start); 
      start = 0;
    } 
    if (end >= MAXYSIZE) 
      end = MAXYSIZE-1; 
    /* add = for hack */
    for (; start < end; start++, sx += delta) 
    { 
      /* yadd(start,(fixp)(sx/256));*/ 
      if (rowarray[start].cur == rowarray[start].max) 
      { 
        /* Growth 8 each time */ 
        tmp = (fixp *)realloc(rowarray[start].grid, 
              (rowarray[start].max+8)*sizeof(fixp)); 
        if (tmp == NULL) { printf("Memory over.\n"); continue; } 
        rowarray[start].grid = tmp; 
        rowarray[start].max += 8; 
      } 
      rowarray[start].grid[rowarray[start].cur] = (fixp)(sx/256); 
      rowarray[start].color[rowarray[start].cur++] = c; 
    } 
  } 

  lastx = x; lasty = y; 
} 
 
void Curveto(fixp x, fixp y,unsigned short ctrl_fst,unsigned short ctrl_last) 
{ 
  unsigned short ctrl_next = ctrl_fst+1; 
  fixp _x = xcoor[ctrl_fst], _y = ycoor[ctrl_fst]; 
  fixp tx, ty; 

  Lineto((lastx+_x)/2, (lasty+_y)/2,1); /*Uniform B-spline*/ 

  for (; ctrl_fst <= ctrl_last; ctrl_fst++, ctrl_next++) 
  { 
    if (ctrl_next <= ctrl_last) 
    { 
      tx = xcoor[ctrl_next]; 
      ty = ycoor[ctrl_next];
    } 
    else 
    { 
      tx = x;
      ty = y; 
    } 
    
    SplitCurve(_x, _y, (_x+tx)/2, (_y+ty)/2); 
    _x = tx; 
    _y = ty; 
  }
  
  Lineto(x, y,1); 
  
} 
 
void SplitCurve(fixp x1, fixp y1, fixp x2, fixp y2) /* Great Modify */ 
{ 
  /*Delta routine for split nonuniform part of B-spline*/ 
  short n, i, m; 
  long p1x = ((long)lastx-2*(long)x1+(long)x2) << 8; 
  long p1y = ((long)lasty-2*(long)y1+(long)y2) << 8; 
  long ddx, ddy, p2x, p2y, fx, fy, dx, dy; 
  long ratio = __max(labs(p1x),labs(p1y)); /* 10 bit point */ 
  
  for (n = 0; ratio >= 512L; n++) ratio >>= 2; /* Compute coef. of spline */ 
  if (n) 
  {                                       /* delta = 1/2^n */ 
    p2x = ((long)x1 - (long)lastx) << 9; 
    p2y = ((long)y1 - (long)lasty) << 9; 
    ddx = ((p1x*2) >> (2*n)); /* 2 order */ 
    ddy = ((p1y*2) >> (2*n)); 
    dx = (p2x >> n)+(p1x >> (2*n)); 
    dy = (p2y >> n)+(p1y >> (2*n)); 
    fx = dx + ((long)lastx << 8); 
    fy = dy + ((long)lasty << 8); 
    for (m = (1 << n), i = 1; i < m; i++) 
    { 
      Lineto((fixp)(fx/256),(fixp)(fy/256),10+i); 
      dx += ddx; fx += dx; 
      dy += ddy; fy += dy; 
    } 
  } 
  Lineto(x2,y2,3); 
} 
 
short ycur; 
 
/* These two functions use data coordinates */ 
void Pixel(BITMAP *,short x, short y); 
void Hline(BITMAP *,short x1, short x2);  /* Hline use ycur as y */ 


void Draw1(BITMAP *bit)
{
  
  int i,j;
  
  for(i=0;i<bit->ysize;i++)
  {
    if (!rowarray[i].cur) continue;
    ycur = (bit->ysize-i-1)*bit->xsize;
    for(j=0;j<rowarray[i].cur;j++)
    bit->map[ycur+(rowarray[i].grid[j]+3)/4] = rowarray[i].color[j];
  }
  for(i=0;i<bit->xpoint;i++)
  {
    if (!colarray[i].cur) continue;
    for(j=0;j<colarray[i].cur;j++)
    {
      ycur = (bit->ysize-(colarray[i].grid[j]+3)/4-1)*bit->xsize;
      bit->map[ycur+j] = rowarray[i].color[j];
    }
  }
}



void Draw(BITMAP *bit) 
{ 
  unsigned short i;
  short j, k, cnt, len; /* Modify */ 
  fixp *tmp, t; 
  
  for (i = 0; i < bit->ysize; i++) 
  { 
    if (!rowarray[i].cur) continue; 
    ycur = (bit->ysize-i-1)*bit->xsize; 
    tmp = rowarray[i].grid; 
    cnt = rowarray[i].cur; 
    for (j = 1; j < cnt; j++) /* Insert sort for average length 6*/ 
    { 
      for (t = tmp[j], k = j-1; (k >= 0) && (tmp[k] > t); k--) 
        tmp[k+1] = tmp[k]; 
      if (k < j-1) tmp[k+1] = t; 
    } 
    cnt &= 0xfffe; /* must be paired */ 
    for (j = 0; j < cnt; j+=2) 
    { 

      /* Scan convert rule 3, usual applying under 96X96 */ 
      
      len = (tmp[j+1]-tmp[j]+2)/4; /* 0.5 */ 
#if 0
      /* a hack, don't use the above rounding */
      len = (tmp[j+1]-tmp[j])/4; /* 0.5 */ 
#endif
      if (len < 2) Pixel(bit,(tmp[j]+3)/4,bit->ysize-i-1); /* (*) */ 
      else Hline(bit,(tmp[j]+3)/4,(tmp[j+1]+3)/4); 
      /* (*) uses another way to mimic the old version */ 

    }    /* Scan convert rule 0 & 1 */ 
  } 
#if 0
  for (i = 0; i < bit->xpoint; i++) 
  { 
    /* Only check rule 3, can be skipped when res > 96 */ 
    if (!colarray[i].cur) continue; 
    tmp = colarray[i].grid; 
    cnt = colarray[i].cur; 
    for (j = 1; j < cnt; j++) /* Insert sort for average length 6*/ 
    { 
      for (t = tmp[j], k = j-1; (k >= 0) && (tmp[k] > t); k--) 
        tmp[k+1] = tmp[k]; 
      if (k < j-1) tmp[k+1] = t; 
    } 
    cnt &= 0xfffe; /* must be paired */ 
    for (j = 0; j < cnt; j+=2) /* Modify */ 
      if (tmp[j+1]-tmp[j] < 6) Pixel(bit,i,bit->ysize-1-(tmp[j])/4); 
      /* Scan convert rule 3, usually appling under 96X96 */ 
  } 
#endif
} 

void __fastcall Pixel(BITMAP *bit,short x, short y) 
{ 
  if ((y < 0) || ((USHORT)y >= bit->ysize) || (x < 0) || (x >= MAXXSIZE)) 
    return; 
  switch(bit->mode)
  {
  case BITMAP_ONE_BIT:
    bit->map[y*bit->xsize+x/8] |= mask_pixel1[x&7]; 
    break;
  case BITMAP_TWO_BIT:
    bit->map[y*bit->xsize+x/4] |= (mask_pixel2[x&3]&bit->color);
    break;
  case BITMAP_FOUR_BIT:
    bit->map[y*bit->xsize+x/2] |= (mask_pixel4[x&2]&bit->color);
    break;
  case BITMAP_ONE_BYTE:
    bit->map[y*bit->xsize+x] = bit->color;
    break;
  case BITMAP_TWO_BYTES:
    ((USHORT *) bit->map)[y*bit->xsize/2+x] = bit->color;
    break;
  case BITMAP_THREE_BYTES:
    *((USHORT *) &(bit->map[y*bit->xsize+x*3])) = bit->color;
    bit->map[y*bit->xsize+x*3+2] = bit->color>>16;
    break;
  case BITMAP_FOUR_BYTES:
    ((USHORT *) bit->map)[y*bit->xsize/4+x] = bit->color;
    break;
  }
} 
 
void Hline(BITMAP *bit,short x1, short x2) 
{ 
  short _x1, _x2; 
  unsigned char *ptr,*font_buf; 
  
  font_buf = bit->map;
  if (x1 < 0) 
  { 
    if (x2 < 0) return; 
    else if (x2 >= MAXXSIZE) { x1 = 0; x2 = MAXXSIZE;} 
    else x1 = 0; 
  } 
  else if (x1 >= MAXXSIZE) return; 
  else if (x2 >= MAXXSIZE) x2 = MAXXSIZE; 
  switch(bit->mode)
  {
  case BITMAP_ONE_BIT:
    _x1 = x1/8; _x2 = x2/8; 
    ptr = font_buf+ycur+_x1; 
    if (_x1 == _x2) 
      *ptr |= (mask_from1[x1&7] & mask_to1[x2&7]); 
    else 
    { 
      *ptr++ |= mask_from1[x1&7]; 
      for (_x1++; _x1 < _x2; _x1++) *ptr++ = 0xff; 
      *ptr |= mask_to1[x2&7]; 
    } 
    break;
  case BITMAP_TWO_BIT:
    _x1 = x1/4; _x2 = x2/4; 
    ptr = font_buf+ycur+_x1; 
    if (_x1 == _x2) 
      *ptr |= ((mask_from2[x1&3] & mask_to2[x2&3])&bit->color); 
    else 
    { 
      *ptr++ |= (mask_from2[x1&3]&bit->color); 
      for (_x1++; _x1 < _x2; _x1++) *ptr++ = bit->color; 
      *ptr |= (mask_to2[x2&3]&bit->color); 
    } 
    break;
  case BITMAP_FOUR_BIT:
    _x1 = x1/2; _x2 = x2/2; 
    ptr = font_buf+ycur+_x1; 
    if (_x1 == _x2) 
      *ptr |= ((mask_from2[x1&1] & mask_to2[x2&1])&bit->color); 
    else 
    { 
      *ptr++ |= (mask_from2[x1&1]&bit->color); 
      for (_x1++; _x1 < _x2; _x1++) *ptr++ = bit->color; 
      *ptr |= (mask_to2[x2&1]&bit->color); 
    } 
    break;
  case BITMAP_ONE_BYTE:
    for(ptr = font_buf+ycur+x1;ptr<=font_buf+ycur+x2;ptr++)
      *ptr = bit->color;

    *(font_buf+ycur+x1) = bit->color+1;
    *(font_buf+ycur+x2) = bit->color+1;
    
    break;
  case BITMAP_TWO_BYTES:
    for(ptr = font_buf+ycur+x1*2;ptr<=font_buf+ycur+x2*2;ptr+=2)
      *(USHORT *) ptr = bit->color;
    break;
  case BITMAP_THREE_BYTES:
    for(ptr = font_buf+ycur+x1*3;ptr<=font_buf+ycur+x2*3;ptr+=3)
    {
      *(USHORT *)ptr = bit->color;
      ptr[2] = bit->color>>16;
    }
    break;
  case BITMAP_FOUR_BYTES:
    for(ptr = font_buf+ycur+x1*4;ptr<=font_buf+ycur+x2*4;ptr+=4)
      *(ULONG *)ptr = bit->color;
    break;
  }
} 
