/* 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 #include #include 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". * * */