/*
 *  read.c     24 bit IFF ILBM -> 24 bit Sun Raster
 *  ILBM's can be compressed or uncompressed, only uncompressed
 *  Sun Rasters are written. By Brett Van Sprewenburg
 * 
 * Credit to the ppm utilities, the developer(s) of xv, and Commodore-Amiga for
 * code and ideas.
 *
 *  This whole thing is a bit of a hack...
 *
 *  Version	Date	
 *   1.0	12/??/92	Initial attempts to read the 24 bit IFF ILBM format
 *   1.1	2/4/93		Cleaned code, fixed bugs with read and writeilbm intergration
 *   1.2	2/9/93		ANSI'fied code some more for compilation with acc
 *   1.2.1	2/10/93		Checks to make sure it's a valid iff file, looks for FORM chuck id
 *				Fixed a couple of minor printf bugs. 
 *   1.2.2	2/11/93		Checks to make sure that there are 24 bit planes in the IFF.
 *   1.2.3	2/12/93		Checked that mallocs actually got what they wanted.
 *				First public release
 */

#include <readilbm.h>

int ReadILBM(char *, char *);

int ReadILBM(char *ilbmfile,char *rasterfile)
{
	unsigned char	form[5];
	unsigned char	*ubp,*bp,*runbuf;
	unsigned char	*Rrow;
	unsigned char	*Brow;
	unsigned char	*Grow;
	unsigned int	byte;
	int 	fd,totbytes,bytes,row,col,plane,j,subtotal;
	long	bmsize,formsize;
	unsigned char *body = 0;
	unsigned long body_chunk_size;

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

  	if ((fd = creat(rasterfile,0660)) < 0) {
	  fprintf(stderr,"Open of '%s' for writing failed.\n",rasterfile);
	  return -1;
	}

	fread(form,4,1,fp);		/* FORM */
	if (strcmp(form,"FORM") != NULL) {
	  fprintf(stderr,"'%s' is not an IFF file. Exiting.\n",ilbmfile);
 	  fclose(fp);
	  close(fd);
	  return -2;
	}
	fread(&formsize,4,1,fp);	/* Size of whole form -4 */
	printf("%s ",form);
	fread(form,4,1,fp);		/* ILBM */
	printf("%s: (%d bytes) ",form,formsize);
	fread(form,4,1,fp);		/* BMHD */
	fread(&bmsize,4,1,fp);		/* size of BMHD */
	fread(&ilbmheader,20,1,fp);
	printf("(%dx%d) (%d planes) (Compression %s)\n",ilbmheader.w,ilbmheader.h,ilbmheader.nPlanes, ilbmheader.compression == cmpByteRun1 ? "On" : "Off");

        if (ilbmheader.nPlanes != 24) {
          fprintf(stderr,"Only IFF ILBM's of 24 bit planes are supported.\n");
	  fclose(fp);
	  close(fd);
	  return -3;
        }
 
	fread(form,4,1,fp);		/* BODY (ANNO sometimes) */

	while ((strcmp(form,"BODY") != 0)) 	/* Find the damn thing */
	  fread(form,4,1,fp);
        
	fread(&body_chunk_size,4,1,fp);

	pixelrow = ALLOCROW(ilbmheader.w);
	body = (unsigned char*) malloc(body_chunk_size);
	runbuf = (unsigned char*) malloc(RowBytes(ilbmheader.w));
	Rrow = (unsigned char*) malloc(ilbmheader.w);
	Grow = (unsigned char*) malloc(ilbmheader.w);
	Brow = (unsigned char*) malloc(ilbmheader.w);
	if (body == NULL || pixelrow == NULL || runbuf == NULL ||
	    Rrow == NULL || Grow == NULL || Brow == NULL) {
	  fprintf(stderr,"Unable to allocate all the memory I need...\n");
	  fclose(fp);
	  close(fd);
	  return -4;
	}

	bp = body;

        /* Define and write the Sun Raster header */
	sunheader.ras_magic = RAS_MAGIC;
	sunheader.ras_width = ilbmheader.w;
	sunheader.ras_height = ilbmheader.h;
	sunheader.ras_depth = ilbmheader.nPlanes;
	sunheader.ras_length = ilbmheader.h * ilbmheader.w * 3;
	sunheader.ras_type = RT_FORMAT_RGB;
	sunheader.ras_maptype = RMT_NONE;
	sunheader.ras_maplength = 0;
	write(fd, sunheader, sizeof(sunheader));

        /* Read in the body of the ilbm */
	fread(body, body_chunk_size, 1, fp); 

  for (row = 0; row < (int)ilbmheader.h; row++ ) {

    /* There are kind of colormappish */
    for (col = 0; col < (int)ilbmheader.w ; col++ )
      Rrow[col] = Grow[col] = Brow[col] = 0;

    for(plane = 0; plane < (int)ilbmheader.nPlanes; plane++) {
      switch (ilbmheader.compression) {

        case 0:
		ubp = bp;
		bp += RowBytes((int)ilbmheader.w);
		break;
        case 1:
		ubp = runbuf;
        	totbytes = RowBytes((int)ilbmheader.w);
		do {
	 	  byte = *bp++;
  	  	  if ( byte <= 127 )
	    	  for ( j = byte, totbytes -= j + 1; j >= 0; j--) {
			*ubp++ = *bp++;
	    	  }
	  	  else if ( byte != 128 )
	    	  for (j = 256 - byte, totbytes -= j + 1, byte = *bp++; j >= 0; j--) {
			*ubp++ = byte;
	    	  }
        	} while ( totbytes > 0 );
		ubp = runbuf;
        	break;
       }

       /* Break raw row buffer up into separate rgb values */
       /* In order to understand this a little better, it might help */
       /* to know that the pixel values in an ILBM appear to be stored */
       /* bit reversed. Hence all the logical bitwise juju. */

       for ( col = 0; col < (int)ilbmheader.w; col++ )
                    if ( plane < 8 )
                        { /* red */
                        if ( ubp[col / 8] & ( 128 >> ( col % 8 ) ) )
                            Rrow[col] |= 1 << plane;
                        }
                    else if ( plane > 15 )
                        { /* blue */
                        if ( ubp[col / 8] & ( 128 >> ( col % 8 ) ) )
                            Brow[col] |= 1 << (plane-16);
                        }
                    else
                        { /* green */
                        if ( ubp[col / 8] & ( 128 >> ( col % 8 ) ) )
                            Grow[col] |= 1 << (plane-8);
                        }
    }
      /* Get the rgb values out of pixel structure, pointed at by pixelrow */
       for ( col = 0; col < (int)ilbmheader.w; col++ )
	 ASSIGN(pixelrow[col],Rrow[col],Grow[col],Brow[col]);

      /* Write out an entire row of pixel interleaved data */
       write(fd, pixelrow, (int)ilbmheader.w * 3);
  }

	free(Rrow);		/* Fly! Be Free!! */
	free(Grow);
	free(Brow);
	free(runbuf);
	free(pixelrow);
	free(body);
	fclose(fp);
	close(fd);
  return 0;
}
