1 {*******************************************************}
3 { Delphi Supplemental Components }
4 { ZLIB Data Compression Interface Unit }
6 { Copyright (c) 1997 Borland International }
7 { Copyright (c) 1998 Jacques Nomssi Nzali }
9 {*******************************************************}
13 Vampyre Imaging Library
15 http://imaginglib.sourceforge.net
17 You can choose which pascal zlib implementation will be
18 used. IMPASZLIB and FPCPASZLIB are translations of zlib
19 to pascal so they don't need any *.obj files.
20 The others are interfaces to *.obj files (Windows) or
21 *.so libraries (Linux).
22 Default implementation is IMPASZLIB because it can be compiled
23 by all supported compilers and works on all supported platforms.
24 I usually use implementation with the fastest decompression
25 when building release Win32 binaries.
26 FPCPASZLIB is useful for Lazarus applications. FPC's zlib is linked
27 to exe by default so there is no need to link additional (and almost identical)
30 There is a small speed comparison table of some of the
31 supported implementations (TGA image 28 311 570 bytes, compression level = 6,
32 Delphi 9, Win32, Athlon XP 1900).
34 ZLib version Decompression Compression Comp. Size
35 IMPASZLIB | 1.1.2 | 824 ms | 4 280 ms | 18 760 133 B
36 ZLIBEX | 1.2.2 | 710 ms | 1 590 ms* | 19 056 621 B
37 DELPHIZLIB | 1.0.4 | 976 ms | 9 190 ms | 18 365 562 B
38 ZLIBPAS | 1.2.3 | 680 ms | 3 790 ms | 18 365 387 B
39 * obj files are compiled with compression level hardcoded to 1 (fastest)
44 {$I ImagingOptions.inc}
54 { Automatically use FPC's PasZLib when compiling with Lazarus.}
65 {$ELSEIF Defined(DELPHIZLIB)}
66 { Use ZLib unit shipped with Delphi.}
68 {$ELSEIF Defined(ZLIBPAS)}
69 { Pascal interface to ZLib shipped with ZLib C source.}
71 {$ELSEIF Defined(IMPASZLIB)}
72 { Use paszlib modified by me for Delphi and FPC.}
73 imzdeflate
, imzinflate
, impaszlib
,
74 {$ELSEIF Defined(FPCPASZLIB)}
80 {$IF Defined(IMPASZLIB) or Defined(FPCPASZLIB) or Defined(ZLIBPAS)}
82 TZStreamRec
= z_stream
;
100 Z_VERSION_ERROR
= -6;
102 Z_NO_COMPRESSION
= 0;
104 Z_BEST_COMPRESSION
= 9;
105 Z_DEFAULT_COMPRESSION
= -1;
110 Z_DEFAULT_STRATEGY
= 0;
120 { Abstract ancestor class }
121 TCustomZlibStream
= class(TStream
)
125 FOnProgress
: TNotifyEvent
;
127 FBuffer
: array [Word] of Byte;
129 procedure Progress(Sender
: TObject
); dynamic;
130 property OnProgress
: TNotifyEvent read FOnProgress write FOnProgress
;
131 constructor Create(Strm
: TStream
);
134 { TCompressionStream compresses data on the fly as data is written to it, and
135 stores the compressed data to another stream.
137 TCompressionStream is write-only and strictly sequential. Reading from the
138 stream will raise an exception. Using Seek to move the stream pointer
139 will raise an exception.
141 Output data is cached internally, written to the output stream only when
142 the internal output buffer is full. All pending output data is flushed
143 when the stream is destroyed.
145 The Position property returns the number of uncompressed bytes of
146 data that have been written to the stream so far.
148 CompressionRate returns the on-the-fly percentage by which the original
149 data has been compressed: (1 - (CompressedBytes / UncompressedBytes)) * 100
150 If raw data size = 100 and compressed data size = 25, the CompressionRate
153 The OnProgress event is called each time the output buffer is filled and
154 written to the output stream. This is useful for updating a progress
155 indicator when you are writing a large chunk of data to the compression
156 stream in a single call.}
159 TCompressionLevel
= (clNone
, clFastest
, clDefault
, clMax
);
161 TCompressionStream
= class(TCustomZlibStream
)
163 function GetCompressionRate
: Single;
165 constructor Create(CompressionLevel
: TCompressionLevel
; Dest
: TStream
);
166 destructor Destroy
; override;
167 function Read(var Buffer
; Count
: Longint): Longint; override;
168 function Write(const Buffer
; Count
: Longint): Longint; override;
169 function Seek(Offset
: Longint; Origin
: Word): Longint; override;
170 property CompressionRate
: Single read GetCompressionRate
;
174 { TDecompressionStream decompresses data on the fly as data is read from it.
176 Compressed data comes from a separate source stream. TDecompressionStream
177 is read-only and unidirectional; you can seek forward in the stream, but not
178 backwards. The special case of setting the stream position to zero is
179 allowed. Seeking forward decompresses data until the requested position in
180 the uncompressed data has been reached. Seeking backwards, seeking relative
181 to the end of the stream, requesting the size of the stream, and writing to
182 the stream will raise an exception.
184 The Position property returns the number of bytes of uncompressed data that
185 have been read from the stream so far.
187 The OnProgress event is called each time the internal input buffer of
188 compressed data is exhausted and the next block is read from the input stream.
189 This is useful for updating a progress indicator when you are reading a
190 large chunk of data from the decompression stream in a single call.}
192 TDecompressionStream
= class(TCustomZlibStream
)
194 constructor Create(Source
: TStream
);
195 destructor Destroy
; override;
196 function Read(var Buffer
; Count
: Longint): Longint; override;
197 function Write(const Buffer
; Count
: Longint): Longint; override;
198 function Seek(Offset
: Longint; Origin
: Word): Longint; override;
204 { CompressBuf compresses data, buffer to buffer, in one call.
205 In: InBuf = ptr to compressed data
206 InBytes = number of bytes in InBuf
207 Out: OutBuf = ptr to newly allocated buffer containing decompressed data
208 OutBytes = number of bytes in OutBuf }
209 procedure CompressBuf(const InBuf
: Pointer; InBytes
: Integer;
210 var OutBuf
: Pointer; var OutBytes
: Integer;
211 CompressLevel
: Integer = Z_DEFAULT_COMPRESSION
);
213 { DecompressBuf decompresses data, buffer to buffer, in one call.
214 In: InBuf = ptr to compressed data
215 InBytes = number of bytes in InBuf
216 OutEstimate = zero, or est. size of the decompressed data
217 Out: OutBuf = ptr to newly allocated buffer containing decompressed data
218 OutBytes = number of bytes in OutBuf }
219 procedure DecompressBuf(const InBuf
: Pointer; InBytes
: Integer;
220 OutEstimate
: Integer; var OutBuf
: Pointer; var OutBytes
: Integer);
224 EZlibError
= class(Exception
);
225 ECompressionError
= class(EZlibError
);
226 EDecompressionError
= class(EZlibError
);
231 ZErrorMessages
: array[0..9] of PAnsiChar = (
232 'need dictionary', // Z_NEED_DICT (2)
233 'stream end', // Z_STREAM_END (1)
235 'file error', // Z_ERRNO (-1)
236 'stream error', // Z_STREAM_ERROR (-2)
237 'data error', // Z_DATA_ERROR (-3)
238 'insufficient memory', // Z_MEM_ERROR (-4)
239 'buffer error', // Z_BUF_ERROR (-5)
240 'incompatible version', // Z_VERSION_ERROR (-6)
243 function zlibAllocMem(AppData
: Pointer; Items
, Size
: Cardinal): Pointer;
245 GetMem(Result
, Items
*Size
);
248 procedure zlibFreeMem(AppData
, Block
: Pointer);
253 function CCheck(code
: Integer): Integer;
257 raise ECompressionError
.Create('zlib: ' + ZErrorMessages
[2 - code
]);
260 function DCheck(code
: Integer): Integer;
264 raise EDecompressionError
.Create('zlib: ' + ZErrorMessages
[2 - code
]);
267 procedure CompressBuf(const InBuf
: Pointer; InBytes
: Integer;
268 var OutBuf
: Pointer; var OutBytes
: Integer;
269 CompressLevel
: Integer);
274 FillChar(strm
, sizeof(strm
), 0);
276 strm
.zalloc
:= @zlibAllocMem
;
277 strm
.zfree
:= @zlibFreeMem
;
279 OutBytes
:= ((InBytes
+ (InBytes
div 10) + 12) + 255) and not 255;
280 GetMem(OutBuf
, OutBytes
);
282 strm
.next_in
:= InBuf
;
283 strm
.avail_in
:= InBytes
;
284 strm
.next_out
:= OutBuf
;
285 strm
.avail_out
:= OutBytes
;
286 CCheck(deflateInit_(strm
, CompressLevel
, zlib_version
, sizeof(strm
)));
288 while CCheck(deflate(strm
, Z_FINISH
)) <> Z_STREAM_END
do
292 ReallocMem(OutBuf
, OutBytes
);
293 strm
.next_out
:= Pointer(Integer(OutBuf
) + (Integer(strm
.next_out
) - Integer(P
)));
294 strm
.avail_out
:= 256;
297 CCheck(deflateEnd(strm
));
299 ReallocMem(OutBuf
, strm
.total_out
);
300 OutBytes
:= strm
.total_out
;
302 zlibFreeMem(nil, OutBuf
);
307 procedure DecompressBuf(const InBuf
: Pointer; InBytes
: Integer;
308 OutEstimate
: Integer; var OutBuf
: Pointer; var OutBytes
: Integer);
314 FillChar(strm
, sizeof(strm
), 0);
316 strm
.zalloc
:= @zlibAllocMem
;
317 strm
.zfree
:= @zlibFreeMem
;
319 BufInc
:= (InBytes
+ 255) and not 255;
320 if OutEstimate
= 0 then
323 OutBytes
:= OutEstimate
;
324 GetMem(OutBuf
, OutBytes
);
326 strm
.next_in
:= InBuf
;
327 strm
.avail_in
:= InBytes
;
328 strm
.next_out
:= OutBuf
;
329 strm
.avail_out
:= OutBytes
;
330 DCheck(inflateInit_(strm
, zlib_version
, sizeof(strm
)));
332 while DCheck(inflate(strm
, Z_NO_FLUSH
)) <> Z_STREAM_END
do
335 Inc(OutBytes
, BufInc
);
336 ReallocMem(OutBuf
, OutBytes
);
337 strm
.next_out
:= Pointer(Integer(OutBuf
) + (Integer(strm
.next_out
) - Integer(P
)));
338 strm
.avail_out
:= BufInc
;
341 DCheck(inflateEnd(strm
));
343 ReallocMem(OutBuf
, strm
.total_out
);
344 OutBytes
:= strm
.total_out
;
346 zlibFreeMem(nil, OutBuf
);
352 { TCustomZlibStream }
354 constructor TCustomZLibStream
.Create(Strm
: TStream
);
358 FStrmPos
:= Strm
.Position
;
360 FZRec
.zalloc
:= @zlibAllocMem
;
361 FZRec
.zfree
:= @zlibFreeMem
;
365 procedure TCustomZLibStream
.Progress(Sender
: TObject
);
367 if Assigned(FOnProgress
) then FOnProgress(Sender
);
370 { TCompressionStream }
372 constructor TCompressionStream
.Create(CompressionLevel
: TCompressionLevel
;
375 Levels
: array [TCompressionLevel
] of ShortInt =
376 (Z_NO_COMPRESSION
, Z_BEST_SPEED
, Z_DEFAULT_COMPRESSION
, Z_BEST_COMPRESSION
);
378 inherited Create(Dest
);
379 FZRec
.next_out
:= @FBuffer
;
380 FZRec
.avail_out
:= sizeof(FBuffer
);
381 CCheck(deflateInit_(FZRec
, Levels
[CompressionLevel
], zlib_version
, sizeof(FZRec
)));
384 destructor TCompressionStream
.Destroy
;
386 FZRec
.next_in
:= nil;
389 if FStrm
.Position
<> FStrmPos
then FStrm
.Position
:= FStrmPos
;
390 while (CCheck(deflate(FZRec
, Z_FINISH
)) <> Z_STREAM_END
)
391 and (FZRec
.avail_out
= 0) do
393 FStrm
.WriteBuffer(FBuffer
, sizeof(FBuffer
));
394 FZRec
.next_out
:= @FBuffer
;
395 FZRec
.avail_out
:= sizeof(FBuffer
);
397 if FZRec
.avail_out
< sizeof(FBuffer
) then
398 FStrm
.WriteBuffer(FBuffer
, sizeof(FBuffer
) - FZRec
.avail_out
);
405 function TCompressionStream
.Read(var Buffer
; Count
: Longint): Longint;
407 raise ECompressionError
.Create('Invalid stream operation');
410 function TCompressionStream
.Write(const Buffer
; Count
: Longint): Longint;
412 FZRec
.next_in
:= @Buffer
;
413 FZRec
.avail_in
:= Count
;
414 if FStrm
.Position
<> FStrmPos
then FStrm
.Position
:= FStrmPos
;
415 while (FZRec
.avail_in
> 0) do
417 CCheck(deflate(FZRec
, 0));
418 if FZRec
.avail_out
= 0 then
420 FStrm
.WriteBuffer(FBuffer
, sizeof(FBuffer
));
421 FZRec
.next_out
:= @FBuffer
;
422 FZRec
.avail_out
:= sizeof(FBuffer
);
423 FStrmPos
:= FStrm
.Position
;
430 function TCompressionStream
.Seek(Offset
: Longint; Origin
: Word): Longint;
432 if (Offset
= 0) and (Origin
= soFromCurrent
) then
433 Result
:= FZRec
.total_in
435 raise ECompressionError
.Create('Invalid stream operation');
438 function TCompressionStream
.GetCompressionRate
: Single;
440 if FZRec
.total_in
= 0 then
443 Result
:= (1.0 - (FZRec
.total_out
/ FZRec
.total_in
)) * 100.0;
446 { TDecompressionStream }
448 constructor TDecompressionStream
.Create(Source
: TStream
);
450 inherited Create(Source
);
451 FZRec
.next_in
:= @FBuffer
;
453 DCheck(inflateInit_(FZRec
, zlib_version
, sizeof(FZRec
)));
456 destructor TDecompressionStream
.Destroy
;
462 function TDecompressionStream
.Read(var Buffer
; Count
: Longint): Longint;
464 FZRec
.next_out
:= @Buffer
;
465 FZRec
.avail_out
:= Count
;
466 if FStrm
.Position
<> FStrmPos
then FStrm
.Position
:= FStrmPos
;
467 while (FZRec
.avail_out
> 0) do
469 if FZRec
.avail_in
= 0 then
471 FZRec
.avail_in
:= FStrm
.Read(FBuffer
, sizeof(FBuffer
));
472 if FZRec
.avail_in
= 0 then
474 Result
:= Count
- Integer(FZRec
.avail_out
);
477 FZRec
.next_in
:= @FBuffer
;
478 FStrmPos
:= FStrm
.Position
;
481 CCheck(inflate(FZRec
, 0));
486 function TDecompressionStream
.Write(const Buffer
; Count
: Longint): Longint;
488 raise EDecompressionError
.Create('Invalid stream operation');
491 function TDecompressionStream
.Seek(Offset
: Longint; Origin
: Word): Longint;
494 Buf
: array [0..4095] of Byte;
496 if (Offset
= 0) and (Origin
= soFromBeginning
) then
498 DCheck(inflateReset(FZRec
));
499 FZRec
.next_in
:= @FBuffer
;
504 else if ( (Offset
>= 0) and (Origin
= soFromCurrent
)) or
505 ( ((Offset
- Integer(FZRec
.total_out
)) > 0) and (Origin
= soFromBeginning
)) then
507 if Origin
= soFromBeginning
then Dec(Offset
, FZRec
.total_out
);
510 for I
:= 1 to Offset
div sizeof(Buf
) do
511 ReadBuffer(Buf
, sizeof(Buf
));
512 ReadBuffer(Buf
, Offset
mod sizeof(Buf
));
516 raise EDecompressionError
.Create('Invalid stream operation');
517 Result
:= FZRec
.total_out
;