ImagingUtility.pas 29 KB

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