home *** CD-ROM | disk | FTP | other *** search
- #include "iff.h"
-
- /* WriteILBM.c: Generates 2-24 bit IFF files
- (c) 1991 Dallas J. Hodgson */
-
- SafeWrite(BPTR fp,void *buf,int length)
- {
- return((Write(fp,buf,length)==-1) ? TRUE:FALSE);
- }
-
- WriteILBM(char *fspec,struct PicMap *picmap)
- {
- BPTR fp=NULL;
- BitMapHeader bmhd;
- Chunk header;
- long id;
- int marker,bodylen,eof,err=TRUE;
- short numCols=1<<picmap->BitMap.Depth;
-
- if (!(fp=Open(fspec,MODE_NEWFILE))) goto cleanup;
-
- header.ckID=ID_FORM;
- header.ckSize=0; /* GETS CORRECTED LATER */
-
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
-
- id=ID_ILBM;
- if (SafeWrite(fp,&id,sizeof(id))) goto cleanup;
-
- header.ckID=ID_BMHD;
- header.ckSize=sizeof(BitMapHeader);
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
-
- bmhd.w=picmap->BitMap.BytesPerRow*8;
- bmhd.h=picmap->BitMap.Rows;
- bmhd.x=bmhd.y=0;
- bmhd.nPlanes=picmap->BitMap.Depth;
- bmhd.masking=mskNone;
- bmhd.compression=cmpByteRun1;
- bmhd.pad1=0;
- bmhd.transparentColor=0;
- bmhd.xAspect=1;
- bmhd.yAspect=1;
- bmhd.pageWidth=bmhd.w;
- bmhd.pageHeight=bmhd.h;
-
- if (SafeWrite(fp,&bmhd,sizeof(bmhd))) goto cleanup;
-
- if (picmap->BitMap.Depth<=8) {
-
- header.ckID=ID_CMAP;
- header.ckSize=numCols*3;
-
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
-
- if (SafeWrite(fp,&picmap->palette,numCols*3))
- goto cleanup;
-
- if (SafePad(fp,header.ckSize)) goto cleanup;
- }
-
- header.ckID=ID_CAMG;
- header.ckSize=sizeof(picmap->ViewModes);
-
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
- if (SafeWrite(fp,&picmap->ViewModes,sizeof(picmap->ViewModes))) goto cleanup;
-
- header.ckID=ID_BODY;
- header.ckSize=0; /* GETS CORRECTED LATER */
-
- if ((marker=Seek(fp,0,OFFSET_CURRENT))==-1) goto cleanup;
-
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
-
- if (!(bodylen=WriteBody(fp,picmap))) goto cleanup;
- if (SafePad(fp,bodylen)) goto cleanup;
-
- if ((eof=Seek(fp,0,OFFSET_CURRENT))==-1) goto cleanup;
-
- /* Go back and fix the two size values */
-
- if (Seek(fp,marker,OFFSET_BEGINNING)==-1) goto cleanup;
-
- header.ckID=ID_BODY;
- header.ckSize=bodylen;
-
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
-
- if (Seek(fp,0,OFFSET_BEGINNING)==-1) goto cleanup;
-
- header.ckID=ID_FORM;
- header.ckSize=eof-sizeof(header);
- if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
-
- err=FALSE;
- cleanup:
- if (fp) Close(fp);
-
- return(err);
- }
-
- #define WRITE_SIZE 8192
- #define MAX_COMPRESSED_LINE 2048
-
- WriteBody(BPTR fp,struct PicMap *picmap)
- {
- unsigned char *rawbuf,*dstPtr,*packbuf=NULL;
- short line,plane,count,bytelen;
- short w=picmap->BitMap.BytesPerRow*8,h=picmap->BitMap.Rows;
- int total=0,len=0;
-
- if (!(packbuf=AllocMem(WRITE_SIZE+MAX_COMPRESSED_LINE,MEMF_PUBLIC))) {
- #ifdef DEBUG
- printf("WriteBody: couldn't allocate packbuf\n");
- #endif
- goto cleanup;
- }
-
- /* roundup width to a multiple of 16 pixels, if necessary; important
- for deinterleaving brushes correctly! */
-
- if (w%16) w=(((w/16)+1)*16);
- bytelen=w/8;
-
- /* Read each line from display, 1 plane at a time. Optionally compress
- each bitplane separately, and write each bitplane out. */
-
- dstPtr=packbuf;
-
- for (line=0;line<h;line++) {
-
- for (plane=0;plane<picmap->BitMap.Depth;plane++) {
- rawbuf=picmap->BitMap.Planes[plane]+(picmap->BitMap.BytesPerRow*line);
-
- /* A worst-case compression would result in the output being TWICE
- the size of the input data. If the compressed version is larger
- than the original, then write the uncompressed version.
- */
-
- if ((count=PackBits2((char *)rawbuf,(char *)dstPtr,bytelen))<=bytelen) {
- dstPtr+=count;
- len+=count;
- total+=count;
- }
- else {
- count=PackUncompressedBits(rawbuf,dstPtr,bytelen);
- dstPtr+=count;
- len+=count;
- total+=count;
- }
-
- if (len >= WRITE_SIZE) {
- if (SafeWrite(fp,packbuf,len)) {
- total=0;
- goto cleanup;
- }
- len=0;
- dstPtr=packbuf;
- }
- }
- }
-
- /* Flush out anything left in the output buffer after the last pass */
- if (len)
- if (SafeWrite(fp,packbuf,len)) total=0;
-
- cleanup:
- if (packbuf) FreeMem(packbuf,WRITE_SIZE+MAX_COMPRESSED_LINE);
- return(total);
- }
-
- SafePad(BPTR fp,int len)
- {
- static char pad=0;
-
- if (len & 1)
- if (SafeWrite(fp,&pad,1)) return(TRUE);
-
- return(FALSE);
- }
-
- /*
- * PRIVATE: PackUncompressedBits is an uncompressed stream writer, suitable
- * for those occasions when RLE encoding just doesn't do the job.
- */
- int
- PackUncompressedBits(unsigned char *src,unsigned char *dst,int size)
- {
- unsigned char *orig=dst;
- int len;
-
- while(size) {
- if (size<128) len=size; else len=127;
- *dst++ = len-1;
- CopyMem((char *)src,(char *)dst,len);
- src+=len;
- dst+=len;
- size-=len;
- }
-
- return(dst-orig);
- }
-
- #define MAXRUN 127
-
- /*
- * PRIVATE: PackBits2 is derived from the original Macintosh compress
- * routine, but modified so the maximum compressed run is 127 instead
- * of 128 bytes. According to the IFF docs, a compressed run of 128 is
- * illegal, tho' possible if your unpacker routine uses unsigned int
- * (instead of signed char) comparisons for the expansion.
- *
- * Runs longer than 127 bytes are permissible, but will be broken up
- * into smaller runs of 127 bytes or less.
- */
- int
- PackBits2(char *src,char *dst,int size)
- {
- char c;
- char *sp, *dp, *start;
- int runChar;
- int runCount,nonRunCount;
-
- /*
- * Initialize.
- */
- sp = src;
- dp = dst;
- start = sp;
- runChar = *sp++;
- --size;
- runCount = 1;
- /*
- * Loop over all input bytes.
- */
- while (size > 0) {
- c = *sp++;
- --size;
- if (c == runChar) {
- ++runCount;
- }
- else {
- /*
- * This is the end of a run of bytes (possibly a very short run).
- * Figure out what to do with it.
- */
- if (runCount >= 3) {
- /*
- * If the run length is greater than three, compress it. Runs
- * of smaller than three are treated as non-runs.
- */
- nonRunCount = (sp - 1) - start - runCount;
- /*
- * First, output any accumulated non-run bytes.
- */
- if (nonRunCount > 0) {
- *dp++ = nonRunCount - 1;
- while (nonRunCount--) {
- *dp++ = *start++;
- }
- }
- /*
- * Now output the compressed run. Since the max run length we
- * can encode is MAXRUN, longer runs must be segmented.
- */
- while (runCount > MAXRUN) {
- /*
- * Loop to output segments of runs longer that MAXRUN.
- */
- *dp++ = -(MAXRUN - 1);
- *dp++ = runChar;
- runCount -= MAXRUN;
- }
- /*
- * Output the last (or only) run of length MAXRUN or less.
- */
- *dp++ = -(runCount - 1);
- *dp++ = runChar;
- start = sp - 1;
- }
- /*
- * Get ready for the next time through this loop.
- */
- runChar = c;
- runCount = 1;
- }
- }
- /*
- * We've reached the end of the source data. Now we have to flush
- * out data we haven't dealt with yet. This code is almost identical
- * to the code inside the main loop, above.
- */
- nonRunCount = sp - start;
- if (runCount >= 3) nonRunCount -= runCount;
- else runCount = 0;
- /*
- * Output non-run data.
- */
- if (nonRunCount) {
- *dp++ = nonRunCount - 1;
- while (nonRunCount--) {
- *dp++ = *start++;
- }
- }
- /*
- * Output compressed run.
- */
- if (runCount) {
- while (runCount > MAXRUN) {
- *dp++ = -(MAXRUN - 1);
- *dp++ = runChar;
- runCount -= MAXRUN;
- }
- *dp++ = -(runCount - 1);
- *dp++ = runChar;
- }
- /*
- * Clean up and return.
- */
-
- return(dp-dst);
- }
-