home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue25 / compress / COMPRESS.ZIP / RLECOMP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-07-09  |  6.4 KB  |  234 lines

  1. { RLECOMP.PAS for TCompress v2.5 -- no change from 2.0
  2.  
  3.  TCompress Event Handler examples & RLE compression source example
  4.  
  5.  This file contains (near the bottom) examples of valid handlers for
  6.  OnCompress, OnRecognize and OnExpand events. The rest of the file
  7.  is routines to perform RLE compression, designed to be called by our
  8.  event handlers. The RLE compression is the same as that built-in to TCompress,
  9.  but uses the 'RLX' compression ID -- if it used 'RLE', TCompress would never
  10.  call it for expansion!
  11.  
  12.  You are free to use this source code as you wish.
  13.  
  14.  (To use, transfer it ALL into a working form, make proper form object
  15.  declarations for the event handlers at the bottom, AND point the
  16.  Compress object's OnCompress/OnExpand and OnRecognize events at them)
  17.  
  18. }
  19.  
  20. const CustomCode = 'RLX' ; { this one is a *custom* RLE handler }
  21.    ChunkSize = 8192;       { work with 8K chunks }
  22.  
  23.  
  24. { Variables for ProcessStreams, getchar, putchar etc. }
  25. var AtStart, InputEOF, inRepeat: Boolean;
  26.     inBuffer, outBuffer: PChar;
  27.     inBmax, inBptr, outBmax, outBptr: Pchar;
  28.     source, dest: TStream;
  29.     lastch: Char;
  30.     DupCount : Integer;
  31.     InChecksum, Outchecksum, Readsize, lChunk, BytesOut: Longint;
  32.  
  33. const RLEescapechar:char=#148; { thus 65 148 37 is 37 A's, and 148 00 is ONE 148 }
  34.  
  35. { Generic routines to get and put characters, buffered. Should not
  36.   change from one compression approach to another... }
  37.  
  38. function GetChar: Char;
  39. var BytesRead: LongInt;
  40. begin
  41.   if inBptr = inBMax then { done buffer }
  42.   begin
  43.      Application.ProcessMessages;
  44.      Result := #0;
  45.      InputEOF := True; { precautionary }
  46.      if readsize=0 then
  47.         exit; { no more boss }
  48.      if lChunk > readsize then
  49.         lChunk := readsize;
  50.      BytesRead := source.Read(inBuffer^, lChunk); { read chunk }
  51.      readsize:=readsize-BytesRead;
  52.      if BytesRead = 0 then { EOF }
  53.         exit
  54.      else
  55.      begin
  56.        InputEOF := False; { keep on in there... }
  57.        inBmax := inBuffer+BytesRead;
  58.        inBptr := inBuffer;
  59.      end;
  60.   end;
  61.   Result := inBptr^;
  62.   InCheckSum:= InChecksum+Ord(Result);
  63.   Inc(inBptr);
  64. end;
  65.  
  66. procedure PutChar(ch: Char);
  67. begin
  68.   if outBptr = outBmax then { filled buffer }
  69.   begin
  70.     Application.ProcessMessages;
  71.     dest.writebuffer(OutBuffer^,ChunkSize);
  72.     outBptr := outBuffer;
  73.   end;
  74.   outBptr^ := ch;
  75.   OutCheckSum:= OutChecksum+Ord(ch);
  76.   Inc(outBptr);
  77.   Inc(BytesOut);
  78. end;
  79.  
  80.  
  81. { Start of RLE-specific code }
  82.  
  83. procedure emit(count: Integer; ch: char);
  84. begin
  85.   if (count > 2) or (count=0) then {only emit if worth it }
  86.   begin
  87.     putChar(RLEescapechar);
  88.     putChar(chr(count));
  89.   end else
  90.   begin
  91.      Dec(count);
  92.      while count > 0 do begin putChar(ch); Dec(count) end;
  93.   end;
  94. end;
  95.  
  96.  
  97. procedure CompressRLE;
  98. var ch: Char;
  99. begin
  100.  while True do
  101.  begin
  102.    ch:= GetChar;
  103.    if InputEOF then
  104.    begin
  105.      if inRepeat then
  106.         emit(Dupcount,lastch); { flag the repeat }
  107.      break;
  108.    end;
  109.    if inRepeat then
  110.    begin
  111.       if (lastch = ch) and (DupCount<255) then
  112.         Inc(DupCount) { and stay in inRepeat }
  113.       else
  114.       begin
  115.         emit(DupCount,lastch); { however many }
  116.         lastch := ch;
  117.         if ch=RLEescapechar then
  118.         begin
  119.            emit(0,RLEEscapechar); { flag it }
  120.         end else
  121.            Putchar(ch);
  122.         inRepeat := False;
  123.       end;
  124.    end else
  125.    begin
  126.      if (ch=RLEescapechar) then
  127.         emit(0,ch)
  128.      else if (ch=lastch) and not AtStart then
  129.      begin
  130.         DupCount := 2;
  131.         inRepeat := True;
  132.      end else Putchar(ch);
  133.      lastch := ch;
  134.    end;
  135.    AtStart := False;
  136.  end; { While not InputEOF }
  137. end;
  138.  
  139. procedure ExpandRLE;
  140. var ch: Char;
  141. begin
  142.  while True do
  143.  begin
  144.    ch:= GetChar;
  145.    if InputEOF then
  146.      break; { done, at last... }
  147.    if ch<> RLEescapechar then
  148.      Putchar(ch)
  149.    else { ok, get a count... MUST be there, really! }
  150.    begin
  151.      DupCount := Ord(GetChar); { 0 if EOF, but not legal, however... }
  152.      if DupCount=0 then Putchar(RLEEscapechar) { special flag }
  153.      else
  154.      begin
  155.         Dec(DupCount);  { because one was already IN the bytestream }
  156.         while Dupcount>0 do begin Putchar(lastch); Dec(DupCount) end;
  157.      end;
  158.    end;
  159.    lastch := ch;
  160.  end; { while }
  161. end;
  162.  
  163. { END of RLE }
  164.  
  165.  
  166. { The main handler -- this shouldn't change from compression method to
  167.   compression method -- it just calls what it should... }
  168.  
  169. function ProcessStreams(outstream, instream: TStream; size: longint;
  170.          var checksum: Longint; mode: TCProcessMode): longint;
  171. begin
  172.   source := inStream; { messy, but allows modular routines w/o zillions of parameters }
  173.   dest := outStream;
  174.   GetMem(inBuffer, ChunkSize); { allocate the buffers }
  175.   inBMax := inBuffer; { initially, until first read... }
  176.   inBptr := inBuffer;
  177.   GetMem(outBuffer, ChunkSize);
  178.   outBMax := outBuffer+ChunkSize;
  179.   outBptr := outBuffer; { not same as inBptr! }
  180.   InputEOF := False;
  181.   AtStart := True;
  182.   lastch := #0;
  183.   inRepeat := False;
  184.   dupCount := 0;
  185.   ReadSize := size;
  186.   lChunk := Chunksize;
  187.   inChecksum:= 0;
  188.   outChecksum := 0;
  189.   try
  190.    if mode = cmCompress then
  191.    begin
  192.     BytesOut:=0;
  193.     CompressRLE;
  194.     checksum := InChecksum;
  195.    end else { expand }
  196.    begin
  197.     BytesOut:=1;
  198.     ExpandRLE;
  199.     checksum := OutChecksum;
  200.    end;
  201.    if outBptr<>OutBuffer then { must flush }
  202.      dest.WriteBuffer(OutBuffer^,outBptr-OutBuffer);
  203.   finally
  204.    FreeMem(inBuffer, ChunkSize); { free the buffer }
  205.    FreeMem(outBuffer, ChunkSize);
  206.   end;
  207.   Result := BytesOut;
  208. end;
  209.  
  210. { Now the custom event handlers which provide hooks from TCompress into
  211.   the above code... }
  212.  
  213. { NOTE: Make CompressID below an OpenString if using this in Delphi 1.0
  214.   -- don't forget the Form-level declaration too... }
  215. procedure TForm1.Compress1Compress(dest, source: TStream;
  216.   var CompressID: String; var Outputsize, checksum: Longint);
  217. begin
  218.   OutputSize := ProcessStreams(dest,source,source.size,checksum, cmCompress);
  219.   CompressID := CustomCode;
  220. end;
  221.  
  222. procedure TForm1.Compress1Recognize(CompressID: String;
  223. var recognized: Boolean);
  224. begin
  225.   if CompressID = CustomCode then recognized := True; { easy, yes? }
  226. end;
  227.  
  228. procedure TForm1.Compress1Expand(dest, source: TStream;
  229.   Sourcesize, DestSize: Longint; CompressID: String; var checksum: Longint);
  230. begin { could check CompressID for more detail, but no need... Destsize not needed either}
  231.   ProcessStreams(dest,source,Sourcesize,checksum, cmExpand);
  232. end;
  233.  
  234.