/*
 * raster.c - a rasterizer for outline data
 * by Hirotsugu Kakugawa
 *
 */
/*
 * Copyright (C) 1993-1997 Hirotsugu Kakugawa. 
 * All rights reserved.
 *
 * This file is part of the VFlib Library.  This library is free
 * software; you can redistribute it and/or modify it under the terms of
 * the GNU Library General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  This library is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#include <math.h>
#include "config.h"
#include "VFlib-3_6.h"
#include "VFsys.h"
#include "consts.h"
#include "bitmap.h"


Private VF_BITMAP  Vbm;
Private int        Vmax_width, Vmax_height;
Private VF_OUTLINE_ELEM  read_xy(int*,int*,VF_OUTLINE_ELEM**,VF_OUTLINE_ELEM);
Private void             draw_arc(int*,int*,void(*)());
Private void             draw_bezier(int*,int*,void(*)());
Private void             fill_edges(), trace_outline();

#ifndef M_PI
#  define M_PI     3.14159265358979323846
#endif
#define ARC_DIV    8
#define BEZ_DIV    8


static unsigned char  flip[] = {
  0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
static unsigned char bit_table[] = {
  0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};


/**
 **   VF_Outline2Bitmap
 **/
Public VF_BITMAP
VF_OutlineToBitmap(VF_OUTLINE outline, 
		   double dpi_x, double dpi_y, 
		   double point_size, double mag_x, double mag_y)
{
  vf_error = 0;
  return vf_outline_to_bitmap(outline, dpi_x, dpi_y,
			      point_size, mag_x, mag_y);
}

Glocal VF_BITMAP
vf_outline_to_bitmap(VF_OUTLINE outline, 
		     double dpi_x, double dpi_y, 
		     double point_size, double mag_x, double mag_y)
{
  double     bbx_w, bbx_h;
  VF_BITMAP  bm;

  if (outline == NULL)
    return NULL;

  bbx_w = mag_x * (double)outline[VF_OL_HEADER_INDEX_MAX_X] 
          / (double)outline[VF_OL_HEADER_INDEX_EM];
  bbx_h = mag_y * (double)outline[VF_OL_HEADER_INDEX_MAX_Y]
          / (double)outline[VF_OL_HEADER_INDEX_EM];
  if (point_size >= 0){
    bbx_w *= point_size;
    bbx_h *= point_size;
  } else {
    bbx_w *= VF_OL_HEADER_DECODE(outline[VF_OL_HEADER_INDEX_POINT_SIZE]);
    bbx_h *= VF_OL_HEADER_DECODE(outline[VF_OL_HEADER_INDEX_POINT_SIZE]);
  }
  if ((dpi_x >= 0) && (dpi_y >= 0)){
    bbx_w *= (dpi_x / 72.27);
    bbx_h *= (dpi_y / 72.27);
  } else {
    bbx_w *= (VF_OL_HEADER_DECODE(outline[VF_OL_HEADER_INDEX_DPI_X]) / 72.27);
    bbx_h *= (VF_OL_HEADER_DECODE(outline[VF_OL_HEADER_INDEX_DPI_Y]) / 72.27);
  }

  if ((bm = vf_alloc_bitmap((int)ceil(bbx_w), (int)ceil(bbx_h))) == NULL)
    return NULL;

  bm->off_x = toint(0 - bbx_w * (double)outline[VF_OL_HEADER_INDEX_REF_X] 
                    / (double)outline[VF_OL_HEADER_INDEX_MAX_X]);
  bm->off_y = toint(bbx_h * (double)outline[VF_OL_HEADER_INDEX_REF_Y] 
                    / (double)outline[VF_OL_HEADER_INDEX_MAX_Y]);
  bm->mv_x  = toint(bbx_w * (double)outline[VF_OL_HEADER_INDEX_MV_X] 
                    / (double)outline[VF_OL_HEADER_INDEX_MAX_X]);
  bm->mv_y  = toint(bbx_h * (double)outline[VF_OL_HEADER_INDEX_MV_Y] 
                    / (double)outline[VF_OL_HEADER_INDEX_MAX_Y]);
#if 0
  printf(">>DEV: %f %f %fpt\n",
	 dev_dpi_x, dev_dpi_y, point_size);
  printf(">>BBX: (%fx%f) \n", bbx_w, bbx_h);
  printf(">>BM BBX: (%dx%d) \n", bm->bbx_width, bm->bbx_height);
  printf(">>OFF: (%d,%d)  MV: (%d,%d)\n", 
    bm->off_x, bm->off_y, bm->mv_x, bm->mv_y);
#endif

  if (vf_draw_outline(bm, outline) < 0){
    vf_free_bitmap(bm);
    return NULL;
  }
  return bm;
}

Glocal int
vf_draw_outline(VF_BITMAP bm, VF_OUTLINE outline)
{
  int              x[4], y[4], f;
  VF_OUTLINE_ELEM  *olp, token;
  Private void    (*func_table[])() = {fill_edges, trace_outline};

  Vbm         = bm; 
  Vmax_width  = outline[VF_OL_HEADER_INDEX_MAX_X];
  Vmax_height = outline[VF_OL_HEADER_INDEX_MAX_Y];
  for (f = 0; f <= 1; f++){
    olp = &outline[VF_OL_OUTLINE_HEADER_SIZE_TYPE0];
    token = *(olp++);
    while (token != 0L){
      switch (token & (VF_OL_INSTR_LINE|VF_OL_INSTR_ARC|VF_OL_INSTR_BEZ)){
      case VF_OL_INSTR_LINE:
        token = read_xy(x, y, &olp, token);
        func_table[f](x[0], y[0], x[1], y[1]);
        break;
      case VF_OL_INSTR_ARC:
        token = read_xy(x, y, &olp, token);
        draw_arc(x, y, func_table[f]);
        break;
      case VF_OL_INSTR_BEZ:
        token = read_xy(x, y, &olp, token);
        draw_bezier(x, y, func_table[f]);
        break;
      default:
	fprintf(stderr, "VFlib: bogus outline data\n");
	vf_error = VF_ERR_BOGUS_OUTLINE;
        return -1;
      }
    }
  }
  return 0;
}

Private VF_OUTLINE_ELEM
read_xy(int *x, int *y, VF_OUTLINE_ELEM **vfpp, VF_OUTLINE_ELEM token)
{
  static int      xbeg, ybeg;

  if (((token & VF_OL_INSTR_TOKEN) != 0)
      && ((token & (VF_OL_INSTR_CWCURV|VF_OL_INSTR_CCWCURV)) != 0)){
    xbeg = VF_OL_GET_X(**vfpp) - VF_OL_COORD_OFFSET;
    ybeg = VF_OL_GET_Y(**vfpp) - VF_OL_COORD_OFFSET;
    token &= ~(VF_OL_INSTR_CWCURV|VF_OL_INSTR_CCWCURV);
  }

  switch (token & (VF_OL_INSTR_LINE|VF_OL_INSTR_ARC|VF_OL_INSTR_BEZ)){
  case VF_OL_INSTR_BEZ:
    *x++ = VF_OL_GET_X(**vfpp) - VF_OL_COORD_OFFSET;
    *y++ = VF_OL_GET_Y(**vfpp) - VF_OL_COORD_OFFSET;
    (*vfpp)++;
  case VF_OL_INSTR_ARC:
    *x++ = VF_OL_GET_X(**vfpp) - VF_OL_COORD_OFFSET;
    *y++ = VF_OL_GET_Y(**vfpp) - VF_OL_COORD_OFFSET;
    (*vfpp)++;
  case VF_OL_INSTR_LINE:
    *x++ = VF_OL_GET_X(**vfpp) - VF_OL_COORD_OFFSET;
    *y++ = VF_OL_GET_Y(**vfpp) - VF_OL_COORD_OFFSET;
    (*vfpp)++;
    break;
  default:
    fprintf(stderr, "VFlib: bogus outline data.\n");
    exit(1);
  }
  if ((**vfpp == 0) || (**vfpp & VF_OL_INSTR_TOKEN) != 0){
    token = **vfpp;
    (*vfpp)++;
    if (   (token == 0) 
	|| ((token & (VF_OL_INSTR_CWCURV|VF_OL_INSTR_CCWCURV)) != 0) ){
      *x = xbeg;
      *y = ybeg;
      return token;
    } else {
      ;  /* ??? */
    }
  }
  *x = VF_OL_GET_X(**vfpp) - VF_OL_COORD_OFFSET;
  *y = VF_OL_GET_Y(**vfpp) - VF_OL_COORD_OFFSET;
  return token;
}

Private void 
draw_arc(int *x, int *y, void (*draw_func)())
{
  double   dx1, dy1, dx3, dy3, cx, cy, z, r, ang, dang, ang_step;
  int      i;

  dx1 = x[0] - x[1];
  dy1 = y[0] - y[1];
  dx3 = x[2] - x[1];
  dy3 = y[2] - y[1];
  
  z = dx1*dy3 - dx3*dy1;
  if (z == 0){
    if((dx1 == dx3) && (dy1 == dy3)){
      cx = dx1 / 2.0;
      cy = dy1 / 2.0;
      r = sqrt(cx*cx + cy*cy);
      cx += x[1];  cy += y[1];
      ang = 0.0;
      dang = 2.0 * M_PI;
    } else {
      (*draw_func)(x[0], y[0], x[2], y[2]);
      return;
    }
  } else {
    cx = ((dx1*dx1 + dy1*dy1)*dy3 - (dx3*dx3 + dy3*dy3)*dy1) / z / 2.0;
    cy = - ((dx1*dx1 + dy1*dy1)*dx3 - (dx3*dx3 + dy3*dy3)*dx1) / z / 2.0;
    r = sqrt(cx*cx + cy*cy);
    ang = atan2(dy1-cy, dx1-cx);
    dang = atan2(dy3-cy, dx3-cx);
    if (z < 0){
      if (dang < ang) 
	dang += 2.0 * M_PI;
    } else {
      if (dang > ang) 
	dang -= 2.0 * M_PI;
    }
    dang -= ang;
    if(dang == 0.0){
      ang = 0.0;  
      dang = 2.0 * M_PI;
    }
    cx += x[1];  
    cy += y[1];
  }
  ang_step = dang / (double)ARC_DIV;
  dx1 = x[0];
  dy1 = y[0];
  ang += ang_step;
  for (i = 1; i < ARC_DIV; i++){
    dx3 = cx + r * cos(ang);
    dy3 = cy + r * sin(ang);
    (*draw_func)((int)dx1, (int)dy1, (int)dx3, (int)dy3);
    dx1 = dx3;
    dy1 = dy3;
    ang += ang_step;
  }
  dx3 = x[2];
  dy3 = y[2];
  (*draw_func)((int)dx1, (int)dy1, (int)dx3, (int)dy3);
}

Private void
draw_bezier(int *x, int* y, void (*draw_func)())
{
  int            i, xs, ys, xe, ye;
  static double  b[BEZ_DIV][4];
  static double  t = -1;

  if (t < 0){
    for (i = 0; i < BEZ_DIV; i++){
      t = (double)(i+1) / BEZ_DIV;
      b[i][0] = (1.0-t)*(1.0-t)*(1.0-t);
      b[i][1] = 3.0*t*(1.0-t)*(1.0-t);
      b[i][2] = 3.0*t*t*(1.0-t);
      b[i][3] = t*t*t;
    }
  }
  xs = x[0];
  ys = y[0];
  for (i = 0; i < BEZ_DIV; i++) {
    xe = b[i][0]*x[0] + b[i][1]*x[1] + b[i][2]*x[2] + b[i][3]*x[3];
    ye = b[i][0]*y[0] + b[i][1]*y[1] + b[i][2]*y[2] + b[i][3]*y[3];
    (*draw_func)((int)xs, (int)ys, (int)xe, (int)ye);
    xs = xe;
    ys = ye;
  }
}

Private void 
trace_outline(int x1, int y1, int x2, int y2)
{
  int     xx1, yy1, xx2, yy2;
  int     dxx, dyy, dxx2, dyy2, e, i, tmp;
  unsigned char        *bm_ptr, mask;
  
  xx1 = toint(((double)x1*Vbm->bbx_width)  / (double)Vmax_width);
  yy1 = toint(((double)y1*Vbm->bbx_height) / (double)Vmax_height);
  xx2 = toint(((double)x2*Vbm->bbx_width)  / (double)Vmax_width);
  yy2 = toint(((double)y2*Vbm->bbx_height) / (double)Vmax_height);
  if (xx1 < 0) xx1 = 0; 
  else if (xx1 >= Vbm->bbx_width)  xx1 = Vbm->bbx_width-1;
  if (yy1 < 0) yy1 = 0;
  else if (yy1 >= Vbm->bbx_height) yy1 = Vbm->bbx_height-1;
  if (xx2 < 0) xx2 = 0;
  else if (xx2 >= Vbm->bbx_width)  xx2 = Vbm->bbx_width-1;
  if (yy2 < 0) yy2 = 0; 
  else if (yy2 >= Vbm->bbx_height) yy2 = Vbm->bbx_height-1;
#if 0
  printf("LINE: (%d,%d)-(%d,%d)\n", xx1, yy1, xx2, yy2);
#endif

  if ((dyy = yy2 - yy1) < 0){
    tmp = xx1; xx1 = xx2; xx2 = tmp;
    tmp = yy1; yy1 = yy2; yy2 = tmp;
    dyy = -dyy;
  }
  dxx = xx2 - xx1;
  if ((dxx == 0) && (dyy == 0)){
    Vbm->bitmap[yy1*Vbm->raster + (xx1/0x08)] |= bit_table[xx1%0x08];
    return;
  }
  if (dxx < 0) 
    dxx = -dxx;

  bm_ptr = &Vbm->bitmap[yy1*Vbm->raster + (xx1/0x08)];
  mask = bit_table[xx1%0x08];
  dxx2 = 2*dxx;
  dyy2 = 2*dyy;
  if (dxx < dyy){
    e = dxx2 - dyy;
    if (xx1 < xx2){
      for (i = 0; i <= dyy; i++){
	*bm_ptr |= mask;
	while (e >= 0){
	  if ((mask >>= 1) == 0){
	    bm_ptr++;
	    mask = 0x80;
	  }
	  e -= dyy2;
	}
	bm_ptr += Vbm->raster;
	e += dxx2;
      }
    } else {
      for (i = 0; i <= dyy; i++) {
	*bm_ptr |= mask;
	while (e >= 0) {
	  if ((mask <<= 1) == 0) {
	    bm_ptr--;
	    mask = 0x1;
	  }
	  e -= dyy2;
	}
	bm_ptr += Vbm->raster;
	e += dxx2;
      }
    }
  } else {
    e = dyy2 - dxx;
    if (xx1 < xx2){
      for (i = 0; i <= dxx; i++){
	*bm_ptr |= mask;
	while (e >= 0) {
	  bm_ptr += Vbm->raster;
	  e -= dxx2;
	}
	if ((mask >>= 1) == 0) {
	  bm_ptr++;
	  mask = 0x80;
	}
	e += dyy2;
      }
    } else {
      for (i = 0; i <= dxx; i++){
	*bm_ptr |= mask;
	while (e >= 0) {
	  bm_ptr += Vbm->raster;
	  e -= dxx2;
	}
	if ((mask <<= 1) == 0){
	  bm_ptr--;
	  mask = 0x1;
	}
	e += dyy2;
      }
    }
  }
}

Private void 
fill_edges(int x1, int y1, int x2, int y2)
{
  int      xx1, yy1, xx2, yy2;
  int      dxx, dyy, dxx2, dyy2, sx, e, i, j, tmp;
  unsigned char *bm_ptr;
  int      bit, rbytes;
  
  xx1 = toint(((double)x1*Vbm->bbx_width)  / (double)Vmax_width);
  yy1 = toint(((double)y1*Vbm->bbx_height) / (double)Vmax_height);
  xx2 = toint(((double)x2*Vbm->bbx_width)  / (double)Vmax_width);
  yy2 = toint(((double)y2*Vbm->bbx_height) / (double)Vmax_height);
  if (xx1 < 0) xx1 = 0; 
  else if (xx1 >= Vbm->bbx_width)  xx1 = Vbm->bbx_width-1;
  if (yy1 < 0) yy1 = 0;
  else if (yy1 >= Vbm->bbx_height) yy1 = Vbm->bbx_height-1;
  if (xx2 < 0) xx2 = 0;
  else if (xx2 >= Vbm->bbx_width)  xx2 = Vbm->bbx_width-1;
  if (yy2 < 0) yy2 = 0; 
  else if (yy2 >= Vbm->bbx_height) yy2 = Vbm->bbx_height-1;

#if 0
  printf("LINE: (%d,%d)-(%d,%d)\n", xx1, yy1, xx2, yy2);
#endif

  if ((dyy = yy2-yy1) == 0) 
    return;
  if (dyy < 0){
    tmp = xx1; xx1 = xx2; xx2 = tmp;
    tmp = yy1; yy1 = yy2; yy2 = tmp;
    dyy = -dyy;
  }
  dxx = xx2 - xx1;
  sx = (dxx > 0) ? 1 : -1;
  if (dxx < 0) 
    dxx = -dxx;

  rbytes = Vbm->raster - (xx1/8) + 1;
  bit = xx1 % 8;
  dxx2 = 2*dxx;
  dyy2 = 2*dyy;
  e = dxx2-dyy;

  bm_ptr = &Vbm->bitmap[yy1*Vbm->raster + (xx1/8)];
  if ((xx1 == xx2) && (yy1 == yy2)){
    /* *bm_ptr |= bit_table[xx1%0x08];*/
    return;
  }

  for (i = 0; i < dyy; i++){
    bm_ptr[0] ^= flip[bit];
    for (j = 1; j <= rbytes; j++)
      bm_ptr[j] = ~bm_ptr[j];
    while (e >= 0){
      bit += sx;
      if (bit & 8){
	bm_ptr += sx;
	rbytes -= sx;
	bit &= 7;
      }
      e -= dyy2;
    }
    bm_ptr += Vbm->raster;
    e += dxx2;
  }
}


/*EOF*/
