This is dvirle2.c in view mode; [Download] [Up]
#ifndef lint
static char rcsid[] = "$Id: dvirle2.c,v 3.0 90/08/03 15:25:01 spencer Exp $";
#endif
/*
* Dvirle2 -- Second half of DVI to RLE converter
*
* Reads pre-sorted pages as put out by dvirle1, and shovels bitmaps
* out to the RLE file as fast as possible. Warning: there is some
* inline assembly code used in inner loops, where the C compiler
* produced particuarly poor code.
*
* We use a technique known as a `band buffer', where we keep track
* of what has yet to be written to the RLE file in a buffer that
* represents a `band' across the current page, analagous to a magnifying
* bar across the page. Only the region in the band can be written,
* and the band moves only downward; this is why dvirle1 must sort
* each page, at least by y coordinate. This also implies that the
* `tallest' object we can write is the same height as the band. This
* is a problem for large characters. For these there is some (as yet
* unimplemented) code that will ask for a `part' of each character
* to be drawn into the band. The character would then be repeated
* with a request for the next part when the band has moved down to
* just below the bottom of the previous part. Rules are also broken
* up as appropriate (and that code *is* implemented).
*
* Another important point is that the band buffer is treated as a
* `cylinder' rather than a `strip': we write bits onto the cylinder,
* then roll it forward over the page, moving the bits off the cylinder
* and onto the paper, leaving that part of the cylinder clean, ready
* for more bits. The variable `CurRow' points at the current row
* in the buffer/on the cylinder, and `FirstRow' and `LastRow' bound
* the `dirty' part of the cylinder. Modular arithmetic suffices to
* change linear to cylindrical.
*
* Yet another point of note is that because the band always moves
* `down' on the page, we need only a positive offset from the current
* row to move to a new row. This means (among other things) that we
* can use negative offsets for special purposes.
*/
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include "types.h"
#include "conv.h"
#include "fio.h"
#include "font.h"
#include "dvirle.h"
#include <rle.h>
/*#define SPEED_HACK*/
void ReadFonts(), FormFeed(), ReadInput(), VWriteChar(), VWriteRule();
void DumpTopOfBand(), MoveDown(), WriteBuf(), WriteBlanks();
char *ProgName;
extern int errno;
extern char *optarg;
extern int optind;
/* Global variables. */
struct font *Fonts[NFONTS]; /* the fonts */
char TeXFontDesc[256]; /* getenv("TEXFONTDESC") from dvirle1 */
int RasterOrientation; /* ROT_NORM or ROT_RIGHT, based on HFlag */
int DFlag; /* -d => output discarded */
int HFlag; /* -h => horizontal (rotated bitmaps) */
int SFlag; /* -s => silent processing */
int TFlag; /* -t => output to tape */
int Debug; /* -D => debug flag */
unsigned char VBuffer[ROWS][COLUMNS]; /* band buffer */
int NewPage; /* New page indicator */
int CurRow; /* current row in buffer */
int CurCol; /* current column in buffer */
int FirstRow; /* the first row used */
int LastRow; /* the last row used */
int NLines; /* counts lines; used for pagefeeds */
int PageHeight, PageWidth; /* Page size, from pass 1 */
int filtw = 5, filth = 5; /* Filter dimensions */
int filtfact; /* multiplier for filter averaging */
int outwidth; /* width of output line */
CONST_DECL char * pic_comments[] = {
"Creator=DVIRLE",
"Note=Created from DVI file at 300 dots/inch",
0
};
/*
* RowsBetween tells how many rows (in cylindrical arithmetic) there
* are between the first position and the second. If the second value
* is less than the first value, add ROWS to do the appropriate modular
* arithmetic. We cannot use `%' as C `%' is machine-dependent with
* respect to negative values.
*/
#define RowsBetween(f, n) ((n) >= (f) ? (n) - (f) : (n) - (f) + ROWS)
/*
* This is it... on your marks ... get set ... main!
*/
void
main(argc, argv)
int argc;
register char **argv;
{
register int c;
register char *s;
int dpi, usermag, num, denom, dvimag;
ProgName = *argv;
while ((c = getopt(argc, argv, "dstDx:y:")) != EOF) {
switch (c) {
case 'd': /* output to /dev/null */
DFlag++;
break;
case 's': /* silent processing except for errors */
SFlag++;
break;
case 't': /* output to tape (not implemented) */
TFlag++;
error(0, 0, "tape option not yet implemented");
break;
case 'D':
Debug++;
break;
case 'x':
if ( optarg )
filtw = atoi( optarg );
break;
case 'y':
if ( optarg )
filth = atoi( optarg );
break;
case '?':
fprintf(stderr, "Usage: %s [-d] [-s] [-t] [file]\n",
ProgName);
exit(1);
}
}
if (optind < argc)
if (freopen(argv[optind], "r", stdin) == NULL)
error(1, 0, "can't open %s", argv[optind]);
HFlag = getchar();
if ((HFlag >> 1) != VERSION)
error(1, 0, "input file is not version %d", VERSION);
HFlag &= 1;
RasterOrientation = HFlag ? ROT_RIGHT : ROT_NORM;
s = TeXFontDesc;
c = GetLong(stdin);
while (--c >= 0)
*s++ = getchar();
if (feof(stdin))
(void) GetByte(stdin); /* let GetByte do error */
*s = 0;
dpi = GetLong(stdin);
usermag = GetLong(stdin);
num = GetLong(stdin);
denom = GetLong(stdin);
dvimag = GetLong(stdin);
SetConversion(dpi, usermag, num, denom, dvimag);
fontinit(*TeXFontDesc ? TeXFontDesc : (char *) NULL);
ReadFonts();
PageHeight = GetLong(stdin);
PageWidth = GetLong(stdin);
if (DFlag) {
(void) fprintf(stderr, "Output will be discarded\n");
(void) fflush(stderr);
rle_dflt_hdr.rle_file = fopen("/dev/null", "w");
}
/* Set up output file papameters */
rle_dflt_hdr.xmin = 0;
rle_dflt_hdr.xmax = outwidth = (PageWidth - 1) / filtw;
rle_dflt_hdr.ymin = 0;
rle_dflt_hdr.ymax = (PageHeight - 1) / filth;
rle_dflt_hdr.ncolors = 1;
rle_dflt_hdr.alpha = 1;
RLE_SET_BIT( rle_dflt_hdr, RLE_ALPHA );
rle_dflt_hdr.comments = pic_comments;
rle_put_setup( &rle_dflt_hdr );
filtfact = 255.0 / (filtw * filth);
ReadInput();
FormFeed(); /* Write last EOF */
if (!SFlag)
(void) putc('\n', stderr);
exit(0);
/* NOTREACHED */
}
/*
* Read the font definitions.
*
* Anti-streak hack: get the rasters ahead of time, #ifdef SPEED_HACK.
*/
void
ReadFonts()
{
register struct font *f, **fp;
register int c;
register char *s;
#ifdef SPEED_HACK
register struct glyph *g;
#endif
i32 mag, dsize;
char *fname;
char nm[512];
if (!SFlag)
(void) fprintf(stderr, "[fonts:\n");
fp = Fonts;
while (GetByte(stdin) == 1) {
(void) GetLong(stdin); /* checksum */
mag = GetLong(stdin); /* magfactor */
dsize = GetLong(stdin); /* design size */
c = GetLong(stdin);
s = nm;
while (--c >= 0)
*s++ = getchar();
if (feof(stdin))
(void) GetByte(stdin); /* let GetByte do error */
*s = 0;
f = GetFont(nm, mag, dsize, "RLE", &fname);
if (f == NULL) {
GripeCannotGetFont(nm, mag, dsize, "RLE", fname);
exit(1);
/* NOTREACHED */
}
if (Debug) {
(void) fprintf(stderr, "[%s -> %s]\n",
Font_TeXName(f), fname);
(void) fflush(stderr);
}
if (!SFlag) {
register char *t = fname;
s = fname;
while (*s)
if (*s++ == '/' && *s)
t = s;
(void) fprintf(stderr, " %s\n", t);
}
#ifdef SPEED_HACK
for (c = 0; c < 128; c++) {
g = GLYPH(f, c);
if (GVALID(g))
(void) RASTER(g, f, RasterOrientation);
}
#endif
*fp++ = f;
}
if (!SFlag)
(void) fprintf(stderr, "]\n");
}
/*
* Read the input stream, decode it, and put character rasters or rules at
* the positions given.
*/
void
ReadInput()
{
register int yx, fcp, height;
/*
* Loop forever. I had a `for (;;)' but everything crept off the
* right side of the screen.
*/
next:
fGetLong(stdin, yx); /* position */
fGetLong(stdin, fcp); /* character, most likely */
if ( Debug > 2 )
fprintf( stderr, "yx:%lx, fcp:%lx\n", yx, fcp );
if (feof(stdin))
return; /* done */
/*
* A `position' of -1 indicates either a rule or an end of page.
* Anything else is a character.
*/
if (yx != -1) { /* place character */
register struct glyph *g;
register struct font *f;
register int fnum;
/*
* Any delta-y required is stored in the upper 16 bits of yx.
*/
if ((height = yx >> 16) != 0)
MoveDown(height);
/*
* Extract the x, font, char, and part info into CurCol,
* fnum, yx, and fcp.
*/
CurCol = yx & 0xffff;
fnum = fcp >> FONTSHIFT;
yx = (fcp >> CHARSHIFT) & CHARMASK;
fcp = fcp & PARTMASK;
f = Fonts[fnum]; /* trusting */
g = GLYPH(f, yx);
if ( Debug > 1 )
fprintf( stderr,
"Col:%d, Height:%d, fnum:%d, yx:%d, fcp:%d\n",
CurCol, height, fnum, yx, fcp );
/*
* In case this character does not fit, write
* out the used part of the band. It had better
* fit afterward....
*/
height = g->g_height;
if (height >= ROWS - RowsBetween(FirstRow, CurRow))
DumpTopOfBand();
if (fcp) /* cannot handle these yet */
error(0, 0, "\
part code not implemented; skipping char %d in %s",
yx, f->f_path);
else if (HASRASTER(g)) {
#ifdef SPEED_HACK
/* XXX, but saves time */
VWriteChar(g->g_raster, height, g->g_width);
#else
VWriteChar(RASTER(g, f, RasterOrientation),
height, g->g_width);
#endif
}
goto next; /* done with character */
}
/*
* If the `character' is negative, we need to move down first,
* possibly because this is an end-of-page. If this is not the
* end of the page, it must be a rule.
*/
if (fcp < 0) { /* move down */
yx = -fcp;
fGetLong(stdin, fcp); /* junk */
fGetLong(stdin, fcp);
if (fcp == 0) { /* end page */
if ( Debug > 1 )
fprintf( stderr, "End of Page\n" );
/* dump entire band */
WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
CurRow = LastRow = FirstRow;
if (!HFlag) {
WriteBlanks(yx - NLines);
FormFeed();
} else
FormFeed();
if (!SFlag)
(void) fprintf(stderr, ".");
NLines = 0;
goto next; /* all done */
}
MoveDown(yx); /* must be a rule; move down by yx rows */
}
/*
* At this point we have a rule to put at the current
* position, CurRow.
*/
height = (fcp & 0xff00) >> 8;
/* make sure it fits */
if (height >= ROWS - RowsBetween(FirstRow, CurRow))
DumpTopOfBand();
VWriteRule(fcp);
goto next; /* done with rule */
}
/*
* Write the given raster for the given character.
*
* Basically, the task is to move bits from the raster to the output
* buffer. However, because the character being plotted can be on an
* arbitrary bit boundary, things are not as simple as we might like.
* The solution used here is to shift each raster value right, OR it
* into the buffer, then (at the next location) OR in the bits that
* `fell off the right edge'.
*/
void
VWriteChar(rastp, height, width)
unsigned char *rastp; /* raster pointer */
int height, width; /* height & width of char */
{
register unsigned char *bp; /* Output buffer pointer [r11] */
register unsigned char *rp; /* raster pointer [r10] */
register int rshift; /* right shift index [r9] */
register int lshift; /* left shift index [r8] */
register int j; /* width loop downcounter */
register int o; /* offset to next row in buffer */
int row; /* current row in buffer */
int col; /* column in buffer of left edge */
int i; /* height loop downcounter */
int w; /* raster width (bytes) */
if ((rp = rastp) == NULL)
return; /* an all-white character */
row = CurRow + height - 1;
if ( row >= ROWS )
row -= ROWS; /* assume height <= ROWS? */
col = CurCol >> 3;
i = height;
w = (width + 7) >> 3;
o = COLUMNS + w;
#if defined(lint) || !defined(vax)
rshift = CurCol & 7;
lshift = 8 - rshift;
#else /* lint || !vax */
rshift = -(CurCol & 7); /* Vax does '>>' as negative '<<' */
lshift = 8 + rshift;
#endif /* lint || !vax */
bp = &VBuffer[row][col];
#define avoiding_shifts_is_faster /* but is it??? */
#ifdef avoiding_shifts_is_faster
/*
* One out of eight or so times, the shift values will be
* zero. This makes the code run faster.
*/
if (rshift == 0) {
while (--i >= 0) {
j = w;
while (--j >= 0)
*bp++ |= *rp++;
if (--row < 0) {
row = ROWS - 1;
bp = &VBuffer[ROWS - 1][col];
} else
bp -= o;
}
} else
#endif
{
while (--i >= 0) {
j = w;
while (--j >= 0) {
#if defined(lint) || !defined(vax)
*bp++ |= (*rp & 255) >> rshift;
*bp |= (*rp++ & 255) << lshift;
#else /* lint || !vax */
/*
* THE FOLLOWING ASSEMBLY CODE IS INSERTED
* BECAUSE THE COMPILER CAN'T OPTIMIZE THE
* C CODE WORTH A DARN
*/
asm(" movzbl (r10)+,r1 # *rp++ & 255");
asm(" ashl r9,r1,r0 # >> rshift");
asm(" bisb2 r0,(r11)+ # *bp++ |=");
asm(" ashl r8,r1,r0 # << lshift");
asm(" bisb2 r0,(r11) # *bp |=");
#endif /* lint || !vax */
}
if (--row < 0) {
row = ROWS - 1;
bp = &VBuffer[ROWS - 1][col];
} else
bp -= o;
}
}
j = height + CurRow - 1;/* have now set bits this far */
if (j >= ROWS)
j -= ROWS; /* keep it modular */
/*
* There are two cases. Either the buffer is not currently wrapped,
* in which case the regions past LastRow or before FirstRow extend
* it; or it is wrapped, in which case the region between LastRow
* and FirstRow extends it:
*
* case 1 case 2
* -------- --------
* | | last ->| XXXX |
* first ->| XXXX | | |
* | XXXX | | |
* last ->| XXXX | first ->| XXXX |
* | | | XXXX |
* -------- --------
*
* The `X's mark the region that is in use; the blank spaces
* mark the region that causes the `last' value to change.
*/
if (FirstRow <= LastRow) {
/* first case: not wrapped */
if (j < FirstRow || j > LastRow)
LastRow = j;
} else {
/* second case: wrapped */
if (j > LastRow && j < FirstRow)
LastRow = j;
}
}
/*
* Write a rule at the current row according to the (packed) information in
* 'info'. This includes the x position and the height and width of the
* rule.
*/
void
VWriteRule(info)
int info;
{
register unsigned char *bp; /* buffer pointer */
register int j;
register int lbits; /* bits along left */
register int rbits; /* bits along right */
register int o; /* offset to next row */
register int i;
register int full; /* number of 8 bit words to set */
register int height; /* rule height */
register int width; /* rule width */
register int row;
register int col;
i = info;
CurCol = (i & 0x7fff0000) >> 16;
height = ((i & 0xff00) >> 8);
width = (i & 0xff);
if ( Debug > 1 )
fprintf( stderr, "Rule: Row: %d, Col:%d, height:%d, width:%d\n",
CurRow, CurCol, height, width );
col = CurCol >> 3;
row = CurRow;
j = CurCol & 7; /* bit # of start position */
lbits = 0xff >> j; /* bits to set along left edge */
/* there are 8-j bits set in lbits */
o = 8 - j - width;
if (o > 0) { /* then lbits has o too many bits set */
lbits >>= o;
lbits <<= o; /* puts zeros into o righthand bits */
rbits = 0;
full = 0;
} else {
i = (CurCol + width) & 7; /* bit # of ending position */
rbits = 0xff00 >> i; /* bits to set along right edge */
/* there are i bits set in rbits (well, in the low byte) */
full = (width - i - (8 - j)) >> 3;
}
bp = &VBuffer[row][col];
i = height;
/* Often "full" is zero, which makes things faster */
if (full) { /* oh well */
o = COLUMNS - full - 1;
while (--i >= 0) {
*bp++ |= lbits;
for (j = full; --j >= 0;)
*bp++ |= 0xff;
*bp |= rbits;
if (++row >= ROWS) {
row = 0;
bp = &VBuffer[0][col];
} else
bp += o;
}
} else {
o = COLUMNS - 1;
while (--i >= 0) {
*bp++ |= lbits;
*bp |= rbits;
if (++row >= ROWS) {
row = 0;
bp = &VBuffer[0][col];
} else
bp += o;
}
}
i = CurRow + height - 1;
if (i >= ROWS)
i -= ROWS;
/*
* This is another way of expressing both cases 1 and 2 in
* VWriteChar(). I think the other way is likely to be
* faster, and characters occur far more frequently; but this
* is the more readable by far.
*/
if (RowsBetween(FirstRow, LastRow) < RowsBetween(FirstRow, i))
LastRow = i;
}
/*
* Dump out the top portion of the band (rows [Firstrow, CurRow)).
*/
void
DumpTopOfBand()
{
/*
* To exclude CurRow, subtract one, but modularly, modularly!
*/
WriteBuf(&VBuffer[0][0], FirstRow, CurRow ? CurRow - 1 : ROWS - 1, 1);
FirstRow = CurRow;
}
/*
* Move the current row in the band buffer down by delta rows, by,
* if necessary, writing out the currently-used portion of the buffer.
*/
void
MoveDown(delta)
register int delta;
{
if (delta >= ROWS - RowsBetween(FirstRow, CurRow)) {
/*
* Need to roll the cylinder forward. Write out the used
* part, and then write as many blank lines as necessary.
*/
WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
WriteBlanks(delta - RowsBetween(CurRow, LastRow) - 1);
CurRow = LastRow = FirstRow; /* band is now empty */
} else {
/*
* Because RowsBetween returns nonnegative integers, we
* know delta <= ROWS, so can do mod more quickly thus:
*/
CurRow += delta; /* result < 2*ROWS */
if (CurRow >= ROWS)
CurRow -= ROWS; /* now result < ROWS */
}
}
/*
* Count the bits in a byte
*/
static unsigned char nbits[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
/*
* Write the lines between the first and last inclusive from the given
* buffer. If 'cl', clear after writing.
* Filters pixels using a box filter (filth x filtw) in size.
*
* If buf is NULL, then just skips blank space.
*/
void
WriteBuf(buf, first, last, cl)
unsigned char *buf;
register int first, last;
int cl;
{
static unsigned char bytebuf[COLUMNS*8];
static int filtrow = 0, sawbit = 0;
unsigned char mask;
unsigned char * scans[2];
int row;
register int bit, col;
register unsigned char * cp, * bufp;
if ( Debug > 1 )
fprintf( stderr, "Writebuf( %lx, %d, %d, %d )\n",
buf, first, last, cl );
/* Setup RLE file if beginning of page */
if ( NewPage )
{
rle_put_setup( &rle_dflt_hdr );
NewPage = 0;
}
scans[0] = scans[1] = bytebuf;
if ( buf == NULL )
{
if ( filtrow > 0 && filtrow + last - first + 1 >= filth )
{
for ( col = 0; col < outwidth; col++ )
{
/* Why two lines? HP compiler bug! */
bit = bytebuf[col] * filtfact;
bytebuf[col] = bit;
}
rle_putrow( &scans[1], outwidth, &rle_dflt_hdr );
for ( col = 0; col < outwidth; col++ )
bytebuf[col] = 0;
}
NLines += last - first + 1;
if ( last - first - filtrow + 1 >= filth )
rle_skiprow( &rle_dflt_hdr,
(last - first - filtrow + 1) / filth );
filtrow = (filtrow + last - first + 1) % filth;
return;
}
if (first > last) { /* recursively do wrapped part first */
WriteBuf(buf, first, ROWS - 1, cl);
first = 0;
}
buf = &buf[first * COLUMNS];
mask = (0xff << (8 - filtw)) & 0xff;
for ( row = first, bufp = buf; row <= last; row++, bufp += COLUMNS )
{
for ( cp = bufp, col = 0, bit = 0; col < outwidth;
col++, bit += filtw, bit >= 8 ? cp++ : cp, bit %= 8 )
if ( (bytebuf[col] +=
nbits[(*cp & (mask >> bit))] +
nbits[((*(cp+1) & (mask << (8 - bit)))) & 0xff]) != 0 )
sawbit++;
filtrow = (filtrow + 1) % filth;
if ( filtrow == 0 )
{
if ( sawbit )
{
for ( col = 0; col < outwidth; col++ )
{
bit = bytebuf[col] * filtfact;
bytebuf[col] = bit;
}
rle_putrow( &scans[1], outwidth, &rle_dflt_hdr );
for ( col = 0; col < outwidth; col++ )
bytebuf[col] = 0;
}
else
rle_skiprow( &rle_dflt_hdr, 1 );
sawbit = 0;
}
}
if (cl)
bzero(buf, (unsigned) (COLUMNS * (last - first + 1)));
NLines += last - first + 1;
}
/*
* Write 'n' blank lines.
*/
void
WriteBlanks(n)
int n;
{
WriteBuf((unsigned char *)0, 0, n - 1, 0);
}
/*
* Perform a page feed.
*/
void
FormFeed()
{
if ( !NewPage )
rle_puteof( &rle_dflt_hdr );
NewPage = 1; /* next output will setup RLE file */
}
#ifdef hpux
void
bcopy( from, to, len )
{
return memcpy( to, from, len );
}
bzero( to, len )
{
memset( to, 0, len );
}
#endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.