home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #30 / NN_1992_30.iso / spool / comp / sys / amiga / programm / 17412 < prev    next >
Encoding:
Text File  |  1992-12-15  |  16.4 KB  |  430 lines

  1. Newsgroups: comp.sys.amiga.programmer
  2. Path: sparky!uunet!think.com!spool.mu.edu!umn.edu!doug.cae.wisc.edu!pochanay
  3. From: pochanay@cae.wisc.edu (Adisak Pochanayon)
  4. Subject: RUN BYTE! RUN!
  5. Organization: College of Engineering, Univ. of Wisconsin--Madison
  6. Date: 15 Dec 92 21:33:59 CST
  7. Message-ID: <1992Dec15.213400.28437@doug.cae.wisc.edu>
  8. Sender: pochanay@cae.wisc.edu
  9. Lines: 419
  10.  
  11. A Modified Byte Run Compression Algorithm.
  12. Part 2:
  13.  
  14. -------------------------------------------------------------------------
  15.      OK, Class....
  16.  
  17.      We left off in part one with an idea for an extremely fast graphics
  18. compression algorithm that was roughly based on the IFF byte run method.
  19. This article will again cover the decompression algorithm as I have made
  20. some improvements in the assembly code.  The main compression routines
  21. and algorithm are unchanged however and the format for the compressed
  22. files has not changed.
  23.  
  24.      In the second part of this article, I describe a Compressor (and
  25. DeCompressor) program in C.  It is not written for optimal speed merely
  26. because I wanted the code to be readable and the time that speed is
  27. really needed is decompression (which occurs in the 36-byte assembly
  28. routine).  Using this code along with the unpacker.asm will allow you
  29. to include any raw compressed graphics easily in your own programs.
  30.  
  31. NOTE: This is not a repost per se.  I got several suggestions on how to
  32.       improve the decompression speed by up to 10 cycles per byte-tag.
  33.       Many thanks to all who replied.  Espescially:
  34.       Bjoern Reese | Email: breese@imada.ou.dk
  35.       for his insight.
  36.       There is also much more information on the compression process
  37.       as well as a compressor program.
  38.  
  39. -------------------------------------------------------------------------
  40.  
  41. Adisak Pochanayon
  42. 2525 University Avenue Apt #J
  43. Madison WI 53706
  44. 608-238-2463
  45.  
  46.      Fancy title screens and graphics can spice up an otherwise mudane
  47. program but they can also present a problem to the programmer.  Including
  48. the actual bitplane data in a program can take a lot of memory and disk
  49. space.  IFF graphics files do employ compression but loading an iff graphic
  50. requires the programmer to add additional source code that may bulk up the
  51. program and forces the program to access external support files.  A simple
  52. way of avoiding these problems is to include compress data directly within
  53. a program.
  54.  
  55.      When compressing bitplane data on the Amiga, byte run compression or
  56. the compression of repeated bytes provides a simple yet rather efficient
  57. method of compression.  As a matter of fact, the IFF ILBM file format
  58. employs byte run compression.  The IFF compression scheme uses the format
  59. of "byte-tag data", where byte-tag is a single byte and data is anywhere
  60. from zero to 128 bytes.  If the byte-tag is 128, then the length of he
  61. data is zero and nothing happens (this is a NOP).  If the byte-tag is less
  62. than 128, then the data is (byte-tag + 1) bytes in length and is a literal
  63. representation of bitplane data.  If the byte-tag is greater than 128, then
  64. the data is one byte that is to be repeated (257 - byte-tag) times.  There
  65. are two main problems with using the IFF byte run compression.  First, the
  66. compression format can be slightly complicated and second, IFF compresses
  67. each line in each bitplane individually.  This leads to slower
  68. decompression and lower compression ratios.
  69.  
  70.      In order to modify the byte-run to achieve the fastest decompression as
  71. well as the best compression ratio, some changes must be made.  This begins
  72. with the simple change of compressing entire bitplanes or bitmaps as a
  73. single entity rather than using line by line compression on each bitplane.
  74. The next improvement comes from a simplification of the "byte-tag data"
  75. format.  A byte-tag of zero will represent STOP.  A byte-tag of less than
  76. 128 will mean repeating the next byte (byte-tag + 1) times.  A byte-tag of
  77. 128 or greater (note that when using signed numbers, a byte greater than or
  78. equal to 128 is negative) will mean copying the next (byte-tag - 127) bytes
  79. literally.  This will allow an extremely fast decompression method which
  80. only takes 36 bytes of code in assembly.
  81.  
  82.      To better understand how this modified byte-run compression works,
  83. let's look at the assembly for the decompression:
  84.  
  85. ---------------------------BEGIN ASM----------------------------------------
  86.  
  87.          CODE, PUBLIC
  88.  
  89. *****    VOID __regargs UnPackByteRun(UBYTE *from, UBYTE *to)
  90. *****        calling from assembly:          a0   ,       a1
  91.  
  92. *****    This code takes a Byte Run packed buffer and unpacks it to
  93. *****  another buffer.  Note: for maximum speed, no checking of any
  94. *****  sort is performed for boundaries.  Also, there is no CRC or
  95. *****  error correction/detection.  It is assumed that your data is
  96. *****  valid.
  97.  
  98. *****    If you're using C with UnPackByteRun1 and your compiler doesn't
  99. *****    support, Lattice/SAS 's __regargs register passing convention,
  100. *****    you will need to use the following commented-out code.
  101.  
  102. *****             XDEF _UnPackByteRun
  103. *****    _UnPackByteRun:
  104. *****             movea.l  4(SP),a0
  105. *****             movea.l  8(SP),a1
  106.  
  107.  
  108.          XDEF @UnPackByteRun
  109. @UnPackByteRun:
  110.  
  111.           move.l    d2,-(SP)       ; Move #7F to d2 so that the and.w
  112.           move.b    #$7F,d2        ; instruction at the upb_copyit label
  113.                                    ; will execute more quickly
  114.  
  115.           bra.s     upb_startit    ; Process First byte-tag
  116.  
  117.  
  118. *********************************
  119.  
  120. upb_copyit
  121.           and.w     d2,d0          ; Clear bit seven in d0
  122.  
  123. upb_copy_loop                      ; Note: 68010+ optimized dbf loop to
  124.           move.b    (a0)+,(a1)+    ; Copy a literal string d0 +1 in length
  125.           dbf       d0,upb_copy_loop
  126.  
  127.                                    ; Process next byte-tag when loop ends
  128.  
  129. *********************************
  130.  
  131. upb_startit
  132.           moveq.l   #0,d0          ; Require to clear MSB of d0 for dbf loops
  133.                                    ; Which is $FF after dbf loops
  134.  
  135.           move.b    (a0)+,d0       ; Load byte-tag into d0 and test byte-tag
  136.  
  137.  
  138.           bmi.s     upb_copyit     ; Negative -> COPY bytes literally
  139.  
  140.           beq.s     upb_out        ; Zero -> quit
  141.  
  142.                                    ; Else Repeat a byte (Positive)
  143.  
  144. *********************************
  145.  
  146.           move.b    (a0)+,d1       ; Repeat this byte d0 +1 times
  147.  
  148. upb_repeatit                       ; Note 68010+ optimized dbf loop
  149.           move.b    d1,(a1)+
  150.           dbf       d0,upb_repeatit
  151.           bra.s     upb_startit    ; Process next byte-tag
  152.  
  153. *********************************
  154.  
  155. upb_out
  156.           move.l    (SP)+,d2       ; Restore value of d2 before exiting
  157.           rts
  158.  
  159.           END
  160.  
  161.  
  162. ---------------------------END ASM------------------------------------------
  163.  
  164.      The code is passed a pointer to the compressed data buffer in memory
  165. in a0 and a pointer to the buffer for data decompression in a1.  Care was
  166. taken to ensure that speed of decompression was of the first importance.
  167. Decoding each byte tag takes a maximum of four instructions in assembly.
  168. If the byte-tag just read is equal to zero, the routine has finished.
  169. After a byte-tag is decoded, control can be passed to the repeat byte
  170. routine or copy bytes routine.  If the control is passed to the repeat byte
  171. routine, the byte to be repeated is loaded into d1 and then written to the
  172. output buffer using a 68010+ optimized "dbf loop" of two instructions.  When
  173. the repeated byte string is finished, the next byte-tag is processed.  If
  174. the control is passed to the copy bytes routine, the high bit in the
  175. byte-tag is cleared.  Then data is copied literally from the compressed
  176. buffer to the output buffer in another 68010+ optimized "dbf loop".  When
  177. this data has been copied, the next byte-tag is processed.
  178.  
  179.      This simple yet extremely fast assembly routine is the heart of the
  180. modified byte run compression scheme.  It is very easy to link to any
  181. program and takes very little memory.  In order to use the decompression
  182. routine, however, a compression routine is required.  This compressor's
  183. source follows immediately as "BR.c".  In can pack or unpack any file
  184. using the modified byte run compression scheme noted above.  The syntax for
  185. using "BR" is: "BR  FLAG  SOURCE  DESTINATION" where FLAG can be U - unpack
  186. or P - pack.
  187.  
  188. ---------------------------BEGIN C------------------------------------------
  189.  
  190. ;/* BR.c - BYte Run (un)packer -- Source for Lattice/SAS C version 5.10
  191.  
  192. LC -b1 -ccist -v -O -L  BR.c
  193. quit
  194.  
  195. */
  196.  
  197. /*****************************************************************************
  198.      BR - Byte Run (un)packer.        C. 1991-1992 SilverFox SoftWare.
  199.         Written by Adisak Pochanayon     All Rights Reserved.
  200.  
  201.           This program is a simple example of using BYTE-RUN compression
  202.      upon a file.  It will allow you to both pack and unpack a file
  203.      using BYTE-RUN compression.  The syntax for calling this program from
  204.      the CLI is:
  205.  
  206.                BR  FLAG  SOURCE  DESTINATION
  207.                FLAG can be U - unpack  P - pack
  208.  
  209. *****************************************************************************/
  210.  
  211. #include  <stdio.h>
  212.  
  213. /*  defines for interpreting the CLI arguments    */
  214. #define   arg_flag     1
  215. #define   arg_srce     2
  216. #define   arg_dest     3
  217. #define   arg_count    4
  218.  
  219. /*  defines for handling argument and file errors */
  220. #define   br_error   1024
  221. #define   error_read   0
  222. #define   error_args   1
  223. #define   error_srce   2
  224. #define   error_dest   4
  225. #define   error_write  8
  226.  
  227. /*  defines for file compression / decompression  */
  228. #define   FILE_ERROR   0
  229. #define   MAX_RUN      128
  230. #define   LITERAL      128
  231. #define   TRUE         1
  232. #define   FALSE        0
  233.  
  234. /*** GLOBAL VARIABLE FOR ERRORS ***/
  235. int  error=0;
  236.  
  237. char __regargs readchar(register FILE *infile)
  238. {
  239.   register int inchar;
  240.  
  241.   if ((inchar=getc(infile))!=EOF) return((char)inchar);
  242.   /* OTHERWISE AN ERROR OCCURRED READING THE INPUT FILE */
  243.   if (error==0)
  244.     {
  245.       error = br_error + error_read;
  246.       printf("Unexpected end of file on input file.\nOutput file may be corrupt.\n");
  247.     }
  248.   return(FILE_ERROR);
  249. }
  250.  
  251. void __regargs writechar(register char outchar, register FILE *outfile)
  252. {
  253.   if (putc(outchar,outfile)==EOF)
  254.     {
  255.       printf("Error writing output file.\n");
  256.       fcloseall();
  257.       exit(br_error + error_write);
  258.     }
  259. }
  260.  
  261. void __regargs UnPackByteRun(register FILE *infile, register FILE *outfile)
  262. {
  263.   register char number, fillbyte;
  264.  
  265.   while ((number=readchar(infile)) != 0)
  266.     {
  267.       if ((number & LITERAL)==0) /* Expand compressed string from input to output */
  268.         {
  269.           fillbyte=readchar(infile);
  270.           for (number++; number!=0; number--)
  271.             writechar(fillbyte,outfile);
  272.         }
  273.  
  274.       else                          /* Copy string from input to output */
  275.         {
  276.           number&=0x7f;
  277.           for (number++; number!=0; number--)
  278.             writechar(readchar(infile),outfile);
  279.         }
  280.     }
  281. }
  282.  
  283. void __regargs primebuffer(register short *in,register FILE *infile)
  284. {
  285.   in[0]=in[1];  in[1]=in[2];  in[2]=getc(infile);
  286. }
  287.  
  288. void __regargs PackByteRun(register FILE *infile, register FILE *outfile)
  289. {
  290.   char outbuffer[MAX_RUN];
  291.   short inbuff[3]={0,0,0};
  292.   short repeatme;
  293.   register short *incheck=inbuff;
  294.   register short  tag, loop;
  295.  
  296.   primebuffer(incheck,infile); /*  Prime the input "pump"  */
  297.   primebuffer(incheck,infile);
  298.   primebuffer(incheck,infile);
  299.  
  300.   while (incheck[0] != EOF)   /*  As long as input file is not exhausted */
  301.     {
  302.       if (incheck[0] == incheck[1])
  303.         { /***  Compress repeated string of bytes  ***/
  304.           tag=2;
  305.           outbuffer[0] = incheck[0];
  306.           repeatme=incheck[0];
  307.           primebuffer(incheck,infile);  primebuffer(incheck,infile);
  308.           while ((incheck[0] == repeatme)&&(tag != MAX_RUN))
  309.             {
  310.               primebuffer(incheck,infile);
  311.               tag++;
  312.             }
  313.           writechar(tag-1,outfile);
  314.           writechar(outbuffer[0],outfile);
  315.         }
  316.       else
  317.         { /***  Copy string from input to output  ***/
  318.           outbuffer[0] = incheck[0];
  319.           tag=1;
  320.           primebuffer(incheck,infile);
  321.           loop=TRUE;
  322.           while (loop)
  323.             {
  324.               if ((tag == MAX_RUN) || (incheck[0] == EOF) || ((incheck[0] == incheck[1]) && (incheck[1] == incheck[2])))
  325.                 loop=FALSE;
  326.               else
  327.                 {
  328.                   outbuffer[tag] = incheck[0];
  329.                   tag+=1;
  330.                   primebuffer(incheck,infile);
  331.                 }
  332.             }
  333.           writechar(tag+(LITERAL-1),outfile);
  334.           for (loop=0; loop!=tag; loop++)
  335.             writechar(outbuffer[loop],outfile);
  336.         }
  337.     }
  338.   writechar(0,outfile);    /* Terminate output file with zero pad */
  339. }
  340.  
  341. main(int argc, char *argv[])
  342. {
  343.   FILE *infile=NULL, *outfile=NULL;
  344.  
  345.   printf(" BR -- BYte Run (un)packer.      C. 1991-1992 SilverFox SoftWare.\n");
  346.   printf("   Written by Adisak Pochanayon     All Rights Reserved.\n\n");
  347.  
  348.   if (argc != arg_count)
  349.     {
  350.       error= br_error + error_args;
  351.       printf("Error -- Please check arguments.\n");
  352.     }
  353.   else if ((infile  = fopen(argv[arg_srce],"r"))==FILE_ERROR) 
  354.     {
  355.       error= br_error + error_srce;
  356.       printf("Error -- SOURCE file could not be opened.\n");
  357.     }
  358.   else if ((outfile = fopen(argv[arg_dest],"w"))==FILE_ERROR)
  359.     {
  360.       error= br_error + error_dest;
  361.       printf("Error -- DESTINATION file could not be opened.\n");
  362.     }
  363.  
  364.   switch (*argv[arg_flag])
  365.     {
  366.       case 'U' : case 'u' : printf("UnPacking File %s.\n",argv[arg_srce]);
  367.                  UnPackByteRun(infile, outfile); break;
  368.       case 'P' : case 'p' : printf("Packing File %s.\n",argv[arg_srce]); 
  369.                  PackByteRun(infile, outfile);   break;
  370.       default  : error=8; printf("Error -- no FLAG selected.\n");
  371.     }
  372.  
  373.   if (error!=0)
  374.      printf("\nBR command syntax is:  \"BR  FLAG  SOURCE  DESTINATION\"\n   where FLAG can be U - unpack  P - pack.\n\n");
  375.   else
  376.      printf("\nBR successfully completed\n");
  377.  
  378.   fcloseall();
  379.   exit(error);
  380. }
  381.  
  382. -----------------------------END C------------------------------------------
  383.  
  384.      The basic idea of the Byte Run compression is to compress strings of
  385. a single repeating byte wherever they occur.  However, this algorithm
  386. improves slightly upon the algorithm by using a three-byte lookahead.
  387. If the compressor is currently copying a literal string, it will not
  388. break for a two-byte repeat string.  It waits until it finds a three-byte
  389. string.  This results in often saving a byte-tag as interrupting the
  390. literal string requires two byte-tags and the copy-byte (or a total of
  391. three bytes to compress two).
  392.  
  393.      For those who have difficulty following the asm unpacker, there
  394. is a short C unpacking section within the compressor program so that
  395. the compressor can translate files in both directions.
  396.  
  397.      The compression program BR.c does a fairly good job at compressing
  398. graphics files.  Especially for its simplicity.  In order to optimize
  399. compression at a minimal cost in CPU time, it uses a three character
  400. lookahead.  There are several optimizations that could make the compressor
  401. faster or more efficient that I may implement when I have more time.
  402. The first optimization involves bufferring.  Currently I use no file
  403. buffering other than the simple bufferring provided by Lattice C and
  404. Amiga DOS (i.e. inherent buffering).  Using 16K or 32K read and write 
  405. buffers would greatly improve the speed of compression if you are using
  406. floppies.  If you perform the file compression in RAM or on a HD, you
  407. probably won't notice much difference.
  408.  
  409.      The second optimization comes from a lookahead/lookaside compression
  410. algorithm.  Implementing this algorithm allows short byte breaks of 2
  411. repeated bytes in a literal string to be compressed if it allows the
  412. following literal string to be represented in fewer bytes (by saving a
  413. byte tag).  This optimization has a very high CPU cost as it requires
  414. a minimumal 128 character lookahead.  This is an optimization that would
  415. be impossible without buffering.  I have decided not to implement this
  416. optimization as it would slow processing of files by a factor of 20 for
  417. an increase in compression of less than .5%!!!  However, for the picky
  418. out there who want to save a byte or two, I invite you to implement
  419. the optimization.  This optimization can also be extended to byte-tag
  420. start strings to determine whether a literal may be more valuable
  421. than a repeat string.
  422.  
  423. -----------------------------------------------------------------------
  424.  
  425.      As usual, I invite any questions or comments.  Please feel free
  426. to e-mail me at pochanay@cae.wisc.edu for more indepth discussions.
  427. I am still looking for Amiga Artists to help with new games and
  428. programs I am planning on writing.
  429.  
  430.