/*
 * VF_BitOp.c
 *
 *  Programmmed by Hirotsugu Kakugawa, Hiroshima University
 *  E-Mail:  h.kakugawa@computer.org
 *
 *  Edition History
 *  22 Jan 1994  
 *  26 Jan 1994  
 *   8 Mar 1994  Removed dead code
 *  16 Mar 1995  Added NOP operation
 *
 */


/* This file is part of VFlib
 *
 * Copyright (C) 1994-1998 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.
 */


/*
  Capabilities interpretable by bitop font objects::
  "op"  (str)   --  Sequence of operations.  
     Operation List::
      (x, y, z must be one of S, D, R, which are SRC, DEST, REG, resp.)
      Nx   : Reverse register x.              (x := not x) 
      Cx   : Clear register x.                (x <== 0) 
      Exy  : Exchange register x and y.       (x <==> y) 
      Lxy  : Load register x with y.          (x := y)
      Axyz : AND operation.                   (x := y & z)
      Oxyz : OR operation.                    (x := y | z)
      Xxyz : XOR operation.                   (x := y xor z)
      Hxy  : Half-tone operation.             (x := half y)
      Bxnnnn : Get bitmap of char nnnn (hexadecimal of 4 digits) of
               subfont specified by `sf' capability on register x.
                                              (x <= Char nnnn of subfont)
      SPACE, TAB, NEWLINE:  Non-operation. 

  "fo"  (str)   --  Font entry name which is `operated' by this class.
  "sf"  (str)   --  Subfont entry name for Bnnnn operation.
 */


#include  <stdio.h> 
#include  <stdlib.h> 
#include  <string.h> 
#include  <ctype.h> 
#include  <fcntl.h> 
#include  "config.h"
#include  "defs.h"
#include  "_VF.h"
#include  "VF.h"
#include  "VFcap.h"

struct s_font {
  int     fd; 
  char   *Operations;    /* op */
  char   *FontEntry;     /* fo */
  int    FontId;         /* fid for `fo' */
  char   *SubFontEntry;  /* sf */
  int    SubFontId;      /* fid for `sf' */
};
typedef struct s_font  Font;


Private int    OpenFont();
Private int    CloseFont();
Private int    GetBitmap();
Private long*  GetOutline();
Private long*  GetOutline2();
Private int    DrawOutline();
Private int    FreeOutline();
Private int    Link();
Private int    Unlink();

Private int  ReadCapa();
Private int  BitOperations();

Private int            HexCh2Int();
Private unsigned char  *Reg();



Public FontObj*
CreateFont_BitOp(ent)
  char *ent;
{
  Font    *font;
  FontObj *fobj;

  if ((font = (Font*) malloc(sizeof(Font))) == NULL)
    return NULL;  /* ERR: malloc err */
  font->fd = -1;
  if (ReadCapa(font, ent) < 0){
    free(font);
    return NULL;
  }

  fobj = (FontObj*) malloc(sizeof(FontObj));
  fobj->ClassID     = VF_FONT_BITOP;
  fobj->Self        = fobj;
  fobj->LinkCount   = 0;
  fobj->OpenFont    = OpenFont;
  fobj->CloseFont   = CloseFont;
  fobj->GetBitmap   = GetBitmap;
  fobj->GetOutline  = GetOutline;
  fobj->GetOutline2 = GetOutline2;
  fobj->DrawOutline = DrawOutline;
  fobj->FreeOutline = FreeOutline;
  fobj->GetCharSet  = NULL;
  fobj->GetEnc      = NULL;
  fobj->Link        = Link;
  fobj->Unlink      = Unlink;
  fobj->Locals      = (long) font;
  return fobj;
}
       

Private int
OpenFont(obj)
  FontObj* obj;
{
  Font  *font;

  font = (Font*) obj->Locals;
  if (font->FontEntry == NULL)
    return -1;  /* no font entry name */ 
  if ((font->FontId = VF_OpenFont(font->FontEntry)) < 0)
    return -1;
  return 0;
}


Private int
CloseFont(obj, fid)
  FontObj  *obj;
  int      fid;
{
  Font  *font;

  font = (Font*) obj->Locals;
  if (font->Operations != NULL)
    free(font->Operations);
  if (font->FontEntry != NULL)
    free(font->FontEntry);
  if (font->FontId != -1)
    VF_CloseFont(font->FontId);
  return 0;
}

Private int
GetBitmap(obj, jiscode, w, h, bw, bo, bm_buf)
  FontObj  *obj;
  int   jiscode, w, h, bw, bo;
  char  *bm_buf;
{
  Font  *font;
  
  font = (Font*) obj->Locals;
  if (font->FontId == -1)
    return -1;
  return BitOperations(0, obj, jiscode, (long*)NULL, w, h, bw, bo, bm_buf);
}


Private long*
GetOutline(obj, jiscode)
  FontObj  *obj;
  int      jiscode;
{
  long  *outline;
  Font  *font;
  
  font = (Font*) obj->Locals;
  if (font->FontId == -1)
    return NULL;
  outline = VF_GetOutline(jiscode, font->FontId);
  return outline;
}


Private long*
GetOutline2(obj, jiscode)
  FontObj  *obj;
  int      jiscode;
{
  long  *outline;
  Font  *font;
  
  font = (Font*) obj->Locals;
  if (font->FontId == -1)
    return NULL;
  outline = VF_GetOutline2(jiscode, font->FontId);
  return outline;
}


Private int
DrawOutline(obj, vfdata, w, h, bw, bo, bm_buf)
  FontObj  *obj;
  long     *vfdata;
  int   w;
  int   h;
  int   bw;
  int   bo;
  char  *bm_buf;
{
  Font  *font;
  
  font = (Font*) obj->Locals;
  if (font->FontId == -1)
    return -1;
  return BitOperations(1, obj, vfdata[0], vfdata, w, h, bw, bo, bm_buf);
}


Private int
FreeOutline(obj, vfdata)
  FontObj  *obj;
  long*    vfdata;
{
  Font  *font;
  int   val;

  font = (Font*) obj->Locals;
  if (font->FontId == -1)
    return -1;
  val = VF_FreeOutline(vfdata, font->FontId);
  return val;
}



Private int
Link(obj)
  FontObj  *obj;
{
  obj->LinkCount = obj->LinkCount + 1;
  return obj->LinkCount;
}

Private int
Unlink(obj)
  FontObj  *obj;
{
  obj->LinkCount = obj->LinkCount - 1;
  return obj->LinkCount;
}


static int
ReadCapa(font, ent)
  Font *font;
  char *ent;
{
  char *p;

  VFC_GetEntry(ent);

  if ((p = VFC_GetString(VFCE_OPERATIONS)) != NULL){
    if ((font->Operations = malloc(strlen(p)+1)) == NULL)
      return -1;  /* ERR: malloc err */
  } else 
    p = "";
  strcpy(font->Operations, p);

  font->FontEntry = NULL;
  if ((p = VFC_GetString(VFCE_FONTENTRY)) != NULL){
    if ((font->FontEntry = malloc(strlen(p)+1)) == NULL)
      return -1;  /* ERR: malloc err */
    strcpy(font->FontEntry, p);
  }

  font->SubFontId = -1;
  font->SubFontEntry = NULL;
  if ((p = VFC_GetString(VFCE_SUBFONTENTRY)) != NULL){
    if ((font->SubFontEntry = malloc(strlen(p)+1)) == NULL)
      return -1;  /* ERR: malloc err */
    strcpy(font->SubFontEntry, p);
  }
  return 0;
}




Private int
BitOperations(flag, obj, jiscode, vfdata, w, h, bw, bo, bm_buf)
  int     flag;
  FontObj *obj;
  int     jiscode, w, h, bw, bo;
  long    *vfdata;
  unsigned char *bm_buf;
{
  int            val, rast, yy1, yy2, x, y, pc, ch, i;
  Font           *font;
  unsigned char  *src, *dest, *reg, *xreg, *yreg, *zreg, d;
  char           *prog; 
  Private unsigned char Mask1[] = {
    0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
  Private unsigned char Mask2[] = {
    0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
  Private unsigned char Mask3[] = {
    0xff, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};

  font = (Font*) obj->Locals;
  if (font->FontId == -1)
    return -1;

  if (font->Operations == NULL)
    return 0;

  rast = (w+7)/8;
  src  = (unsigned char*) malloc(h*rast);
  dest = (unsigned char*) malloc(h*rast);
  reg  = (unsigned char*) malloc(h*rast);
  if ((src == NULL) || (dest == NULL) || (reg == NULL)){
    if (src != NULL)  free(src);
    if (dest != NULL) free(dest);
    if (reg != NULL)  free(reg);
    return -1;
  }
  bzero(src,  rast*h);
  bzero(dest, rast*h);
  bzero(reg,  rast*h);

  /* Get DEST */
  yy1 = 0; yy2 = 0;
  for (y = 0; y < h; y++){
    for (x = 0; x < rast; x++)
      dest[yy2 + x] =   (bm_buf[yy1 + x]   << bo)
	              | (bm_buf[yy1 + x+1] >> (8-bo)) ;
    yy1 += bw; yy2 += rast;
  }

  /* Get SRC */
  if (flag == 0)
    val = VF_GetBitmap(jiscode, font->FontId, w, h, rast, 0, src);
  else 
    val = VF_DrawOutline(vfdata, font->FontId, w, h, rast, 0, src);
  if (val < 0)
    goto ExitLabel;

  pc = 0;
  prog = font->Operations;
  while (prog[pc] != '\0'){

    switch (prog[pc++]){

    case 'N': /* x := not x */
      xreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++, yy1 += rast){
	for (x = 0; x < rast; x++)
	  xreg[yy1 + x] = ~xreg[yy1 + x];
      }
      break;

    case 'C': /* x <= 0 */
      xreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++){
	for (x = 0; x < rast; x++)
	  xreg[yy1 + x] = 0x00;
	yy1 += rast;
      }
      break;

    case 'E': /* x <==> y */
      xreg = Reg(prog[pc++], dest, src, reg);
      yreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++, yy1 += rast){
	for (x = 0; x < rast; x++){
	  d = xreg[yy1 + x];
	  xreg[yy1 + x] = yreg[yy1 + x];
	  yreg[yy1 + x] = d;
	}
      }
      break;

    case 'L': /* x := y */
      xreg = Reg(prog[pc++], dest, src, reg);
      yreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++, yy1 += rast){
	for (x = 0; x < rast; x++)
	  xreg[yy1 + x] = yreg[yy1 + x];
      }
      break;

    case 'A': /* x := y & z */
      xreg = Reg(prog[pc++], dest, src, reg);
      yreg = Reg(prog[pc++], dest, src, reg);
      zreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++, yy1 += rast){
	for (x = 0; x < rast; x++)
	  xreg[yy1 + x] = yreg[yy1 + x] & zreg[yy1 + x];
      }
      break;

    case 'O': /* x := y | z */
      xreg = Reg(prog[pc++], dest, src, reg);
      yreg = Reg(prog[pc++], dest, src, reg);
      zreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++, yy1 += rast){
	for (x = 0; x < rast; x++)
	  xreg[yy1 + x] = yreg[yy1 + x] | zreg[yy1 + x];
      }
      break;

    case 'X': /* x := y xor z */
      xreg = Reg(prog[pc++], dest, src, reg);
      yreg = Reg(prog[pc++], dest, src, reg);
      zreg = Reg(prog[pc++], dest, src, reg);
      for (yy1 = 0, y = 0; y < h; y++){
	for (x = 0; x < rast; x++)
	  xreg[yy1 + x] = yreg[yy1 + x] ^ zreg[yy1 + x];
	yy1 += rast;
      }
      break;

    case 'B':
      xreg = Reg(prog[pc++], dest, src, reg);
      if (font->SubFontId == -1){
	/* DEMAND OPEN */
	if (font->SubFontEntry == NULL){
	  val = -1;
	  goto ExitLabel;
	}
	if ((font->SubFontId = VF_OpenFont(font->SubFontEntry)) < 0){
	  free(font->SubFontEntry);
	  font->SubFontEntry = NULL;
	  val = -1;
	  goto ExitLabel;
	}
      }
      ch = 0;
      for (i = 1; i <= 4; i++)
	ch = ch*0x10 + HexCh2Int(prog[pc++]);
      bzero(src,  rast*h);
      if ((val = VF_GetBitmap(ch, font->SubFontId, w, h, rast, 0, xreg)) < 0)
	goto ExitLabel;
      break;

    case 'H': /* x := half y */
      xreg = Reg(prog[pc++], dest, src, reg);
      yreg = Reg(prog[pc++], dest, src, reg);
      {
	unsigned char                   hm;
	int                             m;
	static unsigned char hmask[2] = {0xaa, 0x55};

	m = 0;
	for (yy1 = 0, y = 0; y < h; y++){
	  hm = hmask[m];
	  for (x = 0; x < rast; x++)
	    xreg[yy1 + x] = yreg[yy1 + x] & hm;
	  yy1 += rast;
	  m = (m+1)%2;
	}
      }
      break;

    case ' ':    /* NOP */
    case '\t':
    case '\n':
    case ',':
      break;
      
    default:
      val = -1;  /* ILL OPERATION */
      goto ExitLabel;
    }
  }

  /* Set DEST */
  for (yy1 = 0, yy2 = 0, y = 0; y < h; y++){
    bm_buf[yy1]        &= Mask1[bo];
    bm_buf[yy1+rast-1] &= Mask2[(bo+w)%8];
    dest[yy2 + rast-1] &= Mask3[w % 8];
    for (x = 0; x < rast-1; x++){
      d = dest[yy2 + x];
      bm_buf[yy1 + x]   |= d >> bo;
      bm_buf[yy1 + x+1]  = d << (8-bo);
    }
    d = dest[yy2 + rast - 1];
    bm_buf[yy1 + x]      |= d >> bo;
    bm_buf[yy1 + rast-1] |= d << (8-bo);
    yy1 += bw; yy2 += rast;
  }

ExitLabel:
  free(src);
  free(dest);
  free(reg);

  return val;
}

Private int
HexCh2Int(ch)
  char ch;
{
  typedef struct {
    char  xch;
    int   ich;
  } _Tbl;
  static _Tbl  Table[] = {
    {'0', 0},  {'1', 1},  {'2', 2},  {'3', 3}, 
    {'4', 4},  {'5', 5},  {'6', 6},  {'7', 7}, 
    {'8', 8},  {'9', 9},  {'a', 10}, {'b', 11}, 
    {'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}, 
    {'S', -1}  /* sentinel */
  };
  int  i;
  char ch2;

  ch2 = tolower(ch);
  Table[16].xch = ch2;
  i = 0; 
  while (Table[i].xch != ch2)  /* THIS IS STUPID, BUT PORTABLE */
    i++;
  return Table[i].ich;
}

Private unsigned char*
Reg(r, dest, src, reg)
  char            r;
  unsigned char   *dest, *src, *reg; 
{
  switch (r){
  case 'D':
  case 'd':
    return dest;
  case 'S':
  case 's':
    return src;
  case 'R':
  case 'r':
    return reg;
  default:
    break;
  }
  return NULL;
}
