2 Vampyre Imaging Library
4 http://imaginglib.sourceforge.net
6 The contents of this file are used with permission, subject to the Mozilla
7 Public License Version 1.1 (the "License"); you may not use this file except
8 in compliance with the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/MPL-1.1.html
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
13 the specific language governing rights and limitations under the License.
15 Alternatively, the contents of this file may be used under the terms of the
16 GNU Lesser General Public License (the "LGPL License"), in which case the
17 provisions of the LGPL License are applicable instead of those above.
18 If you wish to allow use of your version of this file only under the terms
19 of the LGPL License and not to allow others to use your version of this file
20 under the MPL, indicate your decision by deleting the provisions above and
21 replace them with the notice and other provisions required by the LGPL
22 License. If you do not delete the provisions above, a recipient may use
23 your version of this file under either the MPL or the LGPL License.
25 For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
28 { This unit contains utility functions and types for Imaging library.}
31 {$I ImagingOptions.inc}
36 SysUtils
, Classes
, Types
;
43 TByteArray
= array[0..MaxInt
- 1] of Byte;
44 PByteArray
= ^TByteArray
;
45 TWordArray
= array[0..MaxInt
div 2 - 1] of Word;
46 PWordArray
= ^TWordArray
;
47 TLongIntArray
= array[0..MaxInt
div 4 - 1] of LongInt;
48 PLongIntArray
= ^TLongIntArray
;
49 TLongWordArray
= array[0..MaxInt
div 4 - 1] of LongWord;
50 PLongWordArray
= ^TLongWordArray
;
51 TInt64Array
= array[0..MaxInt
div 8 - 1] of Int64;
52 PInt64Array
= ^TInt64Array
;
53 TSingleArray
= array[0..MaxInt
div 4 - 1] of Single;
54 PSingleArray
= ^TSingleArray
;
55 TBooleanArray
= array[0..MaxInt
- 1] of Boolean;
56 PBooleanArray
= ^TBooleanArray
;
58 TDynByteArray
= array of Byte;
59 TDynIntegerArray
= array of Integer;
60 TDynBooleanArray
= array of Boolean;
61 TDynStringArray
= array of string;
63 TWordRec
= packed record
69 TWordRecArray
= array[0..MaxInt
div 2 - 1] of TWordRec
;
70 PWordRecArray
= ^TWordRecArray
;
72 TLongWordRec
= packed record
74 0: (LongWordValue
: LongWord);
76 { Array variants - Index 0 means lowest significant byte (word, ...).}
77 2: (Words
: array[0..1] of Word);
78 3: (Bytes
: array[0..3] of Byte);
80 PLongWordRec
= ^TLongWordRec
;
81 TLongWordRecArray
= array[0..MaxInt
div 4 - 1] of TLongWordRec
;
82 PLongWordRecArray
= ^TLongWordRecArray
;
84 TInt64Rec
= packed record
86 0: (Int64Value
: Int64);
87 1: (Low
, High
: LongWord);
88 { Array variants - Index 0 means lowest significant byte (word, ...).}
89 2: (Words
: array[0..3] of Word);
90 3: (Bytes
: array[0..7] of Byte);
92 PInt64Rec
= ^TInt64Rec
;
93 TInt64RecArray
= array[0..MaxInt
div 8 - 1] of TInt64Rec
;
94 PInt64RecArray
= ^TInt64RecArray
;
100 1: (Data32
: LongWord);
102 PFloatHelper
= ^TFloatHelper
;
105 Left
, Top
, Right
, Bottom
: Single;
108 TChar2
= array[0..1] of AnsiChar;
109 TChar3
= array[0..2] of AnsiChar;
110 TChar4
= array[0..3] of AnsiChar;
111 TChar8
= array[0..7] of AnsiChar;
112 TChar16
= array[0..15] of AnsiChar;
113 TAnsiCharSet
= set of AnsiChar;
115 ENotImplemented
= class(Exception
)
120 { Options for BuildFileList function:
121 flFullNames - file names in result will have full path names
122 (ExtractFileDir(Path) + FileName)
123 flRelNames - file names in result will have names relative to
124 ExtractFileDir(Path) dir
125 flRecursive - adds files in subdirectories found in Path.}
126 TFileListOption
= (flFullNames
, flRelNames
, flRecursive
);
127 TFileListOptions
= set of TFileListOption
;
130 { Frees class instance and sets its reference to nil.}
131 procedure FreeAndNil(var Obj
);
132 { Frees pointer and sets it to nil.}
133 procedure FreeMemNil(var P
); {$IFDEF USE_INLINE}inline;{$ENDIF}
134 { Replacement of standard System.FreeMem procedure which checks if P is nil
135 (this is only needed for Free Pascal, Delphi makes checks in its FreeMem).}
136 procedure FreeMem(P
: Pointer); {$IFDEF USE_INLINE}inline;{$ENDIF}
137 { Returns current exception object. Do not call outside exception handler.}
138 function GetExceptObject
: Exception
; {$IFDEF USE_INLINE}inline;{$ENDIF}
139 { Returns time value with microsecond resolution.}
140 function GetTimeMicroseconds
: Int64;
141 { Returns time value with milisecond resolution.}
142 function GetTimeMilliseconds
: Int64;
144 { Returns file extension (without "." dot)}
145 function GetFileExt(const FileName
: string): string;
146 { Returns file name of application's executable.}
147 function GetAppExe
: string;
148 { Returns directory where application's exceutable is located without
149 path delimiter at the end.}
150 function GetAppDir
: string;
151 { Works like SysUtils.ExtractFileName but supports '/' and '\' dir delimiters
152 at the same time (whereas ExtractFileName supports on default delimiter on current platform).}
153 function GetFileName(const FileName
: string): string;
154 { Works like SysUtils.ExtractFileDir but supports '/' and '\' dir delimiters
155 at the same time (whereas ExtractFileDir supports on default delimiter on current platform).}
156 function GetFileDir(const FileName
: string): string;
157 { Returns True if Subject matches given Mask with optional case sensitivity.
158 Mask can contain ? and * special characters: ? matches
159 one character, * matches zero or more characters.}
160 function StrMaskMatch(const Subject
, Mask
: string; CaseSensitive
: Boolean = False): Boolean;
161 { This function fills Files string list with names of files found
162 with FindFirst/FindNext functions (See details on Path/Atrr here).
163 - BuildFileList('c:\*.*', faAnyFile, List, [flRecursive]) returns
164 list of all files (only name.ext - no path) on C drive
165 - BuildFileList('d:\*.*', faDirectory, List, [flFullNames]) returns
166 list of all directories (d:\dirxxx) in root of D drive.}
167 function BuildFileList(Path
: string; Attr
: LongInt; Files
: TStrings
;
168 Options
: TFileListOptions
= []): Boolean;
169 { Similar to RTL's Pos function but with optional Offset where search will start.
170 This function is in the RTL StrUtils unit but }
171 function PosEx(const SubStr
, S
: string; Offset
: LongInt = 1): LongInt;
172 { Same as PosEx but without case sensitivity.}
173 function PosNoCase(const SubStr
, S
: string; Offset
: LongInt = 1): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
174 { Returns a sub-string from S which is followed by
175 Sep separator and deletes the sub-string from S including the separator.}
176 function StrToken(var S
: string; Sep
: Char): string;
177 { Same as StrToken but searches from the end of S string.}
178 function StrTokenEnd(var S
: string; Sep
: Char): string;
179 { Fills instance of TStrings with tokens from string S where tokens are separated by
180 one of Seps characters.}
181 procedure StrTokensToList(const S
: string; Sep
: Char; Tokens
: TStrings
);
182 { Returns string representation of integer number (with digit grouping).
183 Uses current locale.}
184 function IntToStrFmt(const I
: Int64): string; {$IFDEF USE_INLINE}inline;{$ENDIF}
185 { Returns string representation of float number (with digit grouping).
186 Uses current locale.}
187 function FloatToStrFmt(const F
: Double; Precision
: Integer = 2): string; {$IFDEF USE_INLINE}inline;{$ENDIF}
188 { Returns format settings for parsing floats (dot as decimal separator).
189 Useful when fomatting/parsing floats etc.}
190 function GetFormatSettingsForFloats
: TFormatSettings
;
191 { Returns True if S contains at least one of the substrings in SubStrs array. Case sensitive.}
192 function ContainsAnySubStr(const S
: string; const SubStrs
: array of string): Boolean;
193 { Extracts substring starting at IdxStart ending at IdxEnd.
194 S[IdxEnd] is not included in the result.}
195 function SubString(const S
: string; IdxStart
, IdxEnd
: Integer): string; {$IFDEF USE_INLINE}inline;{$ENDIF}
197 { Clamps integer value to range <Min, Max>}
198 function ClampInt(Number
: LongInt; Min
, Max
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
199 { Clamps float value to range <Min, Max>}
200 function ClampFloat(Number
: Single; Min
, Max
: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
201 { Clamps integer value to Byte boundaries.}
202 function ClampToByte(Value
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
203 { Clamps integer value to Word boundaries.}
204 function ClampToWord(Value
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
205 { Returns True if Num is power of 2.}
206 function IsPow2(Num
: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
207 { Returns next power of 2 greater than or equal to Num
208 (if Num itself is power of 2 then it retuns Num).}
209 function NextPow2(Num
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
210 { Raises 2 to the given integer power (in range [0, 30]).}
211 function Pow2Int(Exponent
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
212 { Raises Base to any power.}
213 function Power(const Base
, Exponent
: Single): Single;
214 { Returns log base 2 of integer X (max 2^30) or -1 if X is not power of 2.}
215 function Log2Int(X
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
216 { Returns log base 2 of X.}
217 function Log2(X
: Single): Single;
218 { Returns log base 10 of X.}
219 function Log10(X
: Single): Single;
220 { Returns largest integer <= Val (for 5.9 returns 5).}
221 function Floor(Value
: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
222 { Returns smallest integer >= Val (for 5.1 returns 6).}
223 function Ceil(Value
: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
224 { Returns lesser of two integer numbers.}
225 function Min(A
, B
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
226 { Returns lesser of two float numbers.}
227 function MinFloat(A
, B
: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
228 { Returns greater of two integer numbers.}
229 function Max(A
, B
: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
230 { Returns greater of two float numbers.}
231 function MaxFloat(A
, B
: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
232 { Returns result from multiplying Number by Numerator and then dividing by Denominator.
233 Denominator must be greater than 0.}
234 function MulDiv(Number
, Numerator
, Denominator
: Word): Word; {$IFDEF USE_INLINE}inline;{$ENDIF}
236 { Switches Boolean value.}
237 procedure Switch(var Value
: Boolean); {$IFDEF USE_INLINE}inline;{$ENDIF}
238 { If Condition is True then TruePart is retured, otherwise
239 FalsePart is returned.}
240 function Iff(Condition
: Boolean; TruePart
, FalsePart
: LongInt): LongInt; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
241 { If Condition is True then TruePart is retured, otherwise
242 FalsePart is returned.}
243 function IffUnsigned(Condition
: Boolean; TruePart
, FalsePart
: LongWord): LongWord; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
244 { If Condition is True then TruePart is retured, otherwise
245 FalsePart is returned.}
246 function Iff(Condition
, TruePart
, FalsePart
: Boolean): Boolean; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
247 { If Condition is True then TruePart is retured, otherwise
248 FalsePart is returned.}
249 function Iff(Condition
: Boolean; const TruePart
, FalsePart
: string): string; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
250 { If Condition is True then TruePart is retured, otherwise
251 FalsePart is returned.}
252 function Iff(Condition
: Boolean; TruePart
, FalsePart
: Char): Char; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
253 { If Condition is True then TruePart is retured, otherwise
254 FalsePart is returned.}
255 function Iff(Condition
: Boolean; TruePart
, FalsePart
: Pointer): Pointer; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
256 { If Condition is True then TruePart is retured, otherwise
257 FalsePart is returned.}
258 function Iff(Condition
: Boolean; const TruePart
, FalsePart
: Int64): Int64; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
259 { If Condition is True then TruePart is retured, otherwise
260 FalsePart is returned.}
261 function IffFloat(Condition
: Boolean; TruePart
, FalsePart
: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
262 { Swaps two Boolean values}
263 procedure SwapValues(var A
, B
: Boolean); overload
;
264 { Swaps two Byte values}
265 procedure SwapValues(var A
, B
: Byte); overload
;
266 { Swaps two Word values}
267 procedure SwapValues(var A
, B
: Word); overload
;
268 { Swaps two LongInt values}
269 procedure SwapValues(var A
, B
: LongInt); overload
;
270 { Swaps two Single values}
271 procedure SwapValues(var A
, B
: Single); overload
;
272 { Swaps two LongInt values if necessary to ensure that Min <= Max.}
273 procedure SwapMin(var Min
, Max
: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
274 { This function returns True if running on little endian machine.}
275 function IsLittleEndian
: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
276 { Swaps byte order of Word value.}
277 function SwapEndianWord(Value
: Word): Word; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
278 { Swaps byte order of multiple Word values.}
279 procedure SwapEndianWord(P
: PWordArray
; Count
: LongInt); overload
;
280 { Swaps byte order of LongWord value.}
281 function SwapEndianLongWord(Value
: LongWord): LongWord; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
282 { Swaps byte order of multiple LongWord values.}
283 procedure SwapEndianLongWord(P
: PLongWord; Count
: LongInt); overload
;
285 { Calculates CRC32 for the given data.}
286 procedure CalcCrc32(var Crc
: LongWord; Data
: Pointer; Size
: LongInt);
287 { Fills given memory with given Byte value. Size is size of buffer in bytes.}
288 procedure FillMemoryByte(Data
: Pointer; Size
: LongInt; Value
: Byte);
289 { Fills given memory with given Word value. Size is size of buffer in bytes.}
290 procedure FillMemoryWord(Data
: Pointer; Size
: LongInt; Value
: Word);
291 { Fills given memory with given LongWord value. Size is size of buffer in bytes.}
292 procedure FillMemoryLongWord(Data
: Pointer; Size
: LongInt; Value
: LongWord);
293 { Fills given memory zeroes.}
294 {$EXTERNALSYM ZeroMemory} // Conflicts with WinAPI ZeroMemory in C++ Builder
295 procedure ZeroMemory(Data
: Pointer; Size
: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
297 { Returns how many mipmap levels can be created for image of given size.}
298 function GetNumMipMapLevels(Width
, Height
: LongInt): LongInt;
299 { Returns total number of levels of volume texture with given depth and
300 mipmap count (this is not depth * mipmaps!).}
301 function GetVolumeLevelCount(Depth
, MipMaps
: LongInt): LongInt;
302 { Returns rectangle (X, Y, X + Width, Y + Height).}
303 function BoundsToRect(X
, Y
, Width
, Height
: LongInt): TRect
; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
304 { Returns rectangle (R.Left, R.Top, R.Left + R.Right, R.Top + R.Bottom).}
305 function BoundsToRect(const R
: TRect
): TRect
; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
306 { Returns rectangle (R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top).}
307 function RectToBounds(const R
: TRect
): TRect
; overload
; {$IFDEF USE_INLINE}inline;{$ENDIF}
308 { Clips given bounds to Clip rectangle.}
309 procedure ClipRectBounds(var X
, Y
, Width
, Height
: LongInt; const Clip
: TRect
);
310 { Clips given source bounds and dest position. It is used by various CopyRect
311 functions that copy rect from one image to another. It handles clipping the same way
312 as Win32 BitBlt function. }
313 procedure ClipCopyBounds(var SrcX
, SrcY
, Width
, Height
, DstX
, DstY
: LongInt;
314 SrcImageWidth
, SrcImageHeight
: LongInt; const DstClip
: TRect
);
315 { Clips given source bounds and dest bounds. It is used by various StretchRect
316 functions that stretch rectangle of pixels from one image to another.
317 It handles clipping the same way as Win32 StretchBlt function. }
318 procedure ClipStretchBounds(var SrcX
, SrcY
, SrcWidth
, SrcHeight
, DstX
, DstY
,
319 DstWidth
, DstHeight
: LongInt; SrcImageWidth
, SrcImageHeight
: LongInt; const DstClip
: TRect
);
320 { Scales one rectangle to fit into another. Proportions are preserved so
321 it could be used for 'Stretch To Fit Window' image drawing for instance.}
322 function ScaleRectToRect(const SourceRect
, TargetRect
: TRect
): TRect
;
323 { Scales given size to fit into max size while keeping the original ascpect ration.
324 Useful for calculating thumbnail dimensions etc.}
325 function ScaleSizeToFit(const CurrentSize
, MaxSize
: TSize
): TSize
;
326 { Returns width of given rect. Part of RTL in newer Delphi.}
327 function RectWidth(const Rect
: TRect
): Integer;
328 { Returns height of given rect. Part of RTL in newer Delphi.}
329 function RectHeight(const Rect
: TRect
): Integer;
330 { Returns True if R1 fits into R2.}
331 function RectInRect(const R1
, R2
: TRect
): Boolean;
332 { Returns True if R1 and R2 intersects.}
333 function RectIntersects(const R1
, R2
: TRect
): Boolean;
335 { Converts pixel size in micrometers to corrensponding DPI.}
336 function PixelSizeToDpi(SizeInMicroMeters
: Single): Single;
337 { Converts DPI to corrensponding pixel size in micrometers.}
338 function DpiToPixelSize(Dpi
: Single): Single;
340 function FloatRect(ALeft
, ATop
, ARight
, ABottom
: Single): TFloatRect
;
341 function FloatRectWidth(const R
: TFloatRect
): Single;
342 function FloatRectHeight(const R
: TFloatRect
): Single;
344 { Formats given message for usage in Exception.Create(..). Use only
345 in except block - returned message contains message of last raised exception.}
346 function FormatExceptMsg(const Msg
: string; const Args
: array of const): string;
347 { Outputs debug message - shows message dialog in Windows and writes to console
349 procedure DebugMsg(const Msg
: string; const Args
: array of const);
354 {$IF Defined(MSWINDOWS)}
356 {$ELSEIF Defined(FPC)}
362 {$ELSEIF Defined(DELPHI)}
367 FloatFormatSettings
: TFormatSettings
;
369 constructor ENotImplemented
.Create
;
371 inherited Create('Not implemented');
374 procedure FreeAndNil(var Obj
);
378 Temp
:= TObject(Obj
);
383 procedure FreeMemNil(var P
);
389 procedure FreeMem(P
: Pointer);
395 function GetExceptObject
: Exception
;
397 Result
:= Exception(ExceptObject
);
400 {$IF Defined(MSWINDOWS)}
402 PerfFrequency
: Int64;
403 InvPerfFrequency
: Single;
405 function GetTimeMicroseconds
: Int64;
409 QueryPerformanceCounter(Time
);
410 Result
:= Round(1000000 * InvPerfFrequency
* Time
);
412 {$ELSEIF Defined(DELPHI)}
413 function GetTimeMicroseconds
: Int64;
417 Posix
.SysTime
.GetTimeOfDay(Time
, nil);
418 Result
:= Int64(Time
.tv_sec
) * 1000000 + Time
.tv_usec
;
420 {$ELSEIF Defined(FPC) and Defined(UNIX)}
421 function GetTimeMicroseconds
: Int64;
425 fpGetTimeOfDay(@TimeVal
, nil);
426 Result
:= Int64(TimeVal
.tv_sec
) * 1000000 + TimeVal
.tv_usec
;
429 function GetTimeMicroseconds
: Int64;
431 {$WARNING GetTimeMicroseconds stub!}
436 function GetTimeMilliseconds
: Int64;
438 Result
:= GetTimeMicroseconds
div 1000;
441 function GetFileExt(const FileName
: string): string;
443 Result
:= ExtractFileExt(FileName
);
444 if Length(Result
) > 1 then
445 Delete(Result
, 1, 1);
448 function GetAppExe
: string;
449 {$IF Defined(MSWINDOWS)}
451 FileName
: array[0..MAX_PATH
] of Char;
453 SetString(Result
, FileName
,
454 Windows
.GetModuleFileName(MainInstance
, FileName
, SizeOf(FileName
)));
455 {$ELSEIF Defined(DELPHI)} // Delphi non Win targets
457 FileName
: array[0..1024] of Char;
459 SetString(Result
, FileName
,
460 System
.GetModuleFileName(MainInstance
, FileName
, SizeOf(FileName
)));
463 Result
:= ParamStr(0);
467 function GetAppDir
: string;
469 Result
:= ExtractFileDir(GetAppExe
);
472 function GetFileName(const FileName
: string): string;
476 I
:= LastDelimiter('\/' + DriveDelim
, FileName
);
477 Result
:= Copy(FileName
, I
+ 1, MaxInt
);
480 function GetFileDir(const FileName
: string): string;
482 Delims
= '\/' + DriveDelim
;
486 I
:= LastDelimiter(Delims
, Filename
);
488 ((FileName
[I
] = Delims
[1]) or (FileName
[I
] = Delims
[2])) and
489 (not IsDelimiter(Delims
, FileName
, I
- 1)) then Dec(I
);
490 Result
:= Copy(FileName
, 1, I
);
493 function StrMaskMatch(const Subject
, Mask
: string; CaseSensitive
: Boolean): Boolean;
495 MaskLen
, KeyLen
: LongInt;
497 function CharMatch(A
, B
: Char): Boolean;
499 if CaseSensitive
then
502 Result
:= AnsiUpperCase (A
) = AnsiUpperCase (B
);
505 function MatchAt(MaskPos
, KeyPos
: LongInt): Boolean;
507 while (MaskPos
<= MaskLen
) and (KeyPos
<= KeyLen
) do
509 case Mask
[MaskPos
] of
517 while (MaskPos
<= MaskLen
) and (Mask
[MaskPos
] = '*') do
519 if MaskPos
> MaskLen
then
525 if MatchAt(MaskPos
, KeyPos
) then
531 until KeyPos
> KeyLen
;
536 if not CharMatch(Mask
[MaskPos
], Subject
[KeyPos
]) then
549 while (MaskPos
<= MaskLen
) and (AnsiChar(Mask
[MaskPos
]) in ['?', '*']) do
551 if (MaskPos
<= MaskLen
) or (KeyPos
<= KeyLen
) then
561 MaskLen
:= Length(Mask
);
562 KeyLen
:= Length(Subject
);
568 Result
:= MatchAt(1, 1);
571 function BuildFileList(Path
: string; Attr
: LongInt;
572 Files
: TStrings
; Options
: TFileListOptions
): Boolean;
576 Folders
: TStringList
;
577 CurrentItem
: LongInt;
581 procedure BuildFolderList
;
583 FindInfo
: TSearchRec
;
586 Counter
:= Folders
.Count
- 1;
588 while CurrentItem
<= Counter
do
590 // Searching for subfolders
591 Rslt
:= SysUtils
.FindFirst(Folders
[CurrentItem
] + '*', faDirectory
, FindInfo
);
595 if (FindInfo
.Name
<> '.') and (FindInfo
.Name
<> '..') and
596 (FindInfo
.Attr
and faDirectory
= faDirectory
) then
597 Folders
.Add(Folders
[CurrentItem
] + FindInfo
.Name
+ PathDelim
);
598 Rslt
:= SysUtils
.FindNext(FindInfo
);
601 SysUtils
.FindClose(FindInfo
);
603 Counter
:= Folders
.Count
- 1;
608 procedure FillFileList(CurrentCounter
: LongInt);
610 FindInfo
: TSearchRec
;
612 CurrentFolder
: string;
614 CurrentFolder
:= Folders
[CurrentCounter
];
615 Res
:= SysUtils
.FindFirst(CurrentFolder
+ FileMask
, LocAttr
, FindInfo
);
616 if flRelNames
in Options
then
617 CurrentFolder
:= ExtractRelativePath(RootDir
, CurrentFolder
);
621 if (FindInfo
.Name
<> '.') and (FindInfo
.Name
<> '..') then
623 if (flFullNames
in Options
) or (flRelNames
in Options
) then
624 Files
.Add(CurrentFolder
+ FindInfo
.Name
)
626 Files
.Add(FindInfo
.Name
);
628 Res
:= SysUtils
.FindNext(FindInfo
);
631 SysUtils
.FindClose(FindInfo
);
636 FileMask
:= ExtractFileName(Path
);
637 RootDir
:= ExtractFilePath(Path
);
638 Folders
:= TStringList
.Create
;
639 Folders
.Add(RootDir
);
642 {$WARN SYMBOL_PLATFORM OFF}
644 if Attr
= faAnyFile
then
645 LocAttr
:= faSysFile
or faHidden
or faArchive
or faReadOnly
649 {$WARN SYMBOL_PLATFORM ON}
651 // Here's the recursive search for nested folders
652 if flRecursive
in Options
then
654 if Attr
<> faDirectory
then
655 for Counter
:= 0 to Folders
.Count
- 1 do
656 FillFileList(Counter
)
658 Files
.AddStrings(Folders
);
663 function PosEx(const SubStr
, S
: string; Offset
: LongInt = 1): LongInt;
666 Len
, LenSubStr
: LongInt;
669 LenSubStr
:= Length(SubStr
);
670 Len
:= Length(S
) - LenSubStr
+ 1;
673 if S
[I
] = SubStr
[1] then
676 while (X
< LenSubStr
) and (S
[I
+ X
] = SubStr
[X
+ 1]) do
678 if (X
= LenSubStr
) then
689 function PosNoCase(const SubStr
, S
: string; Offset
: LongInt): LongInt;
691 Result
:= PosEx(AnsiLowerCase(SubStr
), AnsiLowerCase(S
), Offset
);
694 function StrToken(var S
: string; Sep
: Char): string;
701 Result
:= Copy(S
, 1, I
- 1);
711 function StrTokenEnd(var S
: string; Sep
: Char): string;
720 I
:= PosEx(Sep
, S
, J
+ 1);
724 Result
:= Copy(S
, J
+ 1, MaxInt
);
725 Delete(S
, J
, MaxInt
);
734 procedure StrTokensToList(const S
: string; Sep
: Char; Tokens
: TStrings
);
742 Token
:= StrToken(Str
, Sep
);
747 function IntToStrFmt(const I
: Int64): string;
749 Result
:= Format('%.0n', [I
* 1.0]);
752 function FloatToStrFmt(const F
: Double; Precision
: Integer): string;
754 Result
:= Format('%.' + IntToStr(Precision
) + 'n', [F
]);
757 function GetFormatSettingsForFloats
: TFormatSettings
;
759 Result
:= FloatFormatSettings
;
762 function ContainsAnySubStr(const S
: string; const SubStrs
: array of string): Boolean;
767 for I
:= 0 to High(SubStrs
) do
769 Result
:= Pos(SubStrs
[I
], S
) > 0;
775 function SubString(const S
: string; IdxStart
, IdxEnd
: Integer): string;
777 Result
:= Copy(S
, IdxStart
, IdxEnd
- IdxStart
);
780 function ClampInt(Number
: LongInt; Min
, Max
: LongInt): LongInt;
785 else if Result
> Max
then
789 function ClampFloat(Number
: Single; Min
, Max
: Single): Single;
794 else if Result
> Max
then
798 function ClampToByte(Value
: LongInt): LongInt;
803 else if Result
< 0 then
807 function ClampToWord(Value
: LongInt): LongInt;
810 if Result
> 65535 then
812 else if Result
< 0 then
816 function IsPow2(Num
: LongInt): Boolean;
818 Result
:= (Num
and -Num
) = Num
;
821 function NextPow2(Num
: LongInt): LongInt;
823 Result
:= Num
and -Num
;
824 while Result
< Num
do
825 Result
:= Result
shl 1;
828 function Pow2Int(Exponent
: LongInt): LongInt;
830 Result
:= 1 shl Exponent
;
833 function Power(const Base
, Exponent
: Single): Single;
835 if Exponent
= 0.0 then
837 else if (Base
= 0.0) and (Exponent
> 0.0) then
840 Result
:= Exp(Exponent
* Ln(Base
));
843 function Log2Int(X
: LongInt): LongInt;
863 131072: Result
:= 17;
864 262144: Result
:= 18;
865 524288: Result
:= 19;
866 1048576: Result
:= 20;
867 2097152: Result
:= 21;
868 4194304: Result
:= 22;
869 8388608: Result
:= 23;
870 16777216: Result
:= 24;
871 33554432: Result
:= 25;
872 67108864: Result
:= 26;
873 134217728: Result
:= 27;
874 268435456: Result
:= 28;
875 536870912: Result
:= 29;
876 1073741824: Result
:= 30;
882 function Log2(X
: Single): Single;
892 Ln2
: Single = 0.6931471;
894 Result
:= Ln(X
) / Ln2
;
898 function Log10(X
: Single): Single;
908 Ln10
: Single = 2.30258509299405;
910 Result
:= Ln(X
) / Ln10
;
914 function Floor(Value
: Single): LongInt;
916 Result
:= Trunc(Value
);
917 if Frac(Value
) < 0.0 then
921 function Ceil(Value
: Single): LongInt;
923 Result
:= Trunc(Value
);
924 if Frac(Value
) > 0.0 then
928 procedure Switch(var Value
: Boolean);
933 function Iff(Condition
: Boolean; TruePart
, FalsePart
: LongInt): LongInt;
941 function IffUnsigned(Condition
: Boolean; TruePart
, FalsePart
: LongWord): LongWord;
949 function Iff(Condition
, TruePart
, FalsePart
: Boolean): Boolean;
957 function Iff(Condition
: Boolean; const TruePart
, FalsePart
: string): string;
965 function Iff(Condition
: Boolean; TruePart
, FalsePart
: Char): Char;
973 function Iff(Condition
: Boolean; TruePart
, FalsePart
: Pointer): Pointer;
981 function Iff(Condition
: Boolean; const TruePart
, FalsePart
: Int64): Int64;
989 function IffFloat(Condition
: Boolean; TruePart
, FalsePart
: Single): Single;
997 procedure SwapValues(var A
, B
: Boolean);
1006 procedure SwapValues(var A
, B
: Byte);
1015 procedure SwapValues(var A
, B
: Word);
1024 procedure SwapValues(var A
, B
: LongInt);
1033 procedure SwapValues(var A
, B
: Single);
1042 procedure SwapMin(var Min
, Max
: LongInt);
1054 function Min(A
, B
: LongInt): LongInt;
1062 function MinFloat(A
, B
: Single): Single;
1070 function Max(A
, B
: LongInt): LongInt;
1078 function MaxFloat(A
, B
: Single): Single;
1086 function MulDiv(Number
, Numerator
, Denominator
: Word): Word;
1087 {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
1094 Result
:= Number
* Numerator
div Denominator
;
1098 function IsLittleEndian
: Boolean;
1103 Result
:= PByte(@W
)^ = $FF;
1106 function SwapEndianWord(Value
: Word): Word;
1107 {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
1113 TWordRec(Result
).Low
:= TWordRec(Value
).High
;
1114 TWordRec(Result
).High
:= TWordRec(Value
).Low
;
1118 procedure SwapEndianWord(P
: PWordArray
; Count
: LongInt);
1134 for I
:= 0 to Count
- 1 do
1137 TWordRec(P
[I
]).Low
:= TWordRec(Temp
).High
;
1138 TWordRec(P
[I
]).High
:= TWordRec(Temp
).Low
;
1143 function SwapEndianLongWord(Value
: LongWord): LongWord;
1144 {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
1150 TLongWordRec(Result
).Bytes
[0] := TLongWordRec(Value
).Bytes
[3];
1151 TLongWordRec(Result
).Bytes
[1] := TLongWordRec(Value
).Bytes
[2];
1152 TLongWordRec(Result
).Bytes
[2] := TLongWordRec(Value
).Bytes
[1];
1153 TLongWordRec(Result
).Bytes
[3] := TLongWordRec(Value
).Bytes
[0];
1157 procedure SwapEndianLongWord(P
: PLongWord; Count
: LongInt);
1173 for I
:= 0 to Count
- 1 do
1175 Temp
:= PLongWordArray(P
)[I
];
1176 TLongWordRec(PLongWordArray(P
)[I
]).Bytes
[0] := TLongWordRec(Temp
).Bytes
[3];
1177 TLongWordRec(PLongWordArray(P
)[I
]).Bytes
[1] := TLongWordRec(Temp
).Bytes
[2];
1178 TLongWordRec(PLongWordArray(P
)[I
]).Bytes
[2] := TLongWordRec(Temp
).Bytes
[1];
1179 TLongWordRec(PLongWordArray(P
)[I
]).Bytes
[3] := TLongWordRec(Temp
).Bytes
[0];
1185 TCrcTable
= array[Byte] of LongWord;
1187 CrcTable
: TCrcTable
;
1189 procedure InitCrcTable
;
1191 Polynom
= $EDB88320;
1196 for I
:= 0 to 255 do
1201 if (C
and $01) <> 0 then
1202 C
:= Polynom
xor (C
shr 1)
1210 procedure CalcCrc32(var Crc
: LongWord; Data
: Pointer; Size
: LongInt);
1216 for I
:= 0 to Size
- 1 do
1218 Crc
:= (Crc
shr 8) xor CrcTable
[B
^ xor Byte(Crc
)];
1223 procedure FillMemoryByte(Data
: Pointer; Size
: LongInt; Value
: Byte);
1245 FillChar(Data
^, Size
, Value
);
1249 procedure FillMemoryWord(Data
: Pointer; Size
: LongInt; Value
: Word);
1283 V
:= Value
* $10000 + Value
;
1284 for I
:= 0 to Size
div 4 - 1 do
1285 PLongWordArray(Data
)[I
] := V
;
1287 1: PByteArray(Data
)[Size
- 1] := Lo(Value
);
1288 2: PWordArray(Data
)[Size
div 2] := Value
;
1291 PWordArray(Data
)[Size
div 2 - 1] := Value
;
1292 PByteArray(Data
)[Size
- 1] := Lo(Value
);
1298 procedure FillMemoryLongWord(Data
: Pointer; Size
: LongInt; Value
: LongWord);
1329 for I
:= 0 to Size
div 4 - 1 do
1330 PLongWordArray(Data
)[I
] := Value
;
1332 1: PByteArray(Data
)[Size
- 1] := TLongWordRec(Value
).Bytes
[0];
1333 2: PWordArray(Data
)[Size
div 2] := TLongWordRec(Value
).Words
[0];
1336 PWordArray(Data
)[Size
div 2 - 1] := TLongWordRec(Value
).Words
[0];
1337 PByteArray(Data
)[Size
- 1] := TLongWordRec(Value
).Bytes
[0];
1343 procedure ZeroMemory(Data
: Pointer; Size
: Integer);
1345 FillMemoryByte(Data
, Size
, 0);
1348 function GetNumMipMapLevels(Width
, Height
: LongInt): LongInt;
1351 if (Width
> 0) and (Height
> 0) then
1354 while (Width
<> 1) or (Height
<> 1) do
1356 Width
:= Width
div 2;
1357 Height
:= Height
div 2;
1358 if Width
< 1 then Width
:= 1;
1359 if Height
< 1 then Height
:= 1;
1365 function GetVolumeLevelCount(Depth
, MipMaps
: LongInt): LongInt;
1370 for I
:= 1 to MipMaps
- 1 do
1371 Inc(Result
, ClampInt(Depth
shr I
, 1, Depth
));
1374 function BoundsToRect(X
, Y
, Width
, Height
: LongInt): TRect
;
1378 Result
.Right
:= X
+ Width
;
1379 Result
.Bottom
:= Y
+ Height
;
1382 function BoundsToRect(const R
: TRect
): TRect
;
1384 Result
.Left
:= R
.Left
;
1385 Result
.Top
:= R
.Top
;
1386 Result
.Right
:= R
.Left
+ R
.Right
;
1387 Result
.Bottom
:= R
.Top
+ R
.Bottom
;
1390 function RectToBounds(const R
: TRect
): TRect
;
1392 Result
.Left
:= R
.Left
;
1393 Result
.Top
:= R
.Top
;
1394 Result
.Right
:= R
.Right
- R
.Left
;
1395 Result
.Bottom
:= R
.Bottom
- R
.Top
;
1398 procedure ClipRectBounds(var X
, Y
, Width
, Height
: LongInt; const Clip
: TRect
);
1400 procedure ClipDim(var AStart
, ALength
: LongInt; ClipMin
, ClipMax
: LongInt);
1402 if AStart
< ClipMin
then
1404 ALength
:= ALength
- (ClipMin
- AStart
);
1407 if AStart
+ ALength
> ClipMax
then ALength
:= Max(0, ClipMax
- AStart
);
1411 ClipDim(X
, Width
, Clip
.Left
, Clip
.Right
);
1412 ClipDim(Y
, Height
, Clip
.Top
, Clip
.Bottom
);
1415 procedure ClipCopyBounds(var SrcX
, SrcY
, Width
, Height
, DstX
, DstY
: LongInt; SrcImageWidth
, SrcImageHeight
: LongInt; const DstClip
: TRect
);
1417 procedure ClipDim(var SrcPos
, DstPos
, Size
: LongInt; SrcClipMax
,
1418 DstClipMin
, DstClipMax
: LongInt);
1423 OldDstPos
:= Iff(DstPos
< 0, DstPos
, 0);
1424 if DstPos
< DstClipMin
then
1426 Diff
:= DstClipMin
- DstPos
;
1427 Size
:= Size
- Diff
;
1428 SrcPos
:= SrcPos
+ Diff
;
1429 DstPos
:= DstClipMin
;
1433 Size
:= Size
+ SrcPos
- OldDstPos
;
1434 DstPos
:= DstPos
- SrcPos
+ OldDstPos
;
1437 if SrcPos
+ Size
> SrcClipMax
then Size
:= SrcClipMax
- SrcPos
;
1438 if DstPos
+ Size
> DstClipMax
then Size
:= DstClipMax
- DstPos
;
1442 ClipDim(SrcX
, DstX
, Width
, SrcImageWidth
, DstClip
.Left
, DstClip
.Right
);
1443 ClipDim(SrcY
, DstY
, Height
, SrcImageHeight
, DstClip
.Top
, DstClip
.Bottom
);
1446 procedure ClipStretchBounds(var SrcX
, SrcY
, SrcWidth
, SrcHeight
, DstX
, DstY
,
1447 DstWidth
, DstHeight
: LongInt; SrcImageWidth
, SrcImageHeight
: LongInt; const DstClip
: TRect
);
1449 procedure ClipDim(var SrcPos
, DstPos
, SrcSize
, DstSize
: LongInt; SrcClipMax
,
1450 DstClipMin
, DstClipMax
: LongInt);
1456 Scale
:= DstSize
/ SrcSize
;
1457 if DstPos
< DstClipMin
then
1459 Diff
:= DstClipMin
- DstPos
;
1460 DstSize
:= DstSize
- Diff
;
1461 SrcPos
:= SrcPos
+ Round(Diff
/ Scale
);
1462 SrcSize
:= SrcSize
- Round(Diff
/ Scale
);
1463 DstPos
:= DstClipMin
;
1467 SrcSize
:= SrcSize
+ SrcPos
;
1468 DstPos
:= DstPos
- Round(SrcPos
* Scale
);
1469 DstSize
:= DstSize
+ Round(SrcPos
* Scale
);
1472 if SrcPos
+ SrcSize
> SrcClipMax
then
1475 SrcSize
:= SrcClipMax
- SrcPos
;
1476 DstSize
:= Round(DstSize
* (SrcSize
/ OldSize
));
1478 if DstPos
+ DstSize
> DstClipMax
then
1481 DstSize
:= DstClipMax
- DstPos
;
1482 SrcSize
:= Round(SrcSize
* (DstSize
/ OldSize
));
1487 ClipDim(SrcX
, DstX
, SrcWidth
, DstWidth
, SrcImageWidth
, DstClip
.Left
, DstClip
.Right
);
1488 ClipDim(SrcY
, DstY
, SrcHeight
, DstHeight
, SrcImageHeight
, DstClip
.Top
, DstClip
.Bottom
);
1491 function ScaleRectToRect(const SourceRect
, TargetRect
: TRect
): TRect
;
1493 SourceWidth
: LongInt;
1494 SourceHeight
: LongInt;
1495 TargetWidth
: LongInt;
1496 TargetHeight
: LongInt;
1497 ScaledWidth
: LongInt;
1498 ScaledHeight
: LongInt;
1500 SourceWidth
:= SourceRect
.Right
- SourceRect
.Left
;
1501 SourceHeight
:= SourceRect
.Bottom
- SourceRect
.Top
;
1502 TargetWidth
:= TargetRect
.Right
- TargetRect
.Left
;
1503 TargetHeight
:= TargetRect
.Bottom
- TargetRect
.Top
;
1505 if SourceWidth
* TargetHeight
< SourceHeight
* TargetWidth
then
1507 ScaledWidth
:= (SourceWidth
* TargetHeight
) div SourceHeight
;
1508 Result
:= BoundsToRect(TargetRect
.Left
+ ((TargetWidth
- ScaledWidth
) div 2),
1509 TargetRect
.Top
, ScaledWidth
, TargetHeight
);
1513 ScaledHeight
:= (SourceHeight
* TargetWidth
) div SourceWidth
;
1514 Result
:= BoundsToRect(TargetRect
.Left
, TargetRect
.Top
+ ((TargetHeight
- ScaledHeight
) div 2),
1515 TargetWidth
, ScaledHeight
);
1519 function ScaleSizeToFit(const CurrentSize
, MaxSize
: Types
.TSize
): Types
.TSize
;
1521 SR
, TR
, ScaledRect
: TRect
;
1523 SR
:= Types
.Rect(0, 0, CurrentSize
.CX
, CurrentSize
.CY
);
1524 TR
:= Types
.Rect(0, 0, MaxSize
.CX
, MaxSize
.CY
);
1525 ScaledRect
:= ScaleRectToRect(SR
, TR
);
1526 Result
.CX
:= ScaledRect
.Right
- ScaledRect
.Left
;
1527 Result
.CY
:= ScaledRect
.Bottom
- ScaledRect
.Top
;
1530 function RectWidth(const Rect
: TRect
): Integer;
1532 Result
:= Rect
.Right
- Rect
.Left
;
1535 function RectHeight(const Rect
: TRect
): Integer;
1537 Result
:= Rect
.Bottom
- Rect
.Top
;
1540 function RectInRect(const R1
, R2
: TRect
): Boolean;
1543 (R1
.Left
>= R2
.Left
) and
1544 (R1
.Top
>= R2
.Top
) and
1545 (R1
.Right
<= R2
.Right
) and
1546 (R1
.Bottom
<= R2
.Bottom
);
1549 function RectIntersects(const R1
, R2
: TRect
): Boolean;
1552 not (R1
.Left
> R2
.Right
) and
1553 not (R1
.Top
> R2
.Bottom
) and
1554 not (R1
.Right
< R2
.Left
) and
1555 not (R1
.Bottom
< R2
.Top
);
1558 function PixelSizeToDpi(SizeInMicroMeters
: Single): Single;
1560 Result
:= 25400 / SizeInMicroMeters
;
1563 function DpiToPixelSize(Dpi
: Single): Single;
1565 Result
:= 1e03
/ (Dpi
/ 25.4);
1568 function FloatRect(ALeft
, ATop
, ARight
, ABottom
: Single): TFloatRect
;
1579 function FloatRectWidth(const R
: TFloatRect
): Single;
1581 Result
:= R
.Right
- R
.Left
;
1584 function FloatRectHeight(const R
: TFloatRect
): Single;
1586 Result
:= R
.Bottom
- R
.Top
;
1589 function FormatExceptMsg(const Msg
: string; const Args
: array of const): string;
1591 Result
:= Format(Msg
+ SLineBreak
+ 'Message: ' + GetExceptObject
.Message, Args
);
1594 procedure DebugMsg(const Msg
: string; const Args
: array of const);
1598 FmtMsg
:= Format(Msg
, Args
);
1601 WriteLn('DebugMsg: ' + FmtMsg
)
1603 MessageBox(GetActiveWindow
, PChar(FmtMsg
), 'DebugMsg', MB_OK
);
1606 WriteLn('DebugMsg: ' + FmtMsg
);
1609 WriteLn('DebugMsg: ' + FmtMsg
);
1616 QueryPerformanceFrequency(PerfFrequency
);
1617 InvPerfFrequency
:= 1.0 / PerfFrequency
;
1620 {$IF Defined(DELPHI)}
1621 {$IF CompilerVersion >= 23}
1622 FloatFormatSettings
:= TFormatSettings
.Create('en-US');
1624 GetLocaleFormatSettings(1033, FloatFormatSettings
);
1627 FloatFormatSettings
:= DefaultFormatSettings
;
1628 FloatFormatSettings
.DecimalSeparator
:= '.';
1634 -- TODOS ----------------------------------------------------
1637 -- 0.77.1 ----------------------------------------------------
1638 - Added GetFileName, GetFileDir, RectWidth, RectHeight function.
1639 - Added ScaleSizeToFit function.
1640 - Added ZeroMemory and SwapValues for Booleans.
1641 - Added Substring function.
1642 - Renamed MatchFileNameMask to StrMaskMatch (it's for general use not
1644 - Delphi XE2 new targets (Win64, OSX32) compatibility changes.
1645 - Added GetFormatSettingsForFloats function.
1647 -- 0.26.5 Changes/Bug Fixes -----------------------------------
1648 - Added Log10 function.
1649 - Added TFloatRect type and helper functions FloatRect, FloatRectWidth,
1651 - Added string function ContainsAnySubStr.
1652 - Added functions PixelSizeToDpi, DpiToPixelSize.
1654 -- 0.26.1 Changes/Bug Fixes -----------------------------------
1655 - Some formatting changes.
1656 - Changed some string functions to work with localized strings.
1657 - ASM version of PosEx had bugs, removed it.
1658 - Added StrTokensToList function.
1660 -- 0.25.0 Changes/Bug Fixes -----------------------------------
1661 - Fixed error in ClipCopyBounds which was causing ... bad clipping!
1663 -- 0.24.3 Changes/Bug Fixes -----------------------------------
1664 - Added GetTimeMilliseconds function.
1665 - Added IntToStrFmt and FloatToStrFmt helper functions.
1667 -- 0.23 Changes/Bug Fixes -----------------------------------
1668 - Added RectInRect and RectIntersects functions
1669 - Added some string utils: StrToken, StrTokenEnd, PosEx, PosNoCase.
1670 - Moved BuildFileList here from DemoUtils.
1672 -- 0.21 Changes/Bug Fixes -----------------------------------
1673 - Moved GetVolumeLevelCount from ImagingDds here.
1674 - Renamed FillMemory to FillMemoryByte to avoid name collision in C++ Builder.
1675 - Added Iff function for Char, Pointer, and Int64 types.
1676 - Added IsLittleEndian function.
1677 - Added array types for TWordRec, TLongWordRec, and TInt64Rec.
1678 - Added MatchFileNameMask function.
1680 -- 0.19 Changes/Bug Fixes -----------------------------------
1681 - added ScaleRectToRect (thanks to Paul Michell)
1682 - added BoundsToRect, ClipBounds, ClipCopyBounds, ClipStretchBounds functions
1683 - added MulDiv function
1684 - FreeAndNil is not inline anymore - caused AV in one program
1686 -- 0.17 Changes/Bug Fixes -----------------------------------
1688 - GetAppExe didn't return absolute path in FreeBSD, fixed
1689 - added debug message output
1690 - fixed Unix compatibility issues (thanks to Ales Katona).
1691 Imaging now compiles in FreeBSD and maybe in other Unixes as well.
1693 -- 0.15 Changes/Bug Fixes -----------------------------------
1694 - added some new utility functions
1696 -- 0.13 Changes/Bug Fixes -----------------------------------
1697 - added many new utility functions
1698 - minor change in SwapEndian to avoid range check error