ImagingUtility.pas 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. 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
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains utility functions and types for Imaging library.}
  25. unit ImagingUtility;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, Types;
  30. type
  31. TByteArray = array[0..MaxInt - 1] of Byte;
  32. PByteArray = ^TByteArray;
  33. TWordArray = array[0..MaxInt div 2 - 1] of Word;
  34. PWordArray = ^TWordArray;
  35. TLongIntArray = array[0..MaxInt div 4 - 1] of LongInt;
  36. PLongIntArray = ^TLongIntArray;
  37. TLongWordArray = array[0..MaxInt div 4 - 1] of LongWord;
  38. PLongWordArray = ^TLongWordArray;
  39. TInt64Array = array[0..MaxInt div 8 - 1] of Int64;
  40. PInt64Array = ^TInt64Array;
  41. TSingleArray = array[0..MaxInt div 4 - 1] of Single;
  42. PSingleArray = ^TSingleArray;
  43. TBooleanArray = array[0..MaxInt - 1] of Boolean;
  44. PBooleanArray = ^TBooleanArray;
  45. TWordRec = packed record
  46. case Integer of
  47. 0: (WordValue: Word);
  48. 1: (Low, High: Byte);
  49. end;
  50. PWordRec = ^TWordRec;
  51. TWordRecArray = array[0..MaxInt div 2 - 1] of TWordRec;
  52. PWordRecArray = ^TWordRecArray;
  53. TLongWordRec = packed record
  54. case Integer of
  55. 0: (LongWordValue: LongWord);
  56. 1: (Low, High: Word);
  57. { Array variants - Index 0 means lowest significant byte (word, ...).}
  58. 2: (Words: array[0..1] of Word);
  59. 3: (Bytes: array[0..3] of Byte);
  60. end;
  61. PLongWordRec = ^TLongWordRec;
  62. TLongWordRecArray = array[0..MaxInt div 4 - 1] of TLongWordRec;
  63. PLongWordRecArray = ^TLongWordRecArray;
  64. TInt64Rec = packed record
  65. case Integer of
  66. 0: (Int64Value: Int64);
  67. 1: (Low, High: LongWord);
  68. { Array variants - Index 0 means lowest significant byte (word, ...).}
  69. 2: (Words: array[0..3] of Word);
  70. 3: (Bytes: array[0..7] of Byte);
  71. end;
  72. PInt64Rec = ^TInt64Rec;
  73. TInt64RecArray = array[0..MaxInt div 8 - 1] of TInt64Rec;
  74. PInt64RecArray = ^TInt64RecArray;
  75. TFloatHelper = record
  76. Data1: Int64;
  77. Data2: Int64;
  78. end;
  79. PFloatHelper = ^TFloatHelper;
  80. TChar2 = array[0..1] of Char;
  81. TChar4 = array[0..3] of Char;
  82. TChar8 = array[0..7] of Char;
  83. { Frees class instance and sets its reference to nil.}
  84. procedure FreeAndNil(var Obj);
  85. { Frees pointer and sets it to nil.}
  86. procedure FreeMemNil(var P); {$IFDEF USE_INLINE}inline;{$ENDIF}
  87. { Replacement of standard System.FreeMem procedure which checks if P is nil
  88. (this is only needed for Free Pascal, Delphi makes checks in its FreeMem).}
  89. procedure FreeMem(P: Pointer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  90. { Returns current exception object. Do not call outside exception handler.}
  91. function GetExceptObject: Exception; {$IFDEF USE_INLINE}inline;{$ENDIF}
  92. { Returns time value with microsecond resolution. Use for some time counters.}
  93. function GetTimeMicroseconds: Int64;
  94. { Returns file extension (without "." dot)}
  95. function GetFileExt(const FileName: string): string;
  96. { Returns file name of application's executable.}
  97. function GetAppExe: string;
  98. { Returns directory where application's exceutable is located without
  99. path delimiter at the end.}
  100. function GetAppDir:string;
  101. { Returns True if FileName matches given Mask with optional case sensitivity.
  102. Mask can contain ? and * special characters: ? matches
  103. one character, * matches zero or more characters.}
  104. function MatchFileNameMask(const FileName, Mask: string; CaseSensitive: Boolean = False): Boolean;
  105. { Clamps integer value to range <Min, Max>}
  106. function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  107. { Clamps float value to range <Min, Max>}
  108. function ClampFloat(Number: Single; Min, Max: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  109. { Clamps integer value to Byte boundaries.}
  110. function ClampToByte(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  111. { Clamps integer value to Word boundaries.}
  112. function ClampToWord(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  113. { Returns True if Num is power of 2.}
  114. function IsPow2(Num: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  115. { Returns next power of 2 greater than or equal to Num
  116. (if Num itself is power of 2 then it retuns Num).}
  117. function NextPow2(Num: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  118. { Raises 2 to the given integer power (in range [0, 30]).}
  119. function Pow2Int(Exponent: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  120. { Raises Base to any power.}
  121. function Power(const Base, Exponent: Single): Single;
  122. { Returns log base 2 of integer X (max 2^30) or -1 if X is not power of 2.}
  123. function Log2Int(X: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  124. { Returns log base 2 of X.}
  125. function Log2(X: Single): Single;
  126. { Returns largest integer <= Val (for 5.9 returns 5).}
  127. function Floor(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  128. { Returns smallest integer >= Val (for 5.1 returns 6).}
  129. function Ceil(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  130. { Returns lesser of two integer numbers.}
  131. function Min(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  132. { Returns lesser of two float numbers.}
  133. function MinFloat(A, B: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  134. { Returns greater of two integer numbers.}
  135. function Max(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  136. { Returns greater of two float numbers.}
  137. function MaxFloat(A, B: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  138. { Returns result from multiplying Number by Numerator and then dividing by Denominator.
  139. Denominator must be greater than 0.}
  140. function MulDiv(Number, Numerator, Denominator: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  141. { Switches Boolean value.}
  142. procedure Switch(var Value: Boolean); {$IFDEF USE_INLINE}inline;{$ENDIF}
  143. { If Condition is True then TruePart is retured, otherwise
  144. FalsePart is returned.}
  145. function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  146. { If Condition is True then TruePart is retured, otherwise
  147. FalsePart is returned.}
  148. function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  149. { If Condition is True then TruePart is retured, otherwise
  150. FalsePart is returned.}
  151. function Iff(Condition, TruePart, FalsePart: Boolean): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  152. { If Condition is True then TruePart is retured, otherwise
  153. FalsePart is returned.}
  154. function Iff(Condition: Boolean; const TruePart, FalsePart: string): string; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  155. { If Condition is True then TruePart is retured, otherwise
  156. FalsePart is returned.}
  157. function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  158. { If Condition is True then TruePart is retured, otherwise
  159. FalsePart is returned.}
  160. function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  161. { If Condition is True then TruePart is retured, otherwise
  162. FalsePart is returned.}
  163. function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  164. { If Condition is True then TruePart is retured, otherwise
  165. FalsePart is returned.}
  166. function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  167. { Swaps two Byte values}
  168. procedure SwapValues(var A, B: Byte); overload;
  169. { Swaps two Word values}
  170. procedure SwapValues(var A, B: Word); overload;
  171. { Swaps two LongInt values}
  172. procedure SwapValues(var A, B: LongInt); overload;
  173. { Swaps two Single values}
  174. procedure SwapValues(var A, B: Single); overload;
  175. { Swaps two LongInt values if necessary to ensure that Min <= Max.}
  176. procedure SwapMin(var Min, Max: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
  177. { This function returns True if running on little endian machine.}
  178. function IsLittleEndian: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  179. { Swaps byte order of Word value.}
  180. function SwapEndianWord(Value: Word): Word; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  181. { Swaps byte order of multiple Word values.}
  182. procedure SwapEndianWord(P: PWordArray; Count: LongInt); overload;
  183. { Swaps byte order of LongWord value.}
  184. function SwapEndianLongWord(Value: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  185. { Swaps byte order of multiple LongWord values.}
  186. procedure SwapEndianLongWord(P: PLongWord; Count: LongInt); overload;
  187. { Calculates CRC32 for the given data.}
  188. procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt);
  189. { Fills given memory with given Byte value. Size is size of buffer in bytes.}
  190. procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte);
  191. { Fills given memory with given Word value. Size is size of buffer in bytes.}
  192. procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word);
  193. { Fills given memory with given LongWord value. Size is size of buffer in bytes.}
  194. procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord);
  195. { Returns how many mipmap levels can be created for image of given size.}
  196. function GetNumMipMapLevels(Width, Height: LongInt): LongInt;
  197. { Returns total number of levels of volume texture with given depth and
  198. mipmap count (this is not depth * mipmaps!).}
  199. function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
  200. { Returns rectangle (X, Y, X + Width, Y + Height).}
  201. function BoundsToRect(X, Y, Width, Height: LongInt): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  202. { Returns rectangle (R.Left, R.Top, R.Left + R.Right, R.Top + R.Bottom).}
  203. function BoundsToRect(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  204. { Returns rectangle (R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top).}
  205. function RectToBounds(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  206. { Clips given bounds to Clip rectangle.}
  207. procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect);
  208. { Clips given source bounds and dest position. It is used by various CopyRect
  209. functions that copy rect from one image to another. It handles clipping the same way
  210. as Win32 BitBlt function. }
  211. procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt;
  212. SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  213. { Clips given source bounds and dest bounds. It is used by various StretchRect
  214. functions that stretch rectangle of pixels from one image to another.
  215. It handles clipping the same way as Win32 StretchBlt function. }
  216. procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY,
  217. DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  218. { Scales one rectangle to fit into another. Proportions are preserved so
  219. it could be used for 'Stretch To Fit Window' image drawing for instance.}
  220. function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect;
  221. { Outputs debug message - shows message dialog in Windows and writes to console
  222. in Linux/Unix.}
  223. procedure DebugMsg(const Msg: string; const Args: array of const);
  224. implementation
  225. uses
  226. {$IFDEF MSWINDOWS}
  227. Windows,
  228. {$ENDIF}
  229. {$IFDEF UNIX}
  230. {$IFDEF KYLIX}
  231. Libc,
  232. {$ELSE}
  233. Dos, BaseUnix, Unix,
  234. {$ENDIF}
  235. {$ENDIF}
  236. Classes;
  237. procedure FreeAndNil(var Obj);
  238. var
  239. Temp: TObject;
  240. begin
  241. Temp := TObject(Obj);
  242. Pointer(Obj) := nil;
  243. Temp.Free;
  244. end;
  245. procedure FreeMemNil(var P);
  246. begin
  247. FreeMem(Pointer(P));
  248. Pointer(P) := nil;
  249. end;
  250. procedure FreeMem(P: Pointer);
  251. begin
  252. if P <> nil then
  253. System.FreeMem(P);
  254. end;
  255. function GetExceptObject: Exception;
  256. begin
  257. Result := Exception(ExceptObject);
  258. end;
  259. {$IFDEF MSWINDOWS}
  260. var
  261. PerfFrequency: Int64;
  262. InvPerfFrequency: Single;
  263. function GetTimeMicroseconds: Int64;
  264. var
  265. Time: Int64;
  266. begin
  267. QueryPerformanceCounter(Time);
  268. Result := Round(1000000 * InvPerfFrequency * Time);
  269. end;
  270. {$ENDIF}
  271. {$IFDEF UNIX}
  272. function GetTimeMicroseconds: Int64;
  273. var
  274. TimeVal: TTimeVal;
  275. begin
  276. {$IFDEF KYLIX}
  277. GetTimeOfDay(TimeVal, nil);
  278. {$ELSE}
  279. fpGetTimeOfDay(@TimeVal, nil);
  280. {$ENDIF}
  281. Result := Int64(TimeVal.tv_sec) * 1000000 + TimeVal.tv_usec;
  282. end;
  283. {$ENDIF}
  284. {$IFDEF MSDOS}
  285. function GetTimeMicroseconds: Int64;
  286. asm
  287. XOR EAX, EAX
  288. CLI
  289. OUT $43, AL
  290. MOV EDX, FS:[$46C]
  291. IN AL, $40
  292. DB $EB, 0, $EB, 0, $EB, 0
  293. MOV AH, AL
  294. IN AL, $40
  295. DB $EB, 0, $EB, 0, $EB, 0
  296. XCHG AL, AH
  297. NEG AX
  298. MOVZX EDI, AX
  299. STI
  300. MOV EBX, $10000
  301. MOV EAX, EDX
  302. XOR EDX, EDX
  303. MUL EBX
  304. ADD EAX, EDI
  305. ADC EDX, 0
  306. PUSH EDX
  307. PUSH EAX
  308. MOV ECX, $82BF1000
  309. MOVZX EAX, WORD PTR FS:[$470]
  310. MUL ECX
  311. MOV ECX, EAX
  312. POP EAX
  313. POP EDX
  314. ADD EAX, ECX
  315. ADC EDX, 0
  316. end;
  317. {$ENDIF}
  318. function GetFileExt(const FileName: string): string;
  319. begin
  320. Result := ExtractFileExt(FileName);
  321. if Length(Result) > 1 then
  322. Delete(Result, 1, 1);
  323. end;
  324. function GetAppExe: string;
  325. {$IFDEF MSWINDOWS}
  326. var
  327. FileName: array[0..MAX_PATH] of Char;
  328. begin
  329. SetString(Result, FileName,
  330. Windows.GetModuleFileName(MainInstance, FileName, SizeOf(FileName)));
  331. {$ENDIF}
  332. {$IFDEF UNIX}
  333. {$IFDEF KYLIX}
  334. var
  335. FileName: array[0..FILENAME_MAX] of Char;
  336. begin
  337. SetString(Result, FileName,
  338. System.GetModuleFileName(MainInstance, FileName, SizeOf(FileName)));
  339. {$ELSE}
  340. begin
  341. Result := FExpand(ParamStr(0));
  342. {$ENDIF}
  343. {$ENDIF}
  344. {$IFDEF MSDOS}
  345. begin
  346. Result := ParamStr(0);
  347. {$ENDIF}
  348. end;
  349. function GetAppDir:string;
  350. begin
  351. Result := ExtractFileDir(GetAppExe);
  352. end;
  353. function MatchFileNameMask(const FileName, Mask: string; CaseSensitive: Boolean): Boolean;
  354. var
  355. MaskLen, KeyLen : LongInt;
  356. function CharMatch(A, B: Char): Boolean;
  357. begin
  358. if CaseSensitive then
  359. Result := A = B
  360. else
  361. Result := UpCase(A) = UpCase(B);
  362. end;
  363. function MatchAt(MaskPos, KeyPos: LongInt): Boolean;
  364. begin
  365. while (MaskPos <= MaskLen) and (KeyPos <= KeyLen) do
  366. begin
  367. case Mask[MaskPos] of
  368. '?' :
  369. begin
  370. Inc(MaskPos);
  371. Inc(KeyPos);
  372. end;
  373. '*' :
  374. begin
  375. while (MaskPos <= MaskLen) and (Mask[MaskPos] = '*') do
  376. Inc(MaskPos);
  377. if MaskPos > MaskLen then
  378. begin
  379. Result := True;
  380. Exit;
  381. end;
  382. repeat
  383. if MatchAt(MaskPos, KeyPos) then
  384. begin
  385. Result := True;
  386. Exit;
  387. end;
  388. Inc(KeyPos);
  389. until KeyPos > KeyLen;
  390. Result := False;
  391. Exit;
  392. end;
  393. else
  394. if not CharMatch(Mask[MaskPos], FileName[KeyPos]) then
  395. begin
  396. Result := False;
  397. Exit;
  398. end
  399. else
  400. begin
  401. Inc(MaskPos);
  402. Inc(KeyPos);
  403. end;
  404. end;
  405. end;
  406. while (MaskPos <= MaskLen) and (Mask[MaskPos] in ['?', '*']) do
  407. Inc(MaskPos);
  408. if (MaskPos <= MaskLen) or (KeyPos <= KeyLen) then
  409. begin
  410. Result := False;
  411. Exit;
  412. end;
  413. Result := True;
  414. end;
  415. begin
  416. MaskLen := Length(Mask);
  417. KeyLen := Length(FileName);
  418. if MaskLen = 0 then
  419. begin
  420. Result := True;
  421. Exit;
  422. end;
  423. Result := MatchAt(1, 1);
  424. end;
  425. function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt;
  426. begin
  427. Result := Number;
  428. if Result < Min then
  429. Result := Min
  430. else
  431. if Result > Max then
  432. Result := Max;
  433. end;
  434. function ClampFloat(Number: Single; Min, Max: Single): Single;
  435. begin
  436. Result := Number;
  437. if Result < Min then
  438. Result := Min
  439. else
  440. if Result > Max then
  441. Result := Max;
  442. end;
  443. function ClampToByte(Value: LongInt): LongInt;
  444. begin
  445. Result := Value;
  446. if Result > 255 then
  447. Result := 255
  448. else if Result < 0 then
  449. Result := 0;
  450. end;
  451. function ClampToWord(Value: LongInt): LongInt;
  452. begin
  453. Result := Value;
  454. if Result > 65535 then
  455. Result := 65535
  456. else if Result < 0 then
  457. Result := 0;
  458. end;
  459. function IsPow2(Num: LongInt): Boolean;
  460. begin
  461. Result := (Num and -Num) = Num;
  462. end;
  463. function NextPow2(Num: LongInt): LongInt;
  464. begin
  465. Result := Num and -Num;
  466. while (Result < Num) do
  467. Result := Result shl 1;
  468. end;
  469. function Pow2Int(Exponent: LongInt): LongInt;
  470. begin
  471. Result := 1 shl Exponent;
  472. end;
  473. function Power(const Base, Exponent: Single): Single;
  474. begin
  475. if Exponent = 0.0 then
  476. Result := 1.0
  477. else if (Base = 0.0) and (Exponent > 0.0) then
  478. Result := 0.0
  479. else
  480. Result := Exp(Exponent * Ln(Base));
  481. end;
  482. function Log2Int(X: LongInt): LongInt;
  483. begin
  484. case X of
  485. 1: Result := 0;
  486. 2: Result := 1;
  487. 4: Result := 2;
  488. 8: Result := 3;
  489. 16: Result := 4;
  490. 32: Result := 5;
  491. 64: Result := 6;
  492. 128: Result := 7;
  493. 256: Result := 8;
  494. 512: Result := 9;
  495. 1024: Result := 10;
  496. 2048: Result := 11;
  497. 4096: Result := 12;
  498. 8192: Result := 13;
  499. 16384: Result := 14;
  500. 32768: Result := 15;
  501. 65536: Result := 16;
  502. 131072: Result := 17;
  503. 262144: Result := 18;
  504. 524288: Result := 19;
  505. 1048576: Result := 20;
  506. 2097152: Result := 21;
  507. 4194304: Result := 22;
  508. 8388608: Result := 23;
  509. 16777216: Result := 24;
  510. 33554432: Result := 25;
  511. 67108864: Result := 26;
  512. 134217728: Result := 27;
  513. 268435456: Result := 28;
  514. 536870912: Result := 29;
  515. 1073741824: Result := 30;
  516. else
  517. Result := -1;
  518. end;
  519. end;
  520. function Log2(X: Single): Single;
  521. const
  522. Ln2: Single = 0.6931471;
  523. begin
  524. Result := Ln(X) / Ln2;
  525. end;
  526. function Floor(Value: Single): LongInt;
  527. begin
  528. Result := Trunc(Value);
  529. if Frac(Value) < 0.0 then
  530. Dec(Result);
  531. end;
  532. function Ceil(Value: Single): LongInt;
  533. begin
  534. Result := Trunc(Value);
  535. if Frac(Value) > 0.0 then
  536. Inc(Result);
  537. end;
  538. procedure Switch(var Value: Boolean);
  539. begin
  540. Value := not Value;
  541. end;
  542. function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt;
  543. begin
  544. if Condition then
  545. Result := TruePart
  546. else
  547. Result := FalsePart;
  548. end;
  549. function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord;
  550. begin
  551. if Condition then
  552. Result := TruePart
  553. else
  554. Result := FalsePart;
  555. end;
  556. function Iff(Condition, TruePart, FalsePart: Boolean): Boolean;
  557. begin
  558. if Condition then
  559. Result := TruePart
  560. else
  561. Result := FalsePart;
  562. end;
  563. function Iff(Condition: Boolean; const TruePart, FalsePart: string): string;
  564. begin
  565. if Condition then
  566. Result := TruePart
  567. else
  568. Result := FalsePart;
  569. end;
  570. function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char;
  571. begin
  572. if Condition then
  573. Result := TruePart
  574. else
  575. Result := FalsePart;
  576. end;
  577. function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer;
  578. begin
  579. if Condition then
  580. Result := TruePart
  581. else
  582. Result := FalsePart;
  583. end;
  584. function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64;
  585. begin
  586. if Condition then
  587. Result := TruePart
  588. else
  589. Result := FalsePart;
  590. end;
  591. function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single;
  592. begin
  593. if Condition then
  594. Result := TruePart
  595. else
  596. Result := FalsePart;
  597. end;
  598. procedure SwapValues(var A, B: Byte);
  599. var
  600. Tmp: Byte;
  601. begin
  602. Tmp := A;
  603. A := B;
  604. B := Tmp;
  605. end;
  606. procedure SwapValues(var A, B: Word);
  607. var
  608. Tmp: Word;
  609. begin
  610. Tmp := A;
  611. A := B;
  612. B := Tmp;
  613. end;
  614. procedure SwapValues(var A, B: LongInt);
  615. var
  616. Tmp: LongInt;
  617. begin
  618. Tmp := A;
  619. A := B;
  620. B := Tmp;
  621. end;
  622. procedure SwapValues(var A, B: Single);
  623. var
  624. Tmp: Single;
  625. begin
  626. Tmp := A;
  627. A := B;
  628. B := Tmp;
  629. end;
  630. procedure SwapMin(var Min, Max: LongInt);
  631. var
  632. Tmp: LongInt;
  633. begin
  634. if Min > Max then
  635. begin
  636. Tmp := Min;
  637. Min := Max;
  638. Max := Tmp;
  639. end;
  640. end;
  641. function Min(A, B: LongInt): LongInt;
  642. begin
  643. if A < B then
  644. Result := A
  645. else
  646. Result := B;
  647. end;
  648. function MinFloat(A, B: Single): Single;
  649. begin
  650. if A < B then
  651. Result := A
  652. else
  653. Result := B;
  654. end;
  655. function Max(A, B: LongInt): LongInt;
  656. begin
  657. if A > B then
  658. Result := A
  659. else
  660. Result := B;
  661. end;
  662. function MaxFloat(A, B: Single): Single;
  663. begin
  664. if A > B then
  665. Result := A
  666. else
  667. Result := B;
  668. end;
  669. function MulDiv(Number, Numerator, Denominator: LongInt): LongInt;
  670. begin
  671. Result := Number * Numerator div Denominator;
  672. end;
  673. function IsLittleEndian: Boolean;
  674. var
  675. W: Word;
  676. begin
  677. W := $00FF;
  678. Result := PByte(@W)^ = $FF;
  679. end;
  680. function SwapEndianWord(Value: Word): Word;
  681. {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
  682. asm
  683. XCHG AH, AL
  684. end;
  685. {$ELSE}
  686. begin
  687. TWordRec(Result).Low := TWordRec(Value).High;
  688. TWordRec(Result).High := TWordRec(Value).Low;
  689. end;
  690. {$IFEND}
  691. procedure SwapEndianWord(P: PWordArray; Count: LongInt);
  692. {$IFDEF USE_ASM}
  693. asm
  694. @Loop:
  695. MOV CX, [EAX]
  696. XCHG CH, CL
  697. MOV [EAX], CX
  698. ADD EAX, 2
  699. DEC EDX
  700. JNZ @Loop
  701. end;
  702. {$ELSE}
  703. var
  704. I: LongInt;
  705. Temp: Word;
  706. begin
  707. for I := 0 to Count - 1 do
  708. begin
  709. Temp := P[I];
  710. TWordRec(P[I]).Low := TWordRec(Temp).High;
  711. TWordRec(P[I]).High := TWordRec(Temp).Low;
  712. end;
  713. end;
  714. {$ENDIF}
  715. function SwapEndianLongWord(Value: LongWord): LongWord;
  716. {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
  717. asm
  718. BSWAP EAX
  719. end;
  720. {$ELSE}
  721. begin
  722. TLongWordRec(Result).Bytes[0] := TLongWordRec(Value).Bytes[3];
  723. TLongWordRec(Result).Bytes[1] := TLongWordRec(Value).Bytes[2];
  724. TLongWordRec(Result).Bytes[2] := TLongWordRec(Value).Bytes[1];
  725. TLongWordRec(Result).Bytes[3] := TLongWordRec(Value).Bytes[0];
  726. end;
  727. {$IFEND}
  728. procedure SwapEndianLongWord(P: PLongWord; Count: LongInt);
  729. {$IFDEF USE_ASM}
  730. asm
  731. @Loop:
  732. MOV ECX, [EAX]
  733. BSWAP ECX
  734. MOV [EAX], ECX
  735. ADD EAX, 4
  736. DEC EDX
  737. JNZ @Loop
  738. end;
  739. {$ELSE}
  740. var
  741. I: LongInt;
  742. Temp: LongWord;
  743. begin
  744. for I := 0 to Count - 1 do
  745. begin
  746. Temp := PLongWordArray(P)[I];
  747. TLongWordRec(PLongWordArray(P)[I]).Bytes[0] := TLongWordRec(Temp).Bytes[3];
  748. TLongWordRec(PLongWordArray(P)[I]).Bytes[1] := TLongWordRec(Temp).Bytes[2];
  749. TLongWordRec(PLongWordArray(P)[I]).Bytes[2] := TLongWordRec(Temp).Bytes[1];
  750. TLongWordRec(PLongWordArray(P)[I]).Bytes[3] := TLongWordRec(Temp).Bytes[0];
  751. end;
  752. end;
  753. {$ENDIF}
  754. type
  755. TCrcTable = array[Byte] of LongWord;
  756. var
  757. CrcTable: TCrcTable;
  758. procedure InitCrcTable;
  759. const
  760. Polynom = $EDB88320;
  761. var
  762. I, J: LongInt;
  763. C: LongWord;
  764. begin
  765. for I := 0 to 255 do
  766. begin
  767. C := I;
  768. for J := 0 to 7 do
  769. begin
  770. if (C and $01) <> 0 then
  771. C := Polynom xor (C shr 1)
  772. else
  773. C := C shr 1;
  774. end;
  775. CrcTable[I] := C;
  776. end;
  777. end;
  778. procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt);
  779. var
  780. I: LongInt;
  781. B: PByte;
  782. begin
  783. B := Data;
  784. for I := 0 to Size - 1 do
  785. begin
  786. Crc := (Crc shr 8) xor CrcTable[B^ xor Byte(Crc)];
  787. Inc(B);
  788. end
  789. end;
  790. procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte);
  791. {$IFDEF USE_ASM}
  792. asm
  793. PUSH EDI
  794. MOV EDI, EAX
  795. MOV EAX, ECX
  796. MOV AH, AL
  797. MOV CX, AX
  798. SHL EAX, 16
  799. MOV AX, CX
  800. MOV ECX, EDX
  801. SAR ECX, 2
  802. JS @Exit
  803. REP STOSD
  804. MOV ECX, EDX
  805. AND ECX, 3
  806. REP STOSB
  807. POP EDI
  808. @Exit:
  809. end;
  810. {$ELSE}
  811. begin
  812. FillChar(Data^, Size, Value);
  813. end;
  814. {$ENDIF}
  815. procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word);
  816. {$IFDEF USE_ASM}
  817. asm
  818. PUSH EDI
  819. PUSH EBX
  820. MOV EBX, EDX
  821. MOV EDI, EAX
  822. MOV EAX, ECX
  823. MOV CX, AX
  824. SHL EAX, 16
  825. MOV AX, CX
  826. MOV ECX, EDX
  827. SHR ECX, 2
  828. JZ @Word
  829. REP STOSD
  830. @Word:
  831. MOV ECX, EBX
  832. AND ECX, 2
  833. JZ @Byte
  834. MOV [EDI], AX
  835. ADD EDI, 2
  836. @Byte:
  837. MOV ECX, EBX
  838. AND ECX, 1
  839. JZ @Exit
  840. MOV [EDI], AL
  841. @Exit:
  842. POP EBX
  843. POP EDI
  844. end;
  845. {$ELSE}
  846. var
  847. I, V: LongWord;
  848. begin
  849. V := Value * $10000 + Value;
  850. for I := 0 to Size div 4 - 1 do
  851. PLongWordArray(Data)[I] := V;
  852. case Size mod 4 of
  853. 1: PByteArray(Data)[Size - 1] := Lo(Value);
  854. 2: PWordArray(Data)[Size div 2] := Value;
  855. 3:
  856. begin
  857. PWordArray(Data)[Size div 2 - 1] := Value;
  858. PByteArray(Data)[Size - 1] := Lo(Value);
  859. end;
  860. end;
  861. end;
  862. {$ENDIF}
  863. procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord);
  864. {$IFDEF USE_ASM}
  865. asm
  866. PUSH EDI
  867. PUSH EBX
  868. MOV EBX, EDX
  869. MOV EDI, EAX
  870. MOV EAX, ECX
  871. MOV ECX, EDX
  872. SHR ECX, 2
  873. JZ @Word
  874. REP STOSD
  875. @Word:
  876. MOV ECX, EBX
  877. AND ECX, 2
  878. JZ @Byte
  879. MOV [EDI], AX
  880. ADD EDI, 2
  881. @Byte:
  882. MOV ECX, EBX
  883. AND ECX, 1
  884. JZ @Exit
  885. MOV [EDI], AL
  886. @Exit:
  887. POP EBX
  888. POP EDI
  889. end;
  890. {$ELSE}
  891. var
  892. I: LongInt;
  893. begin
  894. for I := 0 to Size div 4 - 1 do
  895. PLongWordArray(Data)[I] := Value;
  896. case Size mod 4 of
  897. 1: PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0];
  898. 2: PWordArray(Data)[Size div 2] := TLongWordRec(Value).Words[0];
  899. 3:
  900. begin
  901. PWordArray(Data)[Size div 2 - 1] := TLongWordRec(Value).Words[0];
  902. PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0];
  903. end;
  904. end;
  905. end;
  906. {$ENDIF}
  907. function GetNumMipMapLevels(Width, Height: LongInt): LongInt;
  908. begin
  909. Result := 0;
  910. if (Width > 0) and (Height > 0) then
  911. begin
  912. Result := 1;
  913. while (Width <> 1) or (Height <> 1) do
  914. begin
  915. Width := Width div 2;
  916. Height := Height div 2;
  917. if Width < 1 then Width := 1;
  918. if Height < 1 then Height := 1;
  919. Inc(Result);
  920. end;
  921. end;
  922. end;
  923. function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
  924. var
  925. I: LongInt;
  926. begin
  927. Result := Depth;
  928. for I := 1 to MipMaps - 1 do
  929. Inc(Result, ClampInt(Depth shr I, 1, Depth));
  930. end;
  931. function BoundsToRect(X, Y, Width, Height: LongInt): TRect;
  932. begin
  933. Result.Left := X;
  934. Result.Top := Y;
  935. Result.Right := X + Width;
  936. Result.Bottom := Y + Height;
  937. end;
  938. function BoundsToRect(const R: TRect): TRect;
  939. begin
  940. Result.Left := R.Left;
  941. Result.Top := R.Top;
  942. Result.Right := R.Left + R.Right;
  943. Result.Bottom := R.Top + R.Bottom;
  944. end;
  945. function RectToBounds(const R: TRect): TRect;
  946. begin
  947. Result.Left := R.Left;
  948. Result.Top := R.Top;
  949. Result.Right := R.Right - R.Left;
  950. Result.Bottom := R.Bottom - R.Top;
  951. end;
  952. procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect);
  953. procedure ClipDim(var AStart, ALength: LongInt; ClipMin, ClipMax: LongInt);
  954. begin
  955. if AStart < ClipMin then
  956. begin
  957. ALength := ALength - (ClipMin - AStart);
  958. AStart := ClipMin;
  959. end;
  960. if AStart + ALength > ClipMax then ALength := Max(0, ClipMax - AStart);
  961. end;
  962. begin
  963. ClipDim(X, Width, Clip.Left, Clip.Right);
  964. ClipDim(Y, Height, Clip.Top, Clip.Bottom);
  965. end;
  966. procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  967. procedure ClipDim(var SrcPos, DstPos, Size: LongInt; SrcClipMax,
  968. DstClipMin, DstClipMax: LongInt);
  969. var
  970. OldDstPos: LongInt;
  971. Diff: LongInt;
  972. begin
  973. OldDstPos := Iff(DstPos < 0, DstPos, 0);
  974. if DstPos < DstClipMin then
  975. begin
  976. Diff := DstClipMin - DstPos;
  977. Size := Size - Diff;
  978. if DstPos < SrcPos then
  979. SrcPos := SrcPos + Diff;
  980. DstPos := DstClipMin;
  981. end;
  982. if SrcPos < 0 then
  983. begin
  984. Size := Size + SrcPos - OldDstPos;
  985. DstPos := DstPos - SrcPos + OldDstPos;
  986. SrcPos := 0;
  987. end;
  988. if SrcPos + Size > SrcClipMax then Size := SrcClipMax - SrcPos;
  989. if DstPos + Size > DstClipMax then Size := DstClipMax - DstPos;
  990. end;
  991. begin
  992. ClipDim(SrcX, DstX, Width, SrcImageWidth, DstClip.Left, DstClip.Right);
  993. ClipDim(SrcY, DstY, Height, SrcImageHeight, DstClip.Top, DstClip.Bottom);
  994. end;
  995. procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY,
  996. DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  997. procedure ClipDim(var SrcPos, DstPos, SrcSize, DstSize: LongInt; SrcClipMax,
  998. DstClipMin, DstClipMax: LongInt);
  999. var
  1000. OldSize: LongInt;
  1001. Diff: LongInt;
  1002. Scale: Single;
  1003. begin
  1004. Scale := DstSize / SrcSize;
  1005. if DstPos < DstClipMin then
  1006. begin
  1007. Diff := DstClipMin - DstPos;
  1008. DstSize := DstSize - Diff;
  1009. SrcPos := SrcPos + Round(Diff / Scale);
  1010. SrcSize := SrcSize - Round(Diff / Scale);
  1011. DstPos := DstClipMin;
  1012. end;
  1013. if SrcPos < 0 then
  1014. begin
  1015. SrcSize := SrcSize + SrcPos;
  1016. DstPos := DstPos - Round(SrcPos * Scale);
  1017. DstSize := DstSize + Round(SrcPos * Scale);
  1018. SrcPos := 0;
  1019. end;
  1020. if SrcPos + SrcSize > SrcClipMax then
  1021. begin
  1022. OldSize := SrcSize;
  1023. SrcSize := SrcClipMax - SrcPos;
  1024. DstSize := Round(DstSize * (SrcSize / OldSize));
  1025. end;
  1026. if DstPos + DstSize > DstClipMax then
  1027. begin
  1028. OldSize := DstSize;
  1029. DstSize := DstClipMax - DstPos;
  1030. SrcSize := Round(SrcSize * (DstSize / OldSize));
  1031. end;
  1032. end;
  1033. begin
  1034. ClipDim(SrcX, DstX, SrcWidth, DstWidth, SrcImageWidth, DstClip.Left, DstClip.Right);
  1035. ClipDim(SrcY, DstY, SrcHeight, DstHeight, SrcImageHeight, DstClip.Top, DstClip.Bottom);
  1036. end;
  1037. function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect;
  1038. var
  1039. SourceWidth: LongInt;
  1040. SourceHeight: LongInt;
  1041. TargetWidth: LongInt;
  1042. TargetHeight: LongInt;
  1043. ScaledWidth: LongInt;
  1044. ScaledHeight: LongInt;
  1045. begin
  1046. SourceWidth := SourceRect.Right - SourceRect.Left;
  1047. SourceHeight := SourceRect.Bottom - SourceRect.Top;
  1048. TargetWidth := TargetRect.Right - TargetRect.Left;
  1049. TargetHeight := TargetRect.Bottom - TargetRect.Top;
  1050. if SourceWidth * TargetHeight < SourceHeight * TargetWidth then
  1051. begin
  1052. ScaledWidth := (SourceWidth * TargetHeight) div SourceHeight;
  1053. Result := BoundsToRect(TargetRect.Left + ((TargetWidth - ScaledWidth) div 2),
  1054. TargetRect.Top, ScaledWidth, TargetHeight);
  1055. end
  1056. else
  1057. begin
  1058. ScaledHeight := (SourceHeight * TargetWidth) div SourceWidth;
  1059. Result := BoundsToRect(TargetRect.Left, TargetRect.Top + ((TargetHeight - ScaledHeight) div 2),
  1060. TargetWidth, ScaledHeight);
  1061. end;
  1062. end;
  1063. procedure DebugMsg(const Msg: string; const Args: array of const);
  1064. var
  1065. FmtMsg: string;
  1066. begin
  1067. FmtMsg := Format(Msg, Args);
  1068. {$IFDEF MSWINDOWS}
  1069. if IsConsole then
  1070. WriteLn('DebugMsg: ' + FmtMsg)
  1071. else
  1072. MessageBox(GetActiveWindow, PChar(FmtMsg), 'DebugMsg', MB_OK);
  1073. {$ENDIF}
  1074. {$IFDEF UNIX}
  1075. WriteLn('DebugMsg: ' + FmtMsg);
  1076. {$ENDIF}
  1077. {$IFDEF MSDOS}
  1078. WriteLn('DebugMsg: ' + FmtMsg);
  1079. {$ENDIF}
  1080. end;
  1081. initialization
  1082. InitCrcTable;
  1083. {$IFDEF MSWINDOWS}
  1084. QueryPerformanceFrequency(PerfFrequency);
  1085. InvPerfFrequency := 1.0 / PerfFrequency;
  1086. {$ENDIF}
  1087. {$IFDEF MSDOS}
  1088. // reset PIT
  1089. asm
  1090. MOV EAX, $34
  1091. OUT $43, AL
  1092. XOR EAX, EAX
  1093. OUT $40, AL
  1094. OUT $40, AL
  1095. end;
  1096. {$ENDIF}
  1097. {
  1098. File Notes:
  1099. -- TODOS ----------------------------------------------------
  1100. - nothing now
  1101. -- 0.21 Changes/Bug Fixes -----------------------------------
  1102. - Moved GetVolumeLevelCount from ImagingDds here.
  1103. - Renamed FillMemory to FillMemoryByte to avoid name collision in C++ Builder.
  1104. - Added Iff function for Char, Pointer, and Int64 types.
  1105. - Added IsLittleEndian function.
  1106. - Added array types for TWordRec, TLongWordRec, and TInt64Rec.
  1107. - Added MatchFileNameMask function.
  1108. -- 0.19 Changes/Bug Fixes -----------------------------------
  1109. - added ScaleRectToRect (thanks to Paul Michell)
  1110. - added BoundsToRect, ClipBounds, ClipCopyBounds, ClipStretchBounds functions
  1111. - added MulDiv function
  1112. - FreeAndNil is not inline anymore - caused AV in one program
  1113. -- 0.17 Changes/Bug Fixes -----------------------------------
  1114. - GetAppExe didn't return absolute path in FreeBSD, fixed
  1115. - added debug message output
  1116. - fixed Unix compatibility issues (thanks to Ales Katona).
  1117. Imaging now compiles in FreeBSD and maybe in other Unixes as well.
  1118. -- 0.15 Changes/Bug Fixes -----------------------------------
  1119. - added some new utility functions
  1120. -- 0.13 Changes/Bug Fixes -----------------------------------
  1121. - added many new utility functions
  1122. - minor change in SwapEndian to avoid range check error
  1123. }
  1124. end.