DEADSOFTWARE

add cvars that allows window resizing
[d2df-sdl.git] / src / lib / vampimg / ZLib / dzlib.pas
1 {*******************************************************}
2 { }
3 { Delphi Supplemental Components }
4 { ZLIB Data Compression Interface Unit }
5 { }
6 { Copyright (c) 1997 Borland International }
7 { Copyright (c) 1998 Jacques Nomssi Nzali }
8 { }
9 {*******************************************************}
11 {
12 Modified for
13 Vampyre Imaging Library
14 by Marek Mauder
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)
28 IMPASZLIB.
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)
40 }
42 unit dzlib;
44 {$I ImagingOptions.inc}
46 interface
48 {$DEFINE IMPASZLIB}
49 { $DEFINE ZLIBPAS}
50 { $DEFINE FPCPASZLIB}
51 { $DEFINE ZLIBEX}
52 { $DEFINE DELPHIZLIB}
54 { Automatically use FPC's PasZLib when compiling with FPC.}
56 {$IFDEF FPC}
57 {$UNDEF IMPASZLIB}
58 {$DEFINE FPCPASZLIB}
59 {$ENDIF}
61 uses
62 {$IF Defined(IMPASZLIB)}
63 { Use paszlib modified by me for Delphi and FPC }
64 imzdeflate, imzinflate, impaszlib,
65 {$ELSEIF Defined(FPCPASZLIB)}
66 { Use FPC's paszlib }
67 zbase, paszlib,
68 {$ELSEIF Defined(ZLIBPAS)}
69 { Pascal interface to ZLib shipped with ZLib C source }
70 zlibpas,
71 {$ELSEIF Defined(ZLIBEX)}
72 { Use ZlibEx unit }
73 ZLibEx,
74 {$ELSEIF Defined(DELPHIZLIB)}
75 { Use ZLib unit shipped with Delphi }
76 ZLib,
77 {$IFEND}
78 ImagingTypes, SysUtils, Classes;
80 {$IF Defined(IMPASZLIB) or Defined(FPCPASZLIB) or Defined(ZLIBPAS)}
81 type
82 TZStreamRec = z_stream;
83 {$IFEND}
85 const
86 Z_NO_FLUSH = 0;
87 Z_PARTIAL_FLUSH = 1;
88 Z_SYNC_FLUSH = 2;
89 Z_FULL_FLUSH = 3;
90 Z_FINISH = 4;
92 Z_OK = 0;
93 Z_STREAM_END = 1;
94 Z_NEED_DICT = 2;
95 Z_ERRNO = -1;
96 Z_STREAM_ERROR = -2;
97 Z_DATA_ERROR = -3;
98 Z_MEM_ERROR = -4;
99 Z_BUF_ERROR = -5;
100 Z_VERSION_ERROR = -6;
102 Z_NO_COMPRESSION = 0;
103 Z_BEST_SPEED = 1;
104 Z_BEST_COMPRESSION = 9;
105 Z_DEFAULT_COMPRESSION = -1;
107 Z_FILTERED = 1;
108 Z_HUFFMAN_ONLY = 2;
109 Z_RLE = 3;
110 Z_DEFAULT_STRATEGY = 0;
112 Z_BINARY = 0;
113 Z_ASCII = 1;
114 Z_UNKNOWN = 2;
116 Z_DEFLATED = 8;
118 type
119 { Abstract ancestor class }
120 TCustomZlibStream = class(TStream)
121 private
122 FStrm: TStream;
123 FStrmPos: Integer;
124 FOnProgress: TNotifyEvent;
125 FZRec: TZStreamRec;
126 FBuffer: array [Word] of Byte;
127 protected
128 procedure Progress(Sender: TObject); dynamic;
129 property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
130 constructor Create(Strm: TStream);
131 end;
133 { TCompressionStream compresses data on the fly as data is written to it, and
134 stores the compressed data to another stream.
136 TCompressionStream is write-only and strictly sequential. Reading from the
137 stream will raise an exception. Using Seek to move the stream pointer
138 will raise an exception.
140 Output data is cached internally, written to the output stream only when
141 the internal output buffer is full. All pending output data is flushed
142 when the stream is destroyed.
144 The Position property returns the number of uncompressed bytes of
145 data that have been written to the stream so far.
147 CompressionRate returns the on-the-fly percentage by which the original
148 data has been compressed: (1 - (CompressedBytes / UncompressedBytes)) * 100
149 If raw data size = 100 and compressed data size = 25, the CompressionRate
150 is 75%
152 The OnProgress event is called each time the output buffer is filled and
153 written to the output stream. This is useful for updating a progress
154 indicator when you are writing a large chunk of data to the compression
155 stream in a single call.}
158 TCompressionLevel = (clNone, clFastest, clDefault, clMax);
160 TCompressionStream = class(TCustomZlibStream)
161 private
162 function GetCompressionRate: Single;
163 public
164 constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream);
165 destructor Destroy; override;
166 function Read(var Buffer; Count: Longint): Longint; override;
167 function Write(const Buffer; Count: Longint): Longint; override;
168 function Seek(Offset: Longint; Origin: Word): Longint; override;
169 property CompressionRate: Single read GetCompressionRate;
170 property OnProgress;
171 end;
173 { TDecompressionStream decompresses data on the fly as data is read from it.
175 Compressed data comes from a separate source stream. TDecompressionStream
176 is read-only and unidirectional; you can seek forward in the stream, but not
177 backwards. The special case of setting the stream position to zero is
178 allowed. Seeking forward decompresses data until the requested position in
179 the uncompressed data has been reached. Seeking backwards, seeking relative
180 to the end of the stream, requesting the size of the stream, and writing to
181 the stream will raise an exception.
183 The Position property returns the number of bytes of uncompressed data that
184 have been read from the stream so far.
186 The OnProgress event is called each time the internal input buffer of
187 compressed data is exhausted and the next block is read from the input stream.
188 This is useful for updating a progress indicator when you are reading a
189 large chunk of data from the decompression stream in a single call.}
191 TDecompressionStream = class(TCustomZlibStream)
192 public
193 constructor Create(Source: TStream);
194 destructor Destroy; override;
195 function Read(var Buffer; Count: Longint): Longint; override;
196 function Write(const Buffer; Count: Longint): Longint; override;
197 function Seek(Offset: Longint; Origin: Word): Longint; override;
198 property OnProgress;
199 end;
203 { CompressBuf compresses data, buffer to buffer, in one call.
204 In: InBuf = ptr to compressed data
205 InBytes = number of bytes in InBuf
206 Out: OutBuf = ptr to newly allocated buffer containing decompressed data
207 OutBytes = number of bytes in OutBuf }
208 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
209 var OutBuf: Pointer; var OutBytes: Integer;
210 CompressLevel: Integer = Z_DEFAULT_COMPRESSION;
211 CompressStrategy: Integer = Z_DEFAULT_STRATEGY);
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);
223 type
224 EZlibError = class(Exception);
225 ECompressionError = class(EZlibError);
226 EDecompressionError = class(EZlibError);
228 implementation
230 const
231 ZErrorMessages: array[0..9] of PAnsiChar = (
232 'need dictionary', // Z_NEED_DICT (2)
233 'stream end', // Z_STREAM_END (1)
234 '', // Z_OK (0)
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)
241 '');
243 function zlibAllocMem(AppData: Pointer; Items, Size: Cardinal): Pointer;
244 begin
245 GetMem(Result, Items*Size);
246 end;
248 procedure zlibFreeMem(AppData, Block: Pointer);
249 begin
250 FreeMem(Block);
251 end;
253 function CCheck(code: Integer): Integer;
254 begin
255 Result := code;
256 if code < 0 then
257 raise ECompressionError.Create('zlib: ' + ZErrorMessages[2 - code]);
258 end;
260 function DCheck(code: Integer): Integer;
261 begin
262 Result := code;
263 if code < 0 then
264 raise EDecompressionError.Create('zlib: ' + ZErrorMessages[2 - code]);
265 end;
267 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
268 var OutBuf: Pointer; var OutBytes: Integer;
269 CompressLevel, CompressStrategy: Integer);
270 var
271 strm: TZStreamRec;
272 P: Pointer;
273 begin
274 FillChar(strm, sizeof(strm), 0);
275 {$IFNDEF FPCPASZLIB}
276 strm.zalloc := @zlibAllocMem;
277 strm.zfree := @zlibFreeMem;
278 {$ENDIF}
279 OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
280 GetMem(OutBuf, OutBytes);
281 try
282 strm.next_in := InBuf;
283 strm.avail_in := InBytes;
284 strm.next_out := OutBuf;
285 strm.avail_out := OutBytes;
287 CCheck(deflateInit2(strm, CompressLevel, Z_DEFLATED, MAX_WBITS,
288 DEF_MEM_LEVEL, CompressStrategy));
290 try
291 while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
292 begin
293 P := OutBuf;
294 Inc(OutBytes, 256);
295 ReallocMem(OutBuf, OutBytes);
296 strm.next_out := Pointer(PtrUInt(OutBuf) + (PtrUInt(strm.next_out) - PtrUInt(P)));
297 strm.avail_out := 256;
298 end;
299 finally
300 CCheck(deflateEnd(strm));
301 end;
302 ReallocMem(OutBuf, strm.total_out);
303 OutBytes := strm.total_out;
304 except
305 zlibFreeMem(nil, OutBuf);
306 raise
307 end;
308 end;
310 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
311 OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
312 var
313 strm: TZStreamRec;
314 P: Pointer;
315 BufInc: Integer;
316 begin
317 FillChar(strm, sizeof(strm), 0);
318 {$IFNDEF FPCPASZLIB}
319 strm.zalloc := @zlibAllocMem;
320 strm.zfree := @zlibFreeMem;
321 {$ENDIF}
322 BufInc := (InBytes + 255) and not 255;
323 if OutEstimate = 0 then
324 OutBytes := BufInc
325 else
326 OutBytes := OutEstimate;
327 GetMem(OutBuf, OutBytes);
328 try
329 strm.next_in := InBuf;
330 strm.avail_in := InBytes;
331 strm.next_out := OutBuf;
332 strm.avail_out := OutBytes;
333 DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
334 try
335 while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do
336 begin
337 P := OutBuf;
338 Inc(OutBytes, BufInc);
339 ReallocMem(OutBuf, OutBytes);
340 strm.next_out := Pointer(PtrUInt(OutBuf) + (PtrUInt(strm.next_out) - PtrUInt(P)));
341 strm.avail_out := BufInc;
342 end;
343 finally
344 DCheck(inflateEnd(strm));
345 end;
346 ReallocMem(OutBuf, strm.total_out);
347 OutBytes := strm.total_out;
348 except
349 zlibFreeMem(nil, OutBuf);
350 raise
351 end;
352 end;
355 { TCustomZlibStream }
357 constructor TCustomZLibStream.Create(Strm: TStream);
358 begin
359 inherited Create;
360 FStrm := Strm;
361 FStrmPos := Strm.Position;
362 {$IFNDEF FPCPASZLIB}
363 FZRec.zalloc := @zlibAllocMem;
364 FZRec.zfree := @zlibFreeMem;
365 {$ENDIF}
366 end;
368 procedure TCustomZLibStream.Progress(Sender: TObject);
369 begin
370 if Assigned(FOnProgress) then FOnProgress(Sender);
371 end;
373 { TCompressionStream }
375 constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
376 Dest: TStream);
377 const
378 Levels: array [TCompressionLevel] of ShortInt =
379 (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
380 begin
381 inherited Create(Dest);
382 FZRec.next_out := @FBuffer;
383 FZRec.avail_out := sizeof(FBuffer);
384 CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec)));
385 end;
387 destructor TCompressionStream.Destroy;
388 begin
389 FZRec.next_in := nil;
390 FZRec.avail_in := 0;
391 try
392 if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
393 while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
394 and (FZRec.avail_out = 0) do
395 begin
396 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
397 FZRec.next_out := @FBuffer;
398 FZRec.avail_out := sizeof(FBuffer);
399 end;
400 if FZRec.avail_out < sizeof(FBuffer) then
401 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
402 finally
403 deflateEnd(FZRec);
404 end;
405 inherited Destroy;
406 end;
408 function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
409 begin
410 raise ECompressionError.Create('Invalid stream operation');
411 end;
413 function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
414 begin
415 FZRec.next_in := @Buffer;
416 FZRec.avail_in := Count;
417 if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
418 while (FZRec.avail_in > 0) do
419 begin
420 CCheck(deflate(FZRec, 0));
421 if FZRec.avail_out = 0 then
422 begin
423 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
424 FZRec.next_out := @FBuffer;
425 FZRec.avail_out := sizeof(FBuffer);
426 FStrmPos := FStrm.Position;
427 Progress(Self);
428 end;
429 end;
430 Result := Count;
431 end;
433 function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
434 begin
435 if (Offset = 0) and (Origin = soFromCurrent) then
436 Result := FZRec.total_in
437 else
438 raise ECompressionError.Create('Invalid stream operation');
439 end;
441 function TCompressionStream.GetCompressionRate: Single;
442 begin
443 if FZRec.total_in = 0 then
444 Result := 0
445 else
446 Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0;
447 end;
449 { TDecompressionStream }
451 constructor TDecompressionStream.Create(Source: TStream);
452 begin
453 inherited Create(Source);
454 FZRec.next_in := @FBuffer;
455 FZRec.avail_in := 0;
456 DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec)));
457 end;
459 destructor TDecompressionStream.Destroy;
460 begin
461 inflateEnd(FZRec);
462 inherited Destroy;
463 end;
465 function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
466 begin
467 FZRec.next_out := @Buffer;
468 FZRec.avail_out := Count;
469 if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
470 while (FZRec.avail_out > 0) do
471 begin
472 if FZRec.avail_in = 0 then
473 begin
474 FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
475 if FZRec.avail_in = 0 then
476 begin
477 Result := Count - Integer(FZRec.avail_out);
478 Exit;
479 end;
480 FZRec.next_in := @FBuffer;
481 FStrmPos := FStrm.Position;
482 Progress(Self);
483 end;
484 CCheck(inflate(FZRec, 0));
485 end;
486 Result := Count;
487 end;
489 function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
490 begin
491 raise EDecompressionError.Create('Invalid stream operation');
492 end;
494 function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
495 var
496 I: Integer;
497 Buf: array [0..4095] of Byte;
498 begin
499 if (Offset = 0) and (Origin = soFromBeginning) then
500 begin
501 DCheck(inflateReset(FZRec));
502 FZRec.next_in := @FBuffer;
503 FZRec.avail_in := 0;
504 FStrm.Position := 0;
505 FStrmPos := 0;
506 end
507 else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
508 ( ((Offset - Integer(FZRec.total_out)) > 0) and (Origin = soFromBeginning)) then
509 begin
510 if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
511 if Offset > 0 then
512 begin
513 for I := 1 to Offset div sizeof(Buf) do
514 ReadBuffer(Buf, sizeof(Buf));
515 ReadBuffer(Buf, Offset mod sizeof(Buf));
516 end;
517 end
518 else
519 raise EDecompressionError.Create('Invalid stream operation');
520 Result := FZRec.total_out;
521 end;
523 end.