#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<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;lineBitMap.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); }