/* writeip.c
*
* Module: writeip
* Owner: knox
* stdin: image internal format
* args:
* name (name of the output Interpress file)
* resolution (resolution of the destination printer)
* compression (type of compression, if any)
*
* Description:
* This program reads internal format images and puts them
* into Interpress files. The input internal memory image is read
* from standard input. The name of the output Interpress file is
* the first argument of the command line.
*
* The image raster will be stored in the file "name.ip",
* where name is read from the command line. The ".ip"
* extension will not be added if it is already present in the
* name. The mask raster present in the internal image will
* be applied to the image data in the file. The "oneiswhite" internal
* format data will be inverted to the "oneisblack" Interpress form.
* Both the image and mask rasters must be 1 bit/pixel.
*
* The second argument, if present, is the resolution of the
* printer on which the image will be printed. The resolution
* is a decimal number representing spots/inch. The default value
* is 300 spots/inch.
*
* If the third argument is present and is the string "compressed",
* then each output image strip will be written in the "compressed"
* format, formerly called IMG. If there is no string, then the
* image strip is written in "packed pixel" format.
*
* All scanlines are padded out with zeroes to a multiple of 32 bits,
* i.e. the number of pixels is rounded up to a multiple of 32.
* This solves two problems simultaneously. The first is that
* Print Services 8.0 and earlier assume packed pixel vectors
* to be padded to 16 bit boundaries. Print Services 9.0 and later
* assume packed pixel vectors to be padded to 32 bit boundaries.
* By rounding the number of pixels to 32, we can satisfy both of them
* at the same time. The second problem is that IMG ("compressed")
* compression requires the number of pixels to be a multiple of 8.
* By making the number of pixels to be a multiple of 32, we can satisfy
* IMG too.
*
*
*/
#include <internal.h>
#include <iptokens.h>
#include <stdio.h>
extern unsigned char *malloc();
extern struct header *getheader();
extern long ftell();
#define err0 "usage: writeip filename [resolution]\n"
#define err1 "writeip: Needs one resolution parameter, got %d!\n"
#define err2 "writeip: Resolution cannot be zero or negative, got %d!\n"
#define err3 "writeip: Output Interpress file could not be created, %s!\n"
#define err4 "writeip: Number of scanlines, %d, > 8.5*resolution!\n"
#define err5 "writeip: Number of pixels, %d, > 11*resolution!\n"
struct header *input;
char * program;
int resolution;
int compression;
int fdin;
FILE *fpout;
char *filename;
int npixels;
int nscanlines;
int tb←nzeroes[256];
int tb←nibble[256];
int tb←pos[256];
unsigned char tb←codes[4096];
unsigned char tb←lengths[1024];
#define max(a, b) (a > b ? a : b)
#define COMPRESSION←PACKED 0
#define COMPRESSION←IMG 1
main(argc, argv)
int argc;
char *argv[];
{
program = argv[0];
getargs(argc, argv);
input = getheader(fdin);
checkheader(argc, argv);
writefile();
exit(0);
}
getargs(argc, argv)
int argc;
char *argv[];
{
int n;
fdin = 0;
if (argc < 2) error(err0);
resolution = 300;
compression = COMPRESSION←PACKED;
if (argc < 3) return;
n = sscanf(argv[2], "%d", &resolution);
if (n != 1) error(err1, n);
if (resolution == 0 || resolution < 0) error(err2, resolution);
if (argc < 4) return;
if (strcmp(argv[3], "compressed") == 0)
{
compression = COMPRESSION←IMG;
inittables();
}
}
checkheader(argc, argv)
int argc;
char *argv[];
{
/* Variables. */
int width, depth;
/* Check for errors in the input header using library routines. */
check(input, program);
restrict(input, program, "imageExists", "1");
restrict(input, program, "bitsperPixel", "1");
restrict(input, program, "maskbitsperPixel", "1");
restrict(input, program, "signed", "0");
restrict(input, program, "trcLength", "0");
/* Check the size of the image. */
npixels = 32*((input->pixels+31)/32);
nscanlines = input->scanlines;
if (nscanlines > 17*resolution/2) error(err4, nscanlines);
if (npixels > 11*resolution) error(err5, npixels);
/* Make output RES file. */
filename = (char *) malloc(strlen(argv[1])+1+strlen(".ip"));
strcpy(filename, argv[1]);
if (strcmp(".ip", strrchr(filename, '.')) != 0) strcat(filename, ".ip");
fpout = fopen(filename, "w");
if (fpout == NULL) error(err3, filename);
}
writefile()
{
/* variables */
int n, bytesperSL, nblocks, blocksize, total;
unsigned char *image, *mask;
/* start Interpress file */
AppendHeader(fpout, IP←Header);
AppendOp(OP←beginBlock); /* BEGIN */
AppendOp(OP←beginBody); /* { */
AppendOp(OP←endBody); /* } */
AppendOp(OP←beginBody); /* { */
AppendRational(254, 10000*resolution); /* convert inches to meters */
AppendOp(OP←scale);
AppendOp(OP←concatt);
AppendInteger(17*resolution/4); /* xDimension, middle of the page */
AppendInteger(11*resolution/2); /* yDimension, middle of the page */
AppendOp(OP←setxy);
AppendRational(-1, 2);
AppendOp(OP←scale);
AppendOp(OP←concatt);
AppendInteger(nscanlines); /* xDimension of the image */
AppendInteger(npixels); /* yDimension of the image */
AppendOp(OP←setxyrel);
AppendRational(-2, 1);
AppendOp(OP←scale);
AppendOp(OP←concatt);
/* npixels must always be a multiple of 32. */
bytesperSL = npixels/8;
/* allocate buffer space for scanlines */
image = malloc(max(bytesperSL, input->bytesperSL));
if (input->maskExists) mask = malloc(input->maskbytesperSL);
/* send the raster data in blocks */
total = nscanlines;
blocksize = (64*1024-4-16)/bytesperSL;
nblocks = nscanlines/blocksize;
for (n=0; n < nblocks; n++)
{
writepixelarray(image, mask, blocksize, bytesperSL);
total -= blocksize;
}
if (total > 0) writepixelarray(image, mask, total, bytesperSL);
free(image);
if (input->maskExists) free(mask);
/* wrap up the rest of the Interpress file. */
AppendOp(OP←endBody); /* } */
AppendOp(OP←endBlock); /* END */
}
writepixelarray(image, mask, blocksize, bytesperSL)
unsigned char *image, *mask;
int blocksize, bytesperSL;
{
int n, m;
AppendInteger(blocksize); /* xPixels */
AppendInteger(npixels); /* yPixels */
AppendInteger(1); /* samples/pixel */
AppendInteger(1); /* max sample value */
AppendInteger(0); /* samples interleaved */
AppendInteger(1);
AppendOp(OP←scale); /* transformation */
if (compression == COMPRESSION←PACKED)
writepackedpixels(image, mask, blocksize, bytesperSL);
else
writecompressedpixels(image, mask, blocksize, bytesperSL);
AppendOp(OP←makepixelarray); /* maskImage */
AppendOp(OP←trans);
AppendOp(OP←maskpixel); /* image the mask */
AppendInteger(blocksize); /* xDimension of the image */
AppendOp(OP←setxrel);
}
writepackedpixels(image, mask, blocksize, bytesperSL)
unsigned char *image, *mask;
int blocksize, bytesperSL;
{
int n, m;
AppendPPV(4+bytesperSL*blocksize, input->bitsperPixel, npixels);
for (n=0; n < blocksize; n++)
{
readscanline(image, mask);
fwrite(image, 1, bytesperSL, fpout);
}
}
#define IMG←RAW 0
#define IMG←BITBACK 1
#define IMG←ENCODED 2
#define IMG←BYTEBACK 3
#define IMG←SOI 0x70
#define IMG←EOI 0x71
writecompressedpixels(image, mask, blocksize, bytesperSL)
unsigned char *image, *mask;
int blocksize, bytesperSL;
{
long start, end, data, total;
int n, m, breakcount, nbytes, nibbles, mode;
int rawlen, bitbacklen, encodedlen, bytebacklen;
unsigned char *previous, *bitback, *encoded, *byteback, *temp, *choice;
previous = malloc(input->bytesperSL+2);
bitback = malloc(input->bytesperSL+2); /* Two bytes used in makeencoded(). */
encoded = malloc(input->bytesperSL+2);
byteback = malloc(input->bytesperSL+2);
temp = malloc(input->bytesperSL+2);
start = ftell(fpout);
nbytes = npixels/8;
AppendCPV(256, 0, 8, npixels); /* The 256 guarantees a LONG SEQUENCE. */
data = ftell(fpout);
writenibbles(IMG←SOI);
breakcount=16;
rawlen = 2*nbytes;
for (n=0; n < blocksize; n++)
{
/* Encode in each of the four ways. */
readscanline(image, mask);
if (breakcount >= 16) bitbacklen = rawlen+1; /* i.e. don't use this mode. */
else bitbacklen = makebitback(image, previous, bitback, temp, nbytes);
encodedlen = makeencoded(image, encoded, nbytes);
bytebacklen = makebyteback(image, byteback, temp, nbytes);
bcopy(image, previous, input->bytesperSL);
/* Decide which is the shortest encoding and write it out. */
mode = IMG←RAW;
nibbles = rawlen;
choice = image;
if (encodedlen < nibbles)
{ mode = IMG←ENCODED; nibbles = encodedlen; choice = encoded; }
if (bitbacklen < nibbles)
{ mode = IMG←BITBACK; nibbles = bitbacklen; choice = bitback; }
if (bytebacklen < nibbles)
{ mode = IMG←BYTEBACK; nibbles = bytebacklen; choice = byteback; }
if (mode == IMG←BITBACK) breakcount++;
else breakcount = 1;
writenibbles(mode, nibbles, choice);
}
writenibbles(IMG←EOI);
end = ftell(fpout);
total = end-data+6; /* add in length of breaktable, range and scanLength. */
if (total < 256) /* A long sequence requires at least 256 data bytes. */
{
for (n=total; n < 256; n++) putc(0, fpout);
total = 256;
end = data+256-6;
}
if ((total & 1) == 1) /* Interpress requires all pixel vectors be even. */
{
putc(0, fpout);
total++;
end++;
}
fseek(fpout, start, 0);
AppendCPV(total, 0, 8, npixels);
fseek(fpout, end, 0);
free(previous);
free(bitback);
free(encoded);
free(byteback);
free(temp);
}
readscanline(image, mask)
unsigned char *image, *mask;
{
int n, nbytes, remainder, bytesperSL, endbytes;
static int endmask[8] = { 0377, 0200, 0300, 0340, 0360, 0370, 0374, 0376 };
bytesperSL = npixels/8;
nbytes = (input->pixels+7)/8;
remainder = input->pixels & 7;
endbytes = bytesperSL-nbytes;
readbytes(fdin, image, input->bytesperSL);
for (n=0; n < input->bytesperSL; n++) image[n] = ~image[n];
if (input->maskExists)
{
readbytes(fdin, mask, input->maskbytesperSL);
hardmask(image, mask, nbytes);
}
if (endbytes) for (n=0; n < endbytes; n++) image[nbytes+n] = 0;
if (remainder == 0) return;
image[nbytes-1] &= endmask[remainder];
}
makebitback(image, previous, out, temp, nbytes)
unsigned char *image, *previous, *out, *temp;
int nbytes;
{
int n;
for (n=0; n < nbytes; n++) temp[n] = image[n] ↑ previous[n];
return(makeencoded(temp, out, nbytes));
}
makebyteback(image, out, temp, nbytes)
unsigned char *image, *out, *temp;
int nbytes;
{
int n;
temp[0] = image[0];
for (n=1; n < nbytes; n++) temp[n] = image[n] ↑ image[n-1];
return(makeencoded(temp, out, nbytes));
}
makeencoded(image, out, nbytes)
unsigned char *image, *out;
int nbytes;
{
int pos, nibble, nzeroes, limit;
int result, unitsof64;
limit = 2*nbytes;
result = 0;
pos = 0;
*out = 0;
while (1)
{
findnibble(image, &pos, &nibble, &nzeroes, nbytes);
if (nibble == 0) break;
unitsof64 = nzeroes >> 6;
if (unitsof64)
{
while (unitsof64 > 63)
{
result += appendcodes(out, result, 0, 63, limit);
unitsof64 -= 63;
}
if (unitsof64) result += appendcodes(out, result, 0, unitsof64, limit);
}
result += appendcodes(out, result, nibble, nzeroes & 63, limit);
}
if (result < limit) return(result);
else return(limit+1);
}
appendcodes(out, loc, nibble, nzeroes, limit)
unsigned char *out;
int loc, nibble, nzeroes, limit;
{
int initial, offset;
unsigned char *codeptr;
if (loc >= limit) return(0);
out += loc/2;
offset = (nzeroes << 4)+nibble;
codeptr = tb←codes+(offset << 2);
initial = 0;
if (loc & 1)
{
codeptr += 2;
initial = *out;
}
*out++ = initial+*codeptr++;
*out = *codeptr;
return(tb←lengths[offset]);
}
findnibble(image, posptr, nibbleptr, nzeroesptr, nbytes)
unsigned char *image;
int *posptr, *nibbleptr, *nzeroesptr, nbytes;
{
int n, v;
*nzeroesptr = 0;
*nibbleptr = 0;
if ((*posptr & 1) == 1)
{
*nibbleptr = 15 & image[(*posptr)/2];
*posptr += 1;
if (*nibbleptr) return;
*nzeroesptr = 1;
}
for (n=(*posptr)/2; n < nbytes; n++)
{
v = image[n];
if (v == 0)
{
*nzeroesptr += 2;
*posptr += 2;
}
else
{
*nzeroesptr += tb←nzeroes[v];
*nibbleptr = tb←nibble[v];
*posptr += tb←pos[v];
return;
}
}
}
writenibbles(mode, length, buffer)
int mode, length;
unsigned char *buffer;
{
static number = 0;
static int leftovers = 0;
static int nibble = 0;
int nbytes;
switch (mode)
{
case IMG←SOI:
fputc(IMG←SOI, fpout);
leftovers = 0;
return;
case IMG←EOI:
if (leftovers)
{
fputc(nibble+(IMG←EOI >> 4), fpout);
fputc((IMG←EOI & 15) << 4, fpout);
}
else fputc(IMG←EOI, fpout);
return;
default:
if (leftovers)
{
fputc(nibble+(mode >> 4), fpout);
if (length)
{
fputc(((mode & 15) << 4)+(buffer[0] >> 4), fpout);
length--;
leftshift(buffer, length);
leftovers = 0;
}
else
{
nibble = (mode & 15) << 4;
leftovers = 1;
}
}
else fputc(mode, fpout);
nbytes = length/2;
if (nbytes) fwrite(buffer, 1, nbytes, fpout);
if (length & 1)
{
leftovers = 1;
nibble = buffer[nbytes];
}
}
}
leftshift(buffer, length)
unsigned char *buffer;
int length;
{
int n;
for (n=0; n < length/2; n++)
{
*buffer++ = ((*buffer << 4)+(*(buffer+1) >> 4)) & 255;
}
if (length & 1) *buffer = (*buffer << 4) & 255;
}
inittables()
{
int n, nzeroes, nibble, temp;
for (n=1; n < 16; n++)
{
tb←nzeroes[n] = 1;
tb←nibble[n] = n & 15;
tb←pos[n] = 2;
}
for (n=16; n < 256; n++)
{
tb←nzeroes[n] = 0;
tb←nibble[n] = (n >> 4) & 15;
tb←pos[n] = 1;
}
bzero(tb←codes, 4096);
for (n=0; n < 1024; n++)
{
nzeroes = (n >> 4) & 63;
nibble = n & 15;
if (nibble == 8 || nibble == 4 || nibble == 2 || nibble == 1)
{
if (nzeroes == 0)
{
tb←lengths[n] = 1;
temp = 8+Axx(nibble);
code12bits(tb←codes+4*n, temp << 8);
}
else
{
if (nzeroes > 25)
{
tb←lengths[n] = 3;
temp = 3072+(nzeroes << 4)+nibble;
code12bits(tb←codes+4*n, temp);
}
else
{
tb←lengths[n] = 2;
temp = (nzeroes << 2)+Axx(nibble);
code12bits(tb←codes+4*n, temp << 4);
}
}
}
else
{
if (nzeroes > 1 || nibble == 0)
{
tb←lengths[n] = 3;
temp = 3072+(nzeroes << 4)+nibble;
code12bits(tb←codes+4*n, temp);
}
else
{
tb←lengths[n] = 2;
if (nibble == 3) nibble = 4;
temp = 96+(nibble << 1)+nzeroes;
code12bits(tb←codes+4*n, temp << 4);
}
}
}
}
Axx(nibble)
int nibble;
{
if (nibble == 8) return(0);
if (nibble == 4) return(1);
if (nibble == 2) return(2);
return(3);
}
code12bits(table, code)
unsigned char *table;
int code;
{
*table++ = (code >> 4) & 255;
*table++ = (code << 4) & 240;
*table++ = (code >> 8) & 15;
*table++ = code & 255;
}
/* Change Log
*
* K. Knox, 21-Feb-85 18:48:54, Created first version.
* K. Knox, 7-May-85 15:25:19, Added padding parameter.
* K. Knox, 6-Jun-85 15:19:57, Corrected padding parameter, did 32 bits wrong.
* K. Knox, 1-Nov-85 17:47:54, Added IMG compression, removed padding parameter.
* K. Knox, 6-Dec-85 9:04:08, Interpress requires all pixel vectors be even.
* K. Knox, 6-Dec-85 9:04:08, Changed "IMG" to "compressed".
*
*
*/