/* writeres.c
 *
 * Module: writeres
 * Owner: knox
 * stdin: image internal format
 * args:
 *   -i           (if present, a request to write the internal file on stdout)
 *   name         (name of the output res file)
 *   resolution   (if present, the resolution of the image in spots/inch)
 *
 * Description:
 *    This program reads internal format images and puts them
 *    into RES files.  The input internal memory image is read
 *    from standard input.  If the flag, -i, is the first argument
 *    then the internal file is written onto the standard output.
 *    If this flag is absent then the name of the output RES file is
 *    the first argument of the command line.
 *
 *    The image raster will be stored in the file "name.res",
 *    where name is read from the command line.  The ".res"
 *    extension will not be added if it is already present in the
 *    name.  The mask raster present in the internal image will
 *    be recorded with the image data in the file.  Both the image
 *    and mask rasters may be either 1 or 8 bits/pixel.
 *
 *    If an argument follows the name on the command line, then
 *    it is the resolution at which the image was originally created.
 *    If this argument is not present, then a resolution of 300 spi
 *    is assumed.  The resolution parameter must be an integer.
 *
 */

#include <time.h>
#include <fcntl.h>
#include <internal.h>
#include <iptokens.h>

extern unsigned char *malloc();
extern struct header *getheader();
extern long timezone;

#define err0 "writeres: No output RES file name!\n"
#define err1 "writeres: Needs one resolution parameter, got %d!\n"
#define err2 "writeres: Resolution cannot be zero!"
#define err3 "writeres: Output RES file could not be created, %s!\n"

#define RES←header "Interpress/Xerox/2.1/RasterEncoding/1.0 "
#define BUFFSIZE 65536
#define BLOCKSIZE 16777215   /* 16*1024*1024-1 */

#define len←longOP    2
#define len←shortNUM  2
#define len←shortSEQ  2
#define len←longSEQ   4


#define max(a, b) (a > b ? a : b)

struct header *input;
char * program;
int resolution = 300;
int fdin, fdout, fdint;
int internal = 0;
int index = 2;
char *filename;
int masklen;
int imagelen;
long startmask;
long startimage;

struct blocks
  {
  int depth;
  long *bytelength;
  int current;
  int bytesused;
  };

struct blocks maskblocks;
struct blocks imageblocks;

main(argc, argv)
  int argc;
  char *argv[];
  {
  program = argv[0];
  getargs(argc, argv);
  input = getheader(fdin);
  checkheader(argc, argv);
  if (internal) putheader(fdint, input);
  writedata();
  writecodes();
  exit(0);
  }

getargs(argc, argv)
  int argc;
  char *argv[];
  {
  int n;
  fdin = 0;
  fdint = 1;
  if (argc < 2) error(err0);
  if (strcmp(argv[1], "-i") == 0)
    {
    internal = 1;
    index = 3;
    }
  if (argc < index) error(err0);
  if (argc > index)
    {
    n = sscanf(argv[index], "%d", &resolution);
    if (n != 1) error(err1, n);
    if (resolution == 0) error(err2);
    }
  }


checkheader(argc, argv)
  int argc;
  char *argv[];
  {
  /* variables */
  int size;
  char *temp;

  /* Check for errors in the input header using library routines. */
  check(input, program);
  restrict(input, program, "bitsperPixel", "1 8");
  restrict(input, program, "maskbitsperPixel", "1 8");
  restrict(input, program, "signed", "0");
  restrict(input, program, "trcLength", "0");
 
  /* Make output RES file. */
  fdout = 0;
  filename = (char *) malloc(strlen(argv[index-1])+1+strlen(".res"));
  strcpy(filename, argv[index-1]);
  if (strcmp(".res", strrchr(filename, '.')) != 0) strcat(filename, ".res");
  ImageSize();
  size = RESfilesize();
  fdout = open(filename, O←WRONLY | O←CREAT | O←CTG, 0666, size);
  if (fdout == -1) fdout = open(filename, O←WRONLY | O←CREAT, 0666);
  if (fdout == -1) error(err3, filename);
  }



writedata()
  {
  /* allocate buffer space for scanlines */
  int n, firstwrite;
  int imagecount, imagebuffersize, imageroom;
  int maskcount, maskbuffersize, maskroom;
  long imagefp, maskfp;
  unsigned char *mask, *image;
  firstwrite = 1;
  if (input->imageExists)
    {
    imageroom = max(imagelen, input->bytesperSL);  /* room for either input or output */
    imagebuffersize = max(imageroom, BUFFSIZE);  /* make sure one line can fit */
    image = malloc(imagebuffersize);
    }
  if (input->maskExists)
    {
    maskroom = max(masklen, input->maskbytesperSL);
    maskbuffersize = max(maskroom, BUFFSIZE);
    mask = malloc(maskbuffersize);
    }
  imagefp = startimage;
  maskfp = startmask;

  /* read and send the raster data */
  imagecount = maskcount = 0;
  for (n=0; n < input->scanlines; n++)
    {
    if (input->imageExists)
      {
      readbytes(fdin, image+imagecount, input->bytesperSL);
      if (internal) writebytes(fdint, image+imagecount, input->bytesperSL);
      imagecount += imagelen;
      if (((imagebuffersize-imagecount) < imageroom)  || (n == (input->scanlines-1)))
        {
        if (input->maskExists != 0 || firstwrite != 0) lseek(fdout, imagefp, 0);
        imagefp += writescanlines(fdout, image, imagecount, &imageblocks);
	imagecount = 0;
        firstwrite = 0;
	}
      }
    if (input->maskExists)
      {
      readbytes(fdin, mask+maskcount, input->maskbytesperSL);
      if (internal) writebytes(fdint, mask+maskcount, input->maskbytesperSL);
      maskcount += masklen;
      if (((maskbuffersize-maskcount) < maskroom) || (n == (input->scanlines-1)))
        {
        if (input->imageExists != 0 || firstwrite != 0) lseek(fdout, maskfp, 0);
        maskfp += writescanlines(fdout, mask, maskcount, &maskblocks);
	maskcount = 0;
        firstwrite = 0;
	}
      }
    }
  if (input->imageExists) free(image);
  if (input->maskExists) free(mask);
  }


writescanlines(fd, buffer, count, ptrblocks)
  int fd;
  unsigned char *buffer;
  int count;
  struct blocks *ptrblocks;
  {
  /* It fits completely in this block. */
  int bytesleft, padding, current;
  bytesleft = ptrblocks->bytelength[ptrblocks->current]-ptrblocks->bytesused;
  if (count < bytesleft)
    {
    write(fd, buffer, count);
    ptrblocks->bytesused += count;
    return(count);
    }

  /* It goes into the next block, too. */
  write(fd, buffer, bytesleft);
  ptrblocks->current++;
  ptrblocks->bytesused = 0;
  current = ptrblocks->current;
  padding = 0;
  if (current < ptrblocks->depth)
    {
    padding = (ptrblocks->bytelength[current] > 255) ? len←longSEQ : len←shortSEQ;
    }
  if (padding) lseek(fd, padding, 1);
  buffer += bytesleft;
  count -= bytesleft;
  if (count) count = writescanlines(fd, buffer, count, ptrblocks);
  return(bytesleft+padding+count);
  }


extern char *RESdate();

writecodes()
  {
  /* start RES file */
  int n;
  char *date;
  lseek(fdout, (long) 0, 0);
  AppendHeader(fdout, RES←header);  /* "Interpress/Xerox/2.1/RasterEncoding/1.0 " */
  AppendOp(OP←beginBlock);          /* BEGIN */
  AppendRational(254, 3000000);     /* scale in x */
  AppendOp(OP←dup);                 /* scale in y */
  AppendInteger(2);
  AppendOp(OP←makevec);             /* imageScale */
  AppendInteger(input->pixels);     /* xDimension */
  AppendInteger(input->scanlines);  /* yDimension */

  /* Do the mask pixel array. */
  if (input->maskExists)
    {
    AppendInteger(input->scanlines);                  /* xPixels */
    AppendInteger(input->pixels);                     /* yPixels */
    AppendInteger(1);                                 /* samples/pixel */
    AppendInteger((1 << input->maskbitsperPixel)-1);  /* max sample value */
    AppendInteger(0);                                 /* samples interleaved */
    AppendInteger(-90);
    AppendOp(OP←rotate);
    AppendInteger(0);
    AppendInteger(input->scanlines);                  /* xPixels */
    AppendOp(OP←translate);
    AppendOp(OP←concat);                              /* transformation */
    AppendPPV(maskblocks.bytelength[0],
      input->maskbitsperPixel, input->pixels);        /* packed pixel vector */
    lseek(fdout, maskblocks.bytelength[0]-4, 1);
    for (n=1; n < maskblocks.depth; n++)
      {
      AppendSeqContinued(maskblocks.bytelength[n]);
      lseek(fdout, maskblocks.bytelength[n], 1);
      }
    AppendOp(OP←makepixelarray);
    }
  else AppendInteger(0);  /* null maskImage */

  /* Do the image pixel array. */
  if (input->imageExists)
    {
    AppendInteger(input->scanlines);                  /* xPixels */
    AppendInteger(input->pixels);                     /* yPixels */
    AppendInteger(1);                                 /* samples/pixel */
    AppendInteger((1 << input->bitsperPixel)-1);      /* max sample value */
    AppendInteger(0);                                 /* samples interleaved */
    AppendInteger(-90);
    AppendOp(OP←rotate);
    AppendInteger(0);
    AppendInteger(input->scanlines);                  /* xPixels */
    AppendOp(OP←translate);
    AppendOp(OP←concat);                              /* transformation */
    AppendPPV(imageblocks.bytelength[0],
      input->bitsperPixel, input->pixels);   /* packed pixel vector */
    lseek(fdout, imageblocks.bytelength[0]-4, 1);
    for (n=1; n < imageblocks.depth; n++)
      {
      AppendSeqContinued(imageblocks.bytelength[n]);
      lseek(fdout, imageblocks.bytelength[n], 1);
      }
    AppendOp(OP←makepixelarray);
    }
  else AppendInteger(0);  /* null colorImage */

  /* Write out the color operator. */
  if (input->imageExists)
    {
    AppendInteger((1 << input->bitsperPixel)-1);  /* swhite */
    AppendInteger(0);               /* sblack */
    AppendInteger(0);               /* pixelMap */
    AppendInteger(3);               /* 3 */
    AppendOp(OP←makevec);           /* MAKEVEC */
    AppendIdentifier("xerox");      /* xerox */
    AppendIdentifier("GrayLinear"); /* GrayLinear */
    AppendInteger(2);               /* 2 */
    AppendOp(OP←makevec);           /* MAKEVEC */
    AppendOp(OP←findcolormodeloperator);  /* FINDCOLORMODELOP */
    AppendOp(OP←do);                /* DO */
    }

  /* Append image properties. */
  AppendIdentifier("name");         /* name */
  AppendString(filename);           /* filename */
  AppendIdentifier("creationTime"); /* creationTime */
  AppendString((date = RESdate())); /* date */
  AppendInteger(4);                 /* 4 */
  AppendOp(OP←makevec);             /* MAKEVEC */

  /* Wrap up the RES file. */
  AppendInteger(13086);             /* signature */
  AppendOp(OP←endBlock);            /* END */
  free(date);
  }

ImageSize()
  {
  /* How big is the mask. */
  int n, depth;
  long total;
  if (input->maskExists)
    {
    masklen = 4*((input->pixels*input->maskbitsperPixel+31)/32);
    total = 4+input->scanlines*masklen;
    depth = (total+BLOCKSIZE-1)/BLOCKSIZE;
    maskblocks.depth = depth;
    maskblocks.bytelength = (long *) malloc(depth*sizeof(long *));
    for (n=0; n < depth-1; n++)
      {
      maskblocks.bytelength[n] = BLOCKSIZE;
      total -= BLOCKSIZE;
      }
    maskblocks.bytelength[depth-1] = total;
    maskblocks.current = 0;
    maskblocks.bytesused = 4;
    }
  
  /* How big is the image. */
  if (input->imageExists)
    {
    imagelen = 4*((input->pixels*input->bitsperPixel+31)/32);
    total = 4+input->scanlines*imagelen;
    depth = (total+BLOCKSIZE-1)/BLOCKSIZE;
    imageblocks.depth = depth;
    imageblocks.bytelength = (long *) malloc(depth*sizeof(long *));
    for (n=0; n < depth-1; n++)
      {
      imageblocks.bytelength[n] = BLOCKSIZE;
      total -= BLOCKSIZE;
      }
    imageblocks.bytelength[depth-1] = total;
    imageblocks.current = 0;
    imageblocks.bytesused = 4;
    }
  }


RESfilesize()
  {
  int n, size;
  size = 0;
  /* RES header */  size += strlen(RES←header);
  /* BEGIN */       size += len←longOP;
  /* rational */    size += len←shortSEQ+2*bytes←in←int(resolution*10000);
  /* DUP */         size += len←longOP;
  /* 2 */           size += len←shortNUM;
  /* MAKEVEC */     size += len←longOP;
  /* xDimension */  size += len←shortNUM;
  /* yDimension */  size += len←shortNUM;
  /* maskImage */   if (input->maskExists == 0) size += 2;
    else
      {
      /* xPixels */             size += len←shortNUM;
      /* yPixels */             size += len←shortNUM;
      /* samplesPerPixel */     size += len←shortNUM;
      /* maxSampleValues */     size += len←shortNUM;
      /* samplesInterleaved */  size += len←shortNUM;
      /* -90 */                 size += len←shortNUM;
      /* ROTATE */              size += len←longOP;
      /* 0 */                   size += len←shortNUM;
      /* xPixels */             size += len←shortNUM;
      /* TRANSLATE */           size += len←longOP;
      /* CONCAT */              size += len←longOP;
      /* sequencePPV */         size += (maskblocks.bytelength[0] > 255) ? len←longSEQ : len←shortSEQ;
      /* BitsPerSample */       size += 2;
      /* ScanLength */          size += 2;
                                startmask = size;
      /* compressed data */     size += input->scanlines*masklen;
      /* sequenceContinued */   for (n=1; n < maskblocks.depth; n++)
                                  {
				  size += (maskblocks.bytelength[n] > 255) ? len←longSEQ : len←shortSEQ;
				  }
      /* MAKEPIXELARRAY */      size += len←longOP;
      }
  /* colorImage */  if (input->imageExists == 0) size += 2;
    else
      {
      /* xPixels */             size += len←shortNUM;
      /* yPixels */             size += len←shortNUM;
      /* samplesPerPixel */     size += len←shortNUM;
      /* maxSampleValues */     size += len←shortNUM;
      /* samplesInterleaved */  size += len←shortNUM;
      /* -90 */                 size += len←shortNUM;
      /* ROTATE */              size += len←longOP;
      /* 0 */                   size += len←shortNUM;
      /* xPixels */             size += len←shortNUM;
      /* TRANSLATE */           size += len←longOP;
      /* CONCAT */              size += len←longOP;
      /* sequencePPV */         size += (imageblocks.bytelength[0] > 255) ? len←longSEQ : len←shortSEQ;
      /* BitsPerSample */       size += 2;
      /* ScanLength */          size += 2;
                                startimage = size;
      /* compressed data */     size += input->scanlines*imagelen;
      /* sequenceContinued */   for (n=1; n < imageblocks.depth; n++)
                                  {
				  size += (imageblocks.bytelength[n] > 255) ? len←longSEQ : len←shortSEQ;
				  }
      /* MAKEPIXELARRAY */      size += len←longOP;
      /* swhite */              size += len←shortNUM;
      /* sblack */              size += len←shortNUM;
      /* pixelMap */            size += len←shortNUM;
      /* 3 */                   size += len←shortNUM;
      /* MAKEVEC */             size += len←longOP;
      /* xerox */               size += len←shortSEQ+strlen("xerox");
      /* GrayLinear */          size += len←shortSEQ+strlen("GrayLinear");
      /* 2 */                   size += len←shortNUM;
      /* MAKEVEC */             size += len←longOP;
      /* FINDCOLORMODELOP */    size += len←longOP;
      /* DO */                  size += len←longOP;
      }
  /* name */          size += len←shortSEQ+strlen("name");
  /* filename */      size += len←shortSEQ+strlen(filename);
  /* creationTime */  size += len←shortSEQ+strlen("creationTime");
  /* date */          size += len←shortSEQ+strlen("1983 10 12 13:22:15-05:00");
  /* 4 */             size += len←shortNUM;
  /* MAKEVEC */       size += len←longOP;
  /* 13086 */         size += len←shortNUM;
  /* END */           size += len←longOP;
  return(size);
  }


extern struct tm *localtime();

char *RESdate()
  {
  int tz;
  char *date, *op;
  struct tm *ptm;
  long tloc;
  tz = (timezone < 0) ? (-timezone)/3600 : timezone/3600;
  op = (timezone < 0) ? "+" : "-";
  date = (char *) malloc(26);
  tloc = time((long *) 0);
  ptm = localtime(&tloc);
  sprintf(date, "%4d %02d %02d %02d:%02d:%02d%s%02d:00",
    ptm->tm←year+1900, ptm->tm←mon+1, ptm->tm←mday, ptm->tm←hour,
      ptm->tm←min, ptm->tm←sec, op, tz);
  return(date);
  }



/* Change Log
 *
 * K. Knox,  1-Apr-85 14:25:59, Created first version.
 * K. Knox, 10-May-85 18:03:10, Changed transformation from landscape to portrait.
 * K. Knox, 20-May-85 10:17:48, Added "-i" flag for outputting internal image.
 * K. Knox, 22-Aug-85 18:16:00, Changed buffer size to 64K bytes.
 * K. Knox, 23-Sep-85 14:48:39, Put xDim and yDim in proper order (were reversed).
 *
 *
 */