Source file: /~heha/hs/dos/hp2xx_hs.zip/picbuf.c

/* Copyright (c) 1991 - 1994 Heinz W. Werntges.  All rights reserved. */

/** picbuf.c: Part of hp2xx project dealing with the picture buffer
 **
 ** 91/01/19  V 1.00  HWW  Derived from hptopcl.c
 ** 91/01/29  V 1.01  HWW  Tested on SUN
 ** 91/02/15  V 1.02  HWW  stdlib.h supported
 ** 91/02/20  V 1.03a HWW  minor mod. in fread(); adaptation to
 **			   new HPGL_Pt structures in tmpfile_to_PicBuf()
 ** 91/06/09  V 1.04  HWW  new options acknowledged; minimal changes
 ** 91/10/15  V 1.05  HWW  ANSI_C
 ** 91/11/20  V 1.06  HWW  "SPn;" consequences
 ** 92/02/17  V 1.07b HWW  Preparations for font support
 ** 92/05/24  V 2.00c HWW  Color supported! Fonts ok now; "init" bug fixed
 ** 92/06/08  V 2.00d HWW  GIVE_BACK: 5 --> 8; free_PicBuf() debugged
 ** 92/12/24  V 2.00e HWW  plot_RowBuf() augmented to bit REsetting
 ** 93/04/02  V 2.01a HWW  Always use four bit planes in color mode!
 **			   Out-dated "DotBlock" concept replaced by "char".
 ** 94/02/14  V 2.10  HWW  New parameter structs; restructured
 **			   Improved cleanup & error handling
 ** 00/07/16          MK   Modify pensize correction in size_Pixbuf
 **                        for new .1 pixel pensize unit scheme (G.B.)
 **/


#include <stdio.h>
#include <stdlib.h>
#ifndef _NO_VCL
#include <unistd.h>
#endif
#include <string.h>
#include <math.h>
#ifdef DOS
#include <dos.h>
#endif
#ifdef WIN32
#include <windows.h>
#undef NUMPENS
#undef ERROR
#endif
#include "bresnham.h"
#include "pendef.h"
#include "hp2xx.h"



#ifndef SEEK_SET
#define SEEK_SET 0
#endif

short swaps(short x) {
 Byte t;
 t=((Byte*)&x)[0];
 ((Byte*)&x)[0]=((Byte*)&x)[1];
 ((Byte*)&x)[1]=t;
 return x;
}

long swapl(long x) {
 Byte t;
 t=((Byte*)&x)[0];
 ((Byte*)&x)[0]=((Byte*)&x)[3];
 ((Byte*)&x)[3]=t;
 t=((Byte*)&x)[1];
 ((Byte*)&x)[1]=((Byte*)&x)[2];
 ((Byte*)&x)[2]=t;
 return x;
}

void ShowPercent(long part, long whole) {
 static unsigned last=(unsigned)-1;
 char buf[51];
 unsigned curr=(unsigned)(part*100/whole);

 if (curr>100) curr=100;
 if (curr!=last) {
  memset(buf,' ',50);
  memset(buf,'-',curr/2);
  buf[curr/2]='>';
  sprintf(buf+23,"%3d",curr);
  buf[26]='%';
  buf[50]='\0';
  Eprintf("\r[%s]",buf);
  last=curr;
 }
}

unsigned Toc(void) {
#ifdef DOS
 return *(unsigned far*)MK_FP(0x40,0x6C);
#else
# ifdef WIN32
 return GetTickCount();
# else
 return 0;		/* no turning dash */
# endif
#endif
}

void dreh(void) {
 static unsigned i=0;
 static unsigned tic;
 unsigned toc;
 toc=Toc();
 if (tic!=toc) {
  Eprintf("%c\b","\\|/-"[++i%4]);
  tic=toc;
 }
}


void swapout_Tile(const PicBuf *pb, int index) {
 if (fseek (pb->sd, (long)index*pb->kby, SEEK_SET)) {
  PError("swapout_RowBuf (on seek)");
  exit(ERROR);
 }
 if (fwrite((char *)pb->tile[index].buf, pb->kby, 1, pb->sd)!=1) {
  PError("swapout_RowBuf (on write)");
  exit(ERROR);
 }
 dreh();
}

void swapin_Tile(const PicBuf *pb, int index) {
 if (fseek (pb->sd, (long)index*pb->kby, SEEK_SET)) {
  PError("swapin_RowBuf (on seek)");
  exit(ERROR);
 }
 if (fread ((char *)pb->tile[index].buf, pb->kby, 1, pb->sd)!=1) {
  PError("swapin_RowBuf (on read)");
  exit(ERROR);
 }
}


void link_Tile_MRU(PicBuf *pb, int index) {
 TILE *act =&pb->tile[index];
 TILE *next=&pb->tails;

 if (next->next!=-1) next=pb->tile+next->next;

 next->prev=index;
 act->next=pb->tails.next;
 pb->tails.next=index;
 act->prev=-1;
}

void unlink_Tile(PicBuf *pb, int index) {
 TILE *act =&pb->tile[index];
 TILE *prev=&pb->tails;
 TILE *next=&pb->tails;

 if (act->prev!=-1) prev=&pb->tile[act->prev];
 if (act->next!=-1) next=&pb->tile[act->next];
 prev->next=act->next; act->next=-1;
 next->prev=act->prev; act->prev=-1;
}


Byte* Map_Tile(PicBuf *pb, int index) {
 TILE *cur;

 cur=&pb->tile[index];

/**
 ** If swapped, load first. Put into first position, if not already there:
 **/
 if (!cur->buf) {
  TILE *last=&pb->tile[pb->tails.prev];
  swapout_Tile(pb,pb->tails.prev);
  unlink_Tile(pb,pb->tails.prev);	/* Mark as swapped	 */
  cur->buf=last->buf;
  last->buf=NULL;
  swapin_Tile(pb,index);
  link_Tile_MRU(pb,index);	/* Put in first position */
 }else if (index!=pb->tails.next) {
  unlink_Tile(pb,index);
  link_Tile_MRU(pb,index);	/* Put in first position */
 }/* else: Leave it in first position */
 return cur->buf;
}


void plot_Tile(const PicBuf *pb, Byte *buf, int x, int y, Byte color_index) {
/**
 ** Write color index into pixel x of given row buffer
 **/
 Byte Mask;
 if (!buf) return;
/**
 ** Color_index is either the low bit (b/w) or the low nibble (color)
 ** rowbuf->buf is either a sequence of such bits or nibbles.
 ** High bits show left, low bits show right.
 **
 ** This is a time-critical step, so code here is compact,
 ** but not easily readable...
 **/

 if (pb->depth==1) {
  buf+=(x>>3)+(y<<(pb->kshx));
  x=~x&7;
  if (color_index) *buf|=1<<x;
  else *buf&=~(1<<x);
 }else{
  if (color_index>15) color_index=15;
  buf+=(x>>1)+(y<<(pb->kshx));
  Mask=0xF0;
  if (!x&1) {
   color_index<<=4;
   Mask=0x0F;
  }
  *buf=(*buf&Mask)|color_index;
 }
}


Byte GetPixel_from_Tile(const PicBuf *pb, Byte *buf, int x, int y) {
/**
 ** Return color index of pixel x/y in given tile
 **/
 Byte M;

 if (pb->depth==1) {
  buf+=(x>>3)+(y<<(pb->kshx));
  return (*buf>>(~x&7))&1;
 }else{
  buf+=(x>>1)+(y<<(pb->kshx));
  M=*buf;
  if (!x&1) M>>=4;
  return M&0x0F;
 }
}

void
HPcoord_to_dotcoord (const HPGL_Pt *HP_P, DevPt *DevP, const OUT_PAR* po)
{
  DevP->x = (int) ((HP_P->x - po->xmin) * po->HP_to_xdots +0.5);
  DevP->y = (int) ((HP_P->y - po->ymin) * po->HP_to_ydots +0.5);
}


void size_PicBuf (const GEN_PAR* pg, const OUT_PAR* po, DevPt *size) {
 HPGL_Pt HP_Pt;
 DevPt	D_Pt;
 int maxps;

 HP_Pt.x  = po->xmax;
 HP_Pt.y  = po->ymax;
 HPcoord_to_dotcoord (&HP_Pt, &D_Pt, po);
  /* Pensize correction	*/
 /* maxps= (int)(1. + pg->maxpensize *po->HP_to_xdots/10.0/0.025); */
 maxps= pg->maxpensize; /* thick lines are drawn to penwidth - not currently scaled */
			 /* so we must do the same when calculating limits - or we try to draw outside page */
 size->x  = D_Pt.x + maxps;
 size->y  = D_Pt.y + maxps;
}


PicBuf* allocate_PicBuf (const GEN_PAR* pg, const DevPt *size) {
/**
 ** Here we allocate the picture buffer. This memory is used by all raster
 ** modes. It is organized in rows (scan lines). Rows which do not
 ** end on a byte boundary will be right-padded with "background" bits.
 **
 ** If colors are active, there will always be "four bit" layers per row,
 ** even if you need only three colors.
 ** These layers are implemented by allocating longer rows
 ** (regular length times number of bit planes per pel (depth)).
 **
 ** We try to allocate all row buffers from main memory first.
 ** If allocation fails, we first free a few lines (see constant GIVE_BACK)
 ** to avoid operation close to the dyn. memory limit,
 ** and then initiate swapping to a file.
 **/
 PicBuf	*pb;
 int	nr, not_allocated;
 char	*preserve;

 pb=(PicBuf*)calloc(sizeof(PicBuf),1);
 if (!pb) {
  Eprintf ("Cannot malloc() PicBuf structure\n");
  return NULL;
 }

 pb->pext.x=size->x;
 pb->pext.y=size->y;
 pb->tails.next=-1;
 pb->tails.prev=-1;

/**
 ** Number of buffer bytes per row:
 **
 ** Example:
 **
 ** dot range (horiz.): 0...2595 ==> 2596 dots per row, pb->nc=2096 ==>
 ** 	[2596 bits / 8 bits per byte]
 ** ==> 324 DotBlocks + 4 bits which require another whole byte (!)
 ** B/W mode      (1 bit per pel, Foreground & Background),
 ** or color mode (4 bits per pel)
 **/
 if (pg->is_color) {
  pb->dsh=1;
  pb->nb=(pb->pext.x+1)>>1;
 }else{
  pb->dsh=3;
  pb->nb=(pb->pext.x+7)>>3;
 }
 pb->depth=8>>pb->dsh;

/** Calculate the size of a tile (64..512 pixel)
 ** DOS: We have a maximum of 64K/8 = 8K tiles
 ** Here: A line should not have more than 90 tiles, 90x90 = 8K tiles */
 nr=MAX(pb->pext.x,pb->pext.y)>>6;
 pb->ksi=64;
 pb->ksh=6;
 while (nr>90) {
  pb->ksi<<=1;
  pb->ksh++;
  nr>>=1;
 }
 if (pb->ksh>9) {
  Eprintf ("Picture too large\n");
  goto error1;
 }
 pb->ksix=pb->ksi>>pb->dsh;
 pb->kshx=pb->ksh-pb->dsh;
 pb->kext.x=(pb->pext.x+pb->ksi-1)>>pb->ksh;
 pb->kext.y=(pb->pext.y+pb->ksi-1)>>pb->ksh;
 pb->nk=pb->kext.x*pb->kext.y;
 pb->kby=(unsigned)(1<<(pb->kshx+pb->ksh));
 Eprintf("Calculated: %dx%d = %d tiles, %d pixels (%d bytes) each\n",
   pb->kext.x,pb->kext.y,pb->nk,pb->ksi,pb->kby);

/**
 ** Allocate a (large) array of RowBuf structures: One for each scan line.
 ** !!! The NULL initialization done implicitly by calloc() is crucial !!!
 **/

 pb->tile=(TILE*)calloc((size_t)pb->nk,sizeof(TILE));
 if (!pb->tile) {
  Eprintf ("Cannot calloc() %d tile structures\n", pb->nk);
  goto error1;
 }

/**
 ** Now try to allocate as many buffers as possible. Double-link all RowBuf's
 ** which succeed in buffer allocation, leave the rest isolated (swapping
 ** candidates!)
 **/
 preserve=malloc(4096);
 if (!preserve) {
  Eprintf ("\nNot enough memory for swapping -- sorry!\n");
  goto error1;
 }

 not_allocated=0;
 for (nr=0; nr < pb->nk; nr++) {
  TILE *cur=&(pb->tile[nr]);
  cur->next=-1;
  cur->prev=-1;
  cur->buf=(Byte*)calloc(pb->kby,1);
  if (cur->buf) {
   link_Tile_MRU(pb,nr);
  }else not_allocated++;
 }
 free(preserve);

 if (pb->tails.next==-1) {	/* at least one entry is necessary! */
  Eprintf ("\nNot enough memory for swapping -- sorry!\n");
  goto error1;
 }

/**
 ** Prepare swapping
 **/

 if (not_allocated) {
  Eprintf ("\nCouldn't allocate %d out of %d tiles, have %d for cacheing.\n",
    not_allocated, pb->nk, pb->nk-not_allocated);
  Eprintf ("Swapping to disk (size=%ld bytes)- may take a while...\n",
    (long)pb->nk*pb->kby);
  pb->sf_name = pg->swapfile;
  pb->sd=fopen(pb->sf_name,WRITE_BIN);
  if (!pb->sd) {
   Eprintf ("Couldn't open swap file '%s'\n", pb->sf_name);
   goto error2;
  }

/**
 ** Init. swap file data to background color (0), using a shortcut by
 ** assuming that all data are stored without gaps. Thus, instead of
 ** row-by-row operation, we simply write a sufficient number of 0 rows
 ** into the swap file sequentially.
 **/

  for (nr=0; nr < pb->nk; nr++) {
   if (fwrite((char *)pb->tile[0].buf,pb->kby,1,pb->sd)!=(size_t)1) {
    Eprintf ("Couldn't clear swap file!\n");
    goto error2;
   }
  }
 }
 return pb;

error2:
 PError ("hp2xx");
error1:
 free_PicBuf (pb);
 return NULL;
}

void free_PicBuf(PicBuf* pb) {
/**
 ** De-allocate all row buffers and the picture puffer struct,
 ** remove the swap file (if any).
 **/
 int	i;

 if (!pb) return;
 if (pb->sd) {
  fclose (pb->sd);
  pb->sd = NULL;
  unlink (pb->sf_name);
 }
 if (!pb->tile) return;

 for (i=0; i< pb->nk; i++) {
  TILE *tile=pb->tile+i;
  if (tile->buf) {
   free (tile->buf);
   tile->buf=NULL;
  }
 }
 free(pb->tile);
 free(pb);
}

int GetTileIndex(const PicBuf *pb, const DevPt *pt) {
 return (pt->y>>pb->ksh)*pb->kext.x+(pt->x>>pb->ksh);
}

int Out_Of_Range(const PicBuf *pb, const DevPt *pt) {
 if ((unsigned)pt->x >= (unsigned)pb->pext.x) {
  Eprintf("PicBuf: Illegal x (%d not in [0, %d])\n",pt->x, pb->pext.x);
  return 1;
 }
 if ((unsigned)pt->y >= (unsigned)pb->pext.y) {
  Eprintf("PicBuf: Illegal y (%d not in [0, %d])\n",pt->y, pb->pext.y);
  return 1;
 }
 return 0;
}

void plot_PicBuf(PicBuf *pb, const DevPt *pt, Byte color) {
/* sets a pixel to the given value (overwrite only) */
 if (Out_Of_Range(pb,pt)) return;
 plot_Tile(pb,Map_Tile(pb,GetTileIndex(pb,pt)),
   pt->x&(pb->ksi-1),pt->y&(pb->ksi-1),color);
}

Byte GetPixel_from_PicBuf (PicBuf *pb, const DevPt *pt) {
/* returns the pixel value at given coordinate */
 if (Out_Of_Range(pb,pt)) return 0;
 return GetPixel_from_Tile(pb,Map_Tile(pb,GetTileIndex(pb,pt)),
   pt->x&(pb->ksi-1),pt->y&(pb->ksi-1));
}

Byte*GetTileLine(PicBuf *pb, const DevPt *pt, int *linelen) {
/* returns a pointer to one line of the tile,
   and the valid length of this line in bytes.
   pt.x should be at tile boundary */
 if (Out_Of_Range(pb,pt)) return NULL;
 if (pt->x&(pb->ksi-1)) {
  Eprintf("GetTileLine: X not left border of tile, %d pixel inside\n",
    pt->x&(pb->ksi-1));
 }
 if (linelen) *linelen=MIN(pb->ksix,pb->nb-(pt->x>>pb->dsh));
 return Map_Tile(pb,GetTileIndex(pb,pt))+((pt->y&(pb->ksi-1))<<(pb->kshx));
}

void line_PicBuf(DevPt *p0, DevPt *p1, int pensize, int pencolor,
  PicBuf* pb) {
/**
 ** Rasterize a vector (draw a line in the picture buffer), using the
 ** Bresenham algorithm.
 **/
 DevPt	pt, *p_act;

/*fprintf(stderr,"line_PicBuf, color %d, width %d\n",pencolor,pensize);*/

 if (pensize == 0) return;		/* No pen selected!	*/
/*fprintf(stderr,"line_PicBuf, pencolor ist %d\n",pencolor);*/
 if (pencolor == xxBackground) return;	/* No drawable color!	*/

 p_act = bresenham_init (p0, p1);
 if (pensize == 1) do {
  plot_PicBuf (pb, p_act, pencolor);
 } while (bresenham_next() != BRESENHAM_ERR);
 else do {
  plot_PicBuf (pb, p_act, pencolor);

  pt = *p_act;
  pt.x++; plot_PicBuf (pb, &pt, pencolor);
  pt.y++; plot_PicBuf (pb, &pt, pencolor);
  pt.x--; plot_PicBuf (pb, &pt, pencolor);

  if (pensize > 2) {
   pt = *p_act;
   pt.x += 2;	plot_PicBuf (pb, &pt, pencolor);
   pt.y++;	plot_PicBuf (pb, &pt, pencolor);
   pt.y++;	plot_PicBuf (pb, &pt, pencolor);
   pt.x--;	plot_PicBuf (pb, &pt, pencolor);
   pt.x--;	plot_PicBuf (pb, &pt, pencolor);

   if (pensize > 3) {	/* expecting 4 ... 9	*/
    pt = *p_act;
    pt.x += 3;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
   }

   if (pensize > 7) {	/* who knows	*/
    pt = *p_act;
    pt.x += 4;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
   }
   if (pensize > 12) {	/* who knows	*/
    pt = *p_act;
    pt.x += 5;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
   }
   if (pensize > 15) {	/* who knows	*/
    pt = *p_act;
    pt.x += 6;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.y++;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
    pt.x--;	plot_PicBuf (pb, &pt, pencolor);
   }
  }
 } while (bresenham_next() != BRESENHAM_ERR);
}


void tmpfile_to_PicBuf (const GEN_PAR* pg, const OUT_PAR* po)
/**
 ** Interface to higher-level routines:
 **   Assuming a valid picture buffer, read the drawing commands from
 **   the temporary file, transform HP_GL coordinates into dot coordinates,
 **   and draw (rasterize) vectors.
 **/
{
 HPGL_Pt	pt1;
 static	DevPt	ref = {0};
 DevPt		next;
 PlotCmd	cmd;
 int		pen_no = 1;

 if (!pg->quiet)
  Eprintf ( "\nPlotting in buffer\n");

 rewind (pg->td);

 while ((cmd = PlotCmd_from_tmpfile()) != CMD_EOF)
 switch (cmd) {
  case NOP: break;

  case SET_PEN:
  if ((pen_no = fgetc(pg->td)) == EOF) {
   PError("Unexpected end of temp. file");
   exit (ERROR);
  }break;

  case DEF_PW:
  if(!load_pen_width_table(pg->td)) {
   PError("Unexpected end of temp. file");
   exit(ERROR);
  }break;

  case DEF_PC:
  if(!load_pen_color_table(pg->td)) {
   PError("Unexpected end of temp. file");
   exit(ERROR);
  }break;

  case MOVE_TO:
  HPGL_Pt_from_tmpfile(&pt1);
  HPcoord_to_dotcoord (&pt1, &ref, po);
  break;

  case DRAW_TO:
  HPGL_Pt_from_tmpfile(&pt1);
  HPcoord_to_dotcoord (&pt1, &next, po);
  line_PicBuf(&ref,&next,pt.width[pen_no],pt.color[pen_no],po->picbuf);
  ref=next;
  break;

  case PLOT_AT:
  HPGL_Pt_from_tmpfile(&pt1);
  HPcoord_to_dotcoord (&pt1, &ref, po);
  line_PicBuf(&ref,&ref,pt.width[pen_no],pt.color[pen_no],po->picbuf);
  break;

  default:
  Eprintf ("Illegal cmd in temp. file!\n");
  exit (ERROR);
 }
}
Detected encoding: ASCII (7 bit)2