/* 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);
}
}
Vorgefundene Kodierung: ASCII (7 bit) | 2
|