Files
erkyrath.infocom-zcode-terps/mac/gfx.c
Andrew Plotkin b642da811e Initial commit.
2023-11-16 18:19:54 -05:00

235 lines
5.6 KiB
C

/*
Steps in picture compression (undo in reverse order):
1. Each line of the picture is exclusive-or'ed with the previous line.
2. A run-length encoding is applied, as follows: byte values 0
through 15 represent colors; byte values 16 through 127 are repetition
counts (16 will never actually appear) Thus: 3 occurrences of byte
value 2 will turn into 2 17 (subtract 15 from the 17 to find that the
two should be repeated 2 MORE times).
3. Optionally, the whole thing is Huffman-coded, using an encoding
specified in the header file. */
#define ULONG unsigned long /* 32 bits */
#define UWORD unsigned short /* 16 */
#define UCHAR unsigned char /* 8 */
/*------------------------------*/
/* uncompress_huff */
/*------------------------------*/
/* Uncompress a huffed picture.
Does all three steps simultaneously--unhuf, then undo rle and xor steps.
Stores extra line of 0s at beginning of outbuf, for xor step.
Returns number of decompressed bytes. */
ULONG /*int*/ uncompress_huff (inbuf, outbuf, huff_tree, inlen, pic_x)
unsigned char *inbuf, *outbuf, *huff_tree;
int pic_x, inlen;
{
unsigned char *p = &outbuf[pic_x];
int j, olen;
int /*UCHAR*/ chr, cnode = 0, cbit = 128, lastpix;
unsigned char *p_base = p;
olen = (inbuf[0] << 8) | inbuf[1]; /* Actual number of characters resulting from unhuf */
inbuf += 2;
for (j = 0; j < pic_x; j++)
outbuf[j] = 0;
chr = *inbuf++; /* Get the first character */
inlen--; /* etc */
while (1) {
if (chr & cbit) /* Proceed with huffman */
cnode = huff_tree[cnode + 1];
else
cnode = huff_tree[cnode];
if (cnode < 128)
cnode *= 2;
else {
cnode -= 128; /* this is a terminal */
/* here we undo both runlength and xor */
if (cnode < 16) { /* It's a color id, output/xor it */
*p++ = cnode ^ *outbuf++;
lastpix = cnode;
}
else { /* Otherwise, run/xor last color id */
for (j = 0; j < (cnode - 15); j++) {
*p++ = lastpix ^ *outbuf++;
}
}
if (--olen <= 0) break;
cnode = 0;
}
cbit = cbit >> 1; /* Next bit */
if (!cbit) { /* If done with this char, go to next */
if (inlen-- <= 0) break;
cbit = 128;
chr = *inbuf++;
}
}
return (p - p_base); /* final outlen */
}
/*------------------------------*/
/* uncompress_nohuff */
/*------------------------------*/
int uncompress_nohuff (inbuf, outbuf, inlen, pic_x)
unsigned char *inbuf, *outbuf;
int pic_x, inlen;
{
unsigned char *p = &outbuf[pic_x];
unsigned char *p_base = p;
int lastpix, val, j;
for (j = 0; j < pic_x; j++)
outbuf[j] = 0;
while (inlen--) {
val = *inbuf++;
if (val < 16) {
lastpix = val;
*p++ = val ^ *outbuf++;
}
else {
for (j = 0; j < (val - 15); j++) {
*p++ = lastpix ^ *outbuf++;
}
}
}
return (p - p_base); /* final outlen */
}
/*------------------------------*/
/* binary_search */
/*------------------------------*/
/* search a directory for a given id [first word of each entry],
return ptr, or null if not found */
short *binary_search (dir, e_count, e_size /*words*/, id)
short *dir;
int e_count, e_size, id;
{
register int top, bot, cur;
register int cid;
register short *p;
bot = 0;
top = e_count;
while (top > bot) {
cur = bot + ((top - bot) >> 1); /* DIV 2, round DOWN */
p = &dir[cur * e_size];
if ((cid = *p) == id)
return (p);
if (cid < id)
bot = cur + 1;
else top = cur;
}
return (0);
}
/** DEAD CALLS **/
#define incl_dead 0
#if incl_dead == 1
/* huf is Huffman tree, from header; len is number of bytes; buf1 is
actual data; buf2 is output data. Returns number of decoded bytes. */
int unhuf (huf, len, buf1, buf2)
unsigned char *huf, *buf1, *buf2;
int len;
{
int olen = 0;
int chr;
unsigned char *outbuf;
int cnode = 0, cbit = 128; /* Node 0 is always the root */
outbuf = buf2;
chr = *buf1++; /* Get a character */
len--;
while (1) {
if (chr & cbit) /* if the bit is one */
cnode = huf[cnode + 1]; /* then use the right-hand descendant of this node */
else
cnode = huf[cnode]; /* otherwise left */
if (cnode >= 128) { /* if descendant >= 128, it's a terminal */
*outbuf++ = cnode - 128; /* subtract 128, and dump it out. */
olen++;
cnode = 0;
}
else /* otherwise, get the byte offset of the new node */
cnode = 2 * cnode;
cbit = cbit >> 1; /* and move to the next bit */
if (!cbit) { /* If we've used up the whole byte, */
len--; /* get another one, if there are any */
if (!len) break;
cbit = 128;
chr = *buf1++;
}
}
return(olen);
}
/* Undo the run-length encoding.
buf1 is input, buf2 is output, len is number of bytes in the input,
The length of the output is known, since it must be the picture size;
however, we return it anyway for error checking.
Currently, we emit 1 byte/pixel, with no row padding.
[ x is pixels per row, rowBytes is bytes (padded) per row. ]
*/
int uncomp (buf1, buf2, len) /*, x, rowBytes */
unsigned char *buf1, *buf2;
int len;
/* int x, rowBytes; */
{
int i, j;
char chr;
unsigned char *b2base = buf2;
for (i = 0; i < len; i++) {
if (buf1[i] < 16)
/* <16 means this doesn't get changed */
*buf2++ = chr = buf1[i];
else {
/* Otherwise, repeat previous byte n-15 times */
for (j = 0; j < (buf1[i] - 15); j++)
*buf2++ = chr;
}
}
return (buf2 - b2base);
}
/* Undo the xor step.
Buf is input and output; x is picture rowBytes; y is picture y. */
void uneor (buf, x, y, step)
unsigned char *buf;
int x,y;
int step; /* either x or 2x */
{
int i;
/* xor element from next line with element from this line,
store in next line */
for (i = 0; i < (x * y) - step; i++)
buf[i + step] ^= buf[i];
}
#endif