home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / 2 / 24bittls.zip / WRITE.C < prev    next >
C/C++ Source or Header  |  1991-10-28  |  8KB  |  323 lines

  1. #include "iff.h"
  2.  
  3. /* WriteILBM.c: Generates 2-24 bit IFF files
  4.         (c) 1991 Dallas J. Hodgson          */
  5.  
  6. SafeWrite(BPTR fp,void *buf,int length)
  7. {
  8.   return((Write(fp,buf,length)==-1) ? TRUE:FALSE);
  9. }
  10.  
  11. WriteILBM(char *fspec,struct PicMap *picmap)
  12. {
  13.   BPTR fp=NULL;
  14.   BitMapHeader bmhd;
  15.   Chunk header;
  16.   long id;
  17.   int marker,bodylen,eof,err=TRUE;
  18.   short numCols=1<<picmap->BitMap.Depth;
  19.  
  20.   if (!(fp=Open(fspec,MODE_NEWFILE))) goto cleanup;
  21.  
  22.   header.ckID=ID_FORM;
  23.   header.ckSize=0;      /* GETS CORRECTED LATER */
  24.  
  25.   if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  26.  
  27.   id=ID_ILBM;
  28.   if (SafeWrite(fp,&id,sizeof(id))) goto cleanup;
  29.  
  30.   header.ckID=ID_BMHD;
  31.   header.ckSize=sizeof(BitMapHeader);
  32.   if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  33.  
  34.   bmhd.w=picmap->BitMap.BytesPerRow*8;
  35.   bmhd.h=picmap->BitMap.Rows;
  36.   bmhd.x=bmhd.y=0;
  37.   bmhd.nPlanes=picmap->BitMap.Depth;
  38.   bmhd.masking=mskNone;
  39.   bmhd.compression=cmpByteRun1;
  40.   bmhd.pad1=0;
  41.   bmhd.transparentColor=0;
  42.   bmhd.xAspect=1;
  43.   bmhd.yAspect=1;
  44.   bmhd.pageWidth=bmhd.w;
  45.   bmhd.pageHeight=bmhd.h;
  46.  
  47.   if (SafeWrite(fp,&bmhd,sizeof(bmhd))) goto cleanup;
  48.  
  49.   if (picmap->BitMap.Depth<=8) {
  50.  
  51.     header.ckID=ID_CMAP;
  52.     header.ckSize=numCols*3;
  53.  
  54.     if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  55.  
  56.     if (SafeWrite(fp,&picmap->palette,numCols*3))
  57.       goto cleanup;
  58.  
  59.     if (SafePad(fp,header.ckSize)) goto cleanup;
  60.   }
  61.  
  62.   header.ckID=ID_CAMG;
  63.   header.ckSize=sizeof(picmap->ViewModes);
  64.  
  65.   if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  66.   if (SafeWrite(fp,&picmap->ViewModes,sizeof(picmap->ViewModes))) goto cleanup;  
  67.  
  68.   header.ckID=ID_BODY;
  69.   header.ckSize=0;      /* GETS CORRECTED LATER */
  70.  
  71.   if ((marker=Seek(fp,0,OFFSET_CURRENT))==-1) goto cleanup;
  72.  
  73.   if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  74.  
  75.   if (!(bodylen=WriteBody(fp,picmap))) goto cleanup;
  76.   if (SafePad(fp,bodylen)) goto cleanup;
  77.  
  78.   if ((eof=Seek(fp,0,OFFSET_CURRENT))==-1) goto cleanup;
  79.  
  80.   /* Go back and fix the two size values */
  81.  
  82.   if (Seek(fp,marker,OFFSET_BEGINNING)==-1) goto cleanup;
  83.  
  84.   header.ckID=ID_BODY;
  85.   header.ckSize=bodylen;
  86.  
  87.   if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  88.  
  89.   if (Seek(fp,0,OFFSET_BEGINNING)==-1) goto cleanup;
  90.  
  91.   header.ckID=ID_FORM;
  92.   header.ckSize=eof-sizeof(header);
  93.   if (SafeWrite(fp,&header,sizeof(header))) goto cleanup;
  94.  
  95.   err=FALSE;
  96. cleanup:
  97.   if (fp) Close(fp);
  98.  
  99.   return(err);
  100. }
  101.  
  102. #define WRITE_SIZE 8192
  103. #define MAX_COMPRESSED_LINE 2048
  104.  
  105. WriteBody(BPTR fp,struct PicMap *picmap)
  106. {
  107.   unsigned char *rawbuf,*dstPtr,*packbuf=NULL;
  108.   short line,plane,count,bytelen;
  109.   short w=picmap->BitMap.BytesPerRow*8,h=picmap->BitMap.Rows;
  110.   int total=0,len=0;
  111.  
  112.   if (!(packbuf=AllocMem(WRITE_SIZE+MAX_COMPRESSED_LINE,MEMF_PUBLIC))) {
  113. #ifdef DEBUG
  114.     printf("WriteBody: couldn't allocate packbuf\n");
  115. #endif
  116.     goto cleanup;
  117.   }
  118.  
  119.   /* roundup width to a multiple of 16 pixels, if necessary; important
  120.      for deinterleaving brushes correctly! */
  121.  
  122.   if (w%16) w=(((w/16)+1)*16);
  123.   bytelen=w/8;
  124.  
  125.   /* Read each line from display, 1 plane at a time. Optionally compress
  126.      each bitplane separately, and write each bitplane out. */
  127.  
  128.   dstPtr=packbuf;
  129.  
  130.   for (line=0;line<h;line++) {
  131.  
  132.     for (plane=0;plane<picmap->BitMap.Depth;plane++) {
  133.       rawbuf=picmap->BitMap.Planes[plane]+(picmap->BitMap.BytesPerRow*line);
  134.  
  135.       /* A worst-case compression would result in the output being TWICE
  136.          the size of the input data. If the compressed version is larger
  137.          than the original, then write the uncompressed version.
  138.        */
  139.  
  140.       if ((count=PackBits2((char *)rawbuf,(char *)dstPtr,bytelen))<=bytelen) {
  141.         dstPtr+=count;
  142.         len+=count;
  143.         total+=count;
  144.       }
  145.       else {
  146.         count=PackUncompressedBits(rawbuf,dstPtr,bytelen);
  147.         dstPtr+=count;
  148.         len+=count;
  149.         total+=count;
  150.       }
  151.  
  152.       if (len >= WRITE_SIZE) {
  153.         if (SafeWrite(fp,packbuf,len)) {
  154.           total=0;
  155.           goto cleanup;
  156.         }
  157.         len=0;
  158.         dstPtr=packbuf;
  159.       }
  160.     }
  161.   }
  162.  
  163.   /* Flush out anything left in the output buffer after the last pass */
  164.   if (len)
  165.     if (SafeWrite(fp,packbuf,len)) total=0;
  166.  
  167. cleanup:
  168.   if (packbuf) FreeMem(packbuf,WRITE_SIZE+MAX_COMPRESSED_LINE);
  169.   return(total);
  170. }
  171.  
  172. SafePad(BPTR fp,int len)
  173. {
  174.   static char pad=0;
  175.  
  176.   if (len & 1)
  177.     if (SafeWrite(fp,&pad,1)) return(TRUE);
  178.  
  179.   return(FALSE);
  180. }
  181.  
  182. /*
  183.  * PRIVATE: PackUncompressedBits is an uncompressed stream writer, suitable
  184.  * for those occasions when RLE encoding just doesn't do the job.
  185.  */
  186.   int
  187. PackUncompressedBits(unsigned char *src,unsigned char *dst,int size)
  188. {
  189.   unsigned char *orig=dst;
  190.   int len;
  191.  
  192.   while(size) {
  193.     if (size<128) len=size; else len=127;
  194.     *dst++ = len-1;
  195.     CopyMem((char *)src,(char *)dst,len);
  196.     src+=len;
  197.     dst+=len;
  198.     size-=len;
  199.   }
  200.  
  201.   return(dst-orig);
  202. }
  203.  
  204. #define MAXRUN 127
  205.  
  206. /*
  207.  * PRIVATE: PackBits2 is derived from the original Macintosh compress
  208.  * routine, but modified so the maximum compressed run is 127 instead
  209.  * of 128 bytes. According to the IFF docs, a compressed run of 128 is
  210.  * illegal, tho' possible if your unpacker routine uses unsigned int
  211.  * (instead of signed char) comparisons for the expansion.
  212.  *
  213.  * Runs longer than 127 bytes are permissible, but will be broken up
  214.  * into smaller runs of 127 bytes or less.
  215.  */
  216.   int
  217. PackBits2(char *src,char *dst,int size)
  218. {
  219.   char c;
  220.   char *sp, *dp, *start;
  221.   int runChar;
  222.   int runCount,nonRunCount;
  223.  
  224.   /*
  225.    * Initialize.
  226.    */
  227.   sp = src;
  228.   dp = dst;
  229.   start = sp;
  230.   runChar = *sp++;
  231.   --size;
  232.   runCount = 1;
  233.   /*
  234.    * Loop over all input bytes.
  235.    */
  236.   while (size > 0) {
  237.     c = *sp++;
  238.     --size;
  239.     if (c == runChar) {
  240.       ++runCount;
  241.     }
  242.     else {
  243.       /*
  244.        *  This is the end of a run of bytes (possibly a very short run).
  245.        *  Figure out what to do with it.
  246.        */
  247.       if (runCount >= 3) {
  248.         /*
  249.          *  If the run length is greater than three, compress it.  Runs
  250.          *  of smaller than three are treated as non-runs.
  251.          */
  252.         nonRunCount = (sp - 1) - start - runCount;
  253.         /*
  254.          *  First, output any accumulated non-run bytes.
  255.          */
  256.         if (nonRunCount > 0) {
  257.           *dp++ = nonRunCount - 1;
  258.           while (nonRunCount--) {
  259.             *dp++ = *start++;
  260.           }
  261.         }
  262.         /*
  263.          *  Now output the compressed run.  Since the max run length we
  264.          *  can encode is MAXRUN, longer runs must be segmented.
  265.          */
  266.         while (runCount > MAXRUN) {
  267.           /*
  268.            *  Loop to output segments of runs longer that MAXRUN.
  269.            */
  270.            *dp++ = -(MAXRUN - 1);
  271.            *dp++ = runChar;
  272.            runCount -= MAXRUN;
  273.         }
  274.         /*
  275.          *  Output the last (or only) run of length MAXRUN or less.
  276.          */
  277.         *dp++ = -(runCount - 1);
  278.         *dp++ = runChar;
  279.         start = sp - 1;
  280.       }
  281.       /*
  282.        *  Get ready for the next time through this loop.
  283.        */
  284.       runChar = c;
  285.       runCount = 1;
  286.     }
  287.   }
  288.   /*
  289.    *  We've reached the end of the source data.  Now we have to flush
  290.    *  out data we haven't dealt with yet.  This code is almost identical
  291.    *  to the code inside the main loop, above.
  292.    */
  293.   nonRunCount = sp - start;
  294.   if (runCount >= 3) nonRunCount -= runCount;
  295.   else runCount = 0;
  296.   /*
  297.    *  Output non-run data.
  298.    */
  299.   if (nonRunCount) {
  300.     *dp++ = nonRunCount - 1;
  301.     while (nonRunCount--) {
  302.       *dp++ = *start++;
  303.     }
  304.   }
  305.   /*
  306.    *  Output compressed run.
  307.    */
  308.   if (runCount) {
  309.     while (runCount > MAXRUN) {
  310.       *dp++ = -(MAXRUN - 1);
  311.       *dp++ = runChar;
  312.       runCount -= MAXRUN;
  313.     }
  314.     *dp++ = -(runCount - 1);
  315.     *dp++ = runChar;
  316.   }
  317.   /*
  318.    * Clean up and return.
  319.    */
  320.  
  321.   return(dp-dst);
  322. }
  323.