/*
 *  Writeilbm.c
 *
 *  Writes out a 24 bit IFF ILBM file, compressed or uncompressed from a 24 or 32 bit
 *  Sun Raster file.
 *  By Brett Van Sprewenburg
 * 
 *  A nice addition would be to convert regular 8 bit rasters to 24 bit, then write the ilbm.
 *  (see version 1.4, hehe)
 *
 *  Credit to the ppm utilities, the developer(s) of xv, and Commodore-Amiga for
 *  code and ideas.
 *
 * Version
 *   1.0 	2/1/93	First working version, with hardcoded form and body chunk sizes.
 *			Only uncompressed images work.
 *   1.1	2/2/93	Added in the correct form and body sizes with compression by altering
 *			the file after it was written.
 *
 *   1.2	2/4/93	First working version with compression done correctly. I still wouldn't
 *			be able to tell you exactly what was wrong. Fixed multiple things with
 *			readilbm integration. The biggest problem with this version is that it's sloooow.
 *  			A faster way may be to do 8 planes at once, instead of a plane at a time.
 *   1.3	2/9/93  Fixed previous slowness by compiling with acc -fast. ANIS'fied code also for 
 *			compilation with acc without warnings.
 *   1.4	2/10/93 Added reading and conversion of 8 bit rasters into 24 bit ilbms.
 *   1.4.1	2/12/93	Checked that memeory allocations succeded.
 *			First public release.
 */

#include <writeilbm.h>

int WriteILBM(char *, int);

extern byte *pic;		  /* Pointer to pixel interleaved raster */
				  /* Or index into cmap if < 24 bit planes */
extern byte r[255],g[255],b[255]; /* Said cmap */

int WriteILBM(char *ilbmfile, int compression)
{
  FILE *fp;
  long formsize,bmhdsize,bodysize;
  byte *coded_rowbuf,*cp,*raw_rowbuf;
  int rel_count,rgbline,linesize,mask,yoffset,offset,push;
  register int row, col, plane;
  unsigned int bit;
  static byte *image,*hpic,*ebitimage,*tpic;
  int pad,tot_bytes,packedbytes,ycoffset,coffset,ccol = 0;

  tot_bytes = packedbytes = 0;

  if ((fp = fopen(ilbmfile,"w")) == NULL) {
    fprintf(stderr,"Open of '%s' for writing failed.\n",ilbmfile); 
    return -1;
  }

  /* I'm fairly certain that I can't handle an odd sized bitmap, so I'll just turn 'em away */
  if ((sunheader.ras_width % 8) != 0) {
    fprintf(stderr,"Width of '%d' is not gonna work. Must be multiple of 8.\n",sunheader.ras_width);
    fclose(fp);
    return -1;
  }

  if (sunheader.ras_depth < 24) {	/* Convert 8 to 24 bits */
     if (sunheader.ras_maptype != RMT_EQUAL_RGB) {
       fprintf(stderr,"Unknown colormap type.  Save from 'xv' and try again.\n");
       fclose(fp);
       return -1;
     }
     tpic = pic;
     fprintf(stderr,"Converting Sun Raster to 24 bit planes.\n");
     hpic = (byte *)malloc(sunheader.ras_height * sunheader.ras_width * 3);
     if (hpic == NULL) {
	fprintf(stderr,"Can't get the memory I need...\n");
	fclose(fp);
	return -2;
     }
     ebitimage = hpic; 

     for (row = 0; row < (int)sunheader.ras_height; row++) {
	yoffset = row * sunheader.ras_width;
 	ycoffset = row * (sunheader.ras_width * 3);
       for (col = 0; col < (int)sunheader.ras_width; col++) {
	 offset = yoffset + col;
 	 coffset = ycoffset + ccol;
	 *(hpic + coffset) = r[*(tpic + offset)];
	 coffset++;
	 *(hpic + coffset) = g[*(tpic + offset)];
	 coffset++;
	 *(hpic + coffset) = b[*(tpic + offset)];
	 ccol+=3;
	 if (ccol == (sunheader.ras_width * 3))
	   ccol = 0;
       }
     }
     pic = ebitimage;
  }

  /* Assign information to ilbm header */
  /* See AmigaDOS hardware RKM for more info */
  ilbmheader.w = (unsigned short)sunheader.ras_width;
  ilbmheader.h = (unsigned short)sunheader.ras_height;
  ilbmheader.x = 0;
  ilbmheader.y = 0;
  ilbmheader.nPlanes = 24;		/* We're only going to write 24 bit IFF's */
  ilbmheader.masking = mskNone;
  if (compression) {
    ilbmheader.compression = cmpByteRun1;
    printf("Compression On.\n");
  } else
    ilbmheader.compression = cmpNone;
  ilbmheader.pad1 = 0;
  ilbmheader.transparentColor = 0;
  ilbmheader.xAspect = 10;
  ilbmheader.yAspect = 10;
  ilbmheader.pageWidth = (unsigned short)sunheader.ras_width ;
  ilbmheader.pageHeight = (unsigned short)sunheader.ras_height;
 
  /* Full pixel interleaved line consists of tuplets of rgb values */
  rgbline = ilbmheader.w * 3; 
  coded_rowbuf = (byte *)malloc(ilbmheader.w);
  raw_rowbuf = (byte *)malloc(rgbline);
  if (coded_rowbuf == NULL || raw_rowbuf == NULL) {
    fprintf(stderr,"Can't get the memory I need...\n");
    fclose(fp);
    return -2;
  }

  /* RoyBytes fromula from ilbm.h */
  linesize = RowBytes(ilbmheader.w);
  bmhdsize = sizeof(struct BitMapHeader);
  if (!compression) {
    bodysize = ilbmheader.h * linesize * ilbmheader.nPlanes;
    formsize = 4 + 4 + 4 + bmhdsize + 4 + 4 + bodysize;
  } else {
    bodysize = 999999;		
    formsize = 999999;		/* Do this because we don't know how much compression we'll get */
  }
 
  fwrite("FORM",4,1,fp);
  fwrite(&formsize,4,1,fp);
  fwrite("ILBM",4,1,fp);
  fwrite("BMHD",4,1,fp);
  fwrite(&bmhdsize,4,1,fp);
  fwrite(&ilbmheader,bmhdsize,1,fp);
  fwrite("BODY",4,1,fp);
  fwrite(&bodysize,4,1,fp);

  image = raw_rowbuf;           /* Hold this pointer to the raw buffer in image pointer */

  /* This next whole thing took quite a while to figure out 
   This file format looks like this:  ( all data is bit reversed )
   Plane	   data		     scanline
   -------------------------------------------
   Plane 0:	RowBytes of red		0
   Plane 1:	RowBytes of red		0
     |
   Plane 7:	RowBytes of red		0
   Plane 8:	RowBytes of green	0
     |
   Plane 15:	RowBytes of green	0
   Plane 16:	RowBytes of blue	0
     |
   Plane 23:	RowBytes of blue	0
   Plane 0:	RowBytes of red		1
   Plane 1:	RowBytes of red		1
      and the beat goes on...
  */

  /* Figure this next bit out for yourself :-<  */

  for (row = 0;row < (int)ilbmheader.h;row++) {
    yoffset = row * rgbline;
    raw_rowbuf = image;
    for (plane = 0 ;plane < (int)ilbmheader.nPlanes; plane++) {
      if (plane < 8) {
	push = plane;
      } else if (plane > 15) {
 	   push = plane - 16;
      }
      else {
	push = plane - 8;
      }
        mask = 1 << push;		/* how many bits to push */
      cp = coded_rowbuf;
      *cp = 0;
      for (col = (plane / 8), rel_count = 0 ; col < rgbline; rel_count++ ,col+=3) {
	  offset = yoffset + col;
  	  bit = (*(pic + offset) & mask) ? 1 : 0;
	  *cp |= bit << ( 7 - (rel_count % 8) );
	if ((rel_count % 8)  == 7) {	/* Every 8th byte, advance pixel */
	  cp++;
	  *cp = 0;
        }
      }
      if (compression) {	/* This bit is pretty obvious. */
        raw_rowbuf = image;	/* compression is rle, as specified in AmigaDOS 2.0 RKM */
	packedbytes = packrow(coded_rowbuf, raw_rowbuf, linesize);
	tot_bytes+=packedbytes;
	fwrite(raw_rowbuf, packedbytes, 1, fp);
      } else			/* Write uncompressed data */
        fwrite(coded_rowbuf, linesize, 1, fp);
    }
  }
  if (compression) {
	fclose(fp);			/* close because we must alter the fopen flags */
  	if ((fp = fopen(ilbmfile,"r+")) == NULL) {
    	  fprintf(stderr,"Open of '%s' for rewriting failed.\n",ilbmfile);
    	return -1;			/* Hack in the real bodysize and formsize values */
  	}
	formsize = tot_bytes + ILBMHSIZE;/* 40 is the number of bytes in the header after FORM and size */
	fseek(fp,4L,0);
	fwrite(&formsize,4,1,fp);
	fseek(fp,44L,0);
	fwrite(&tot_bytes,4,1,fp);
  }
  if ((tot_bytes % 2) != 0) {		/* Pad the body chunk out to an even byte */
    fseek(fp,0L,2);
    pad = 0;
    fwrite(&pad,1,1,fp); 
  }
  fclose(fp);
  free(coded_rowbuf);
  free(raw_rowbuf);
  return 0;
}
