Repo for the search and displace ingest module that takes odf, docx and pdf and transforms it into .md to be used with search and displace operations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1735 lines
47 KiB

3 years ago
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. http://imaginglib.sourceforge.net
  5. The contents of this file are used with permission, subject to the Mozilla
  6. Public License Version 1.1 (the "License"); you may not use this file except
  7. in compliance with the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/MPL-1.1.html
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  11. the specific language governing rights and limitations under the License.
  12. Alternatively, the contents of this file may be used under the terms of the
  13. GNU Lesser General Public License (the "LGPL License"), in which case the
  14. provisions of the LGPL License are applicable instead of those above.
  15. If you wish to allow use of your version of this file only under the terms
  16. of the LGPL License and not to allow others to use your version of this file
  17. under the MPL, indicate your decision by deleting the provisions above and
  18. replace them with the notice and other provisions required by the LGPL
  19. License. If you do not delete the provisions above, a recipient may use
  20. your version of this file under either the MPL or the LGPL License.
  21. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  22. }
  23. { This unit contains utility functions and types for Imaging library.}
  24. unit ImagingUtility;
  25. {$I ImagingOptions.inc}
  26. interface
  27. uses
  28. SysUtils, Classes, Types;
  29. const
  30. STrue = 'True';
  31. SFalse = 'False';
  32. type
  33. TByteArray = array[0..MaxInt - 1] of Byte;
  34. PByteArray = ^TByteArray;
  35. TWordArray = array[0..MaxInt div 2 - 1] of Word;
  36. PWordArray = ^TWordArray;
  37. TLongIntArray = array[0..MaxInt div 4 - 1] of LongInt;
  38. PLongIntArray = ^TLongIntArray;
  39. TLongWordArray = array[0..MaxInt div 4 - 1] of LongWord;
  40. PLongWordArray = ^TLongWordArray;
  41. TInt64Array = array[0..MaxInt div 8 - 1] of Int64;
  42. PInt64Array = ^TInt64Array;
  43. TSingleArray = array[0..MaxInt div 4 - 1] of Single;
  44. PSingleArray = ^TSingleArray;
  45. TBooleanArray = array[0..MaxInt - 1] of Boolean;
  46. PBooleanArray = ^TBooleanArray;
  47. TDynByteArray = array of Byte;
  48. TDynIntegerArray = array of Integer;
  49. TDynBooleanArray = array of Boolean;
  50. TDynStringArray = array of string;
  51. TWordRec = packed record
  52. case Integer of
  53. 0: (WordValue: Word);
  54. 1: (Low, High: Byte);
  55. end;
  56. PWordRec = ^TWordRec;
  57. TWordRecArray = array[0..MaxInt div 2 - 1] of TWordRec;
  58. PWordRecArray = ^TWordRecArray;
  59. TLongWordRec = packed record
  60. case Integer of
  61. 0: (LongWordValue: LongWord);
  62. 1: (Low, High: Word);
  63. { Array variants - Index 0 means lowest significant byte (word, ...).}
  64. 2: (Words: array[0..1] of Word);
  65. 3: (Bytes: array[0..3] of Byte);
  66. end;
  67. PLongWordRec = ^TLongWordRec;
  68. TLongWordRecArray = array[0..MaxInt div 4 - 1] of TLongWordRec;
  69. PLongWordRecArray = ^TLongWordRecArray;
  70. TInt64Rec = packed record
  71. case Integer of
  72. 0: (Int64Value: Int64);
  73. 1: (Low, High: LongWord);
  74. { Array variants - Index 0 means lowest significant byte (word, ...).}
  75. 2: (Words: array[0..3] of Word);
  76. 3: (Bytes: array[0..7] of Byte);
  77. end;
  78. PInt64Rec = ^TInt64Rec;
  79. TInt64RecArray = array[0..MaxInt div 8 - 1] of TInt64Rec;
  80. PInt64RecArray = ^TInt64RecArray;
  81. TFloatHelper = record
  82. Data: Int64;
  83. case Integer of
  84. 0: (Data64: Int64);
  85. 1: (Data32: LongWord);
  86. end;
  87. PFloatHelper = ^TFloatHelper;
  88. TFloatPoint = record
  89. X, Y: Single;
  90. end;
  91. TFloatRect = record
  92. Left, Top, Right, Bottom: Single;
  93. end;
  94. TChar2 = array[0..1] of AnsiChar;
  95. TChar3 = array[0..2] of AnsiChar;
  96. TChar4 = array[0..3] of AnsiChar;
  97. TChar8 = array[0..7] of AnsiChar;
  98. TChar16 = array[0..15] of AnsiChar;
  99. TAnsiCharSet = set of AnsiChar;
  100. ENotImplemented = class(Exception)
  101. public
  102. constructor Create;
  103. end;
  104. { Options for BuildFileList function:
  105. flFullNames - file names in result will have full path names
  106. (ExtractFileDir(Path) + FileName)
  107. flRelNames - file names in result will have names relative to
  108. ExtractFileDir(Path) dir
  109. flRecursive - adds files in subdirectories found in Path.}
  110. TFileListOption = (flFullNames, flRelNames, flRecursive);
  111. TFileListOptions = set of TFileListOption;
  112. { Frees class instance and sets its reference to nil.}
  113. procedure FreeAndNil(var Obj);
  114. { Frees pointer and sets it to nil.}
  115. procedure FreeMemNil(var P); {$IFDEF USE_INLINE}inline;{$ENDIF}
  116. { Replacement of standard System.FreeMem procedure which checks if P is nil
  117. (this is only needed for Free Pascal, Delphi makes checks in its FreeMem).}
  118. procedure FreeMem(P: Pointer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  119. { Returns current exception object. Do not call outside exception handler.}
  120. function GetExceptObject: Exception; {$IFDEF USE_INLINE}inline;{$ENDIF}
  121. { Returns time value with microsecond resolution.}
  122. function GetTimeMicroseconds: Int64;
  123. { Returns time value with milisecond resolution.}
  124. function GetTimeMilliseconds: Int64;
  125. { Returns file extension (without "." dot)}
  126. function GetFileExt(const FileName: string): string;
  127. { Returns file name of application's executable.}
  128. function GetAppExe: string;
  129. { Returns directory where application's exceutable is located without
  130. path delimiter at the end.}
  131. function GetAppDir: string;
  132. { Works like SysUtils.ExtractFileName but supports '/' and '\' dir delimiters
  133. at the same time (whereas ExtractFileName supports on default delimiter on current platform).}
  134. function GetFileName(const FileName: string): string;
  135. { Works like SysUtils.ExtractFileDir but supports '/' and '\' dir delimiters
  136. at the same time (whereas ExtractFileDir supports on default delimiter on current platform).}
  137. function GetFileDir(const FileName: string): string;
  138. { Returns True if Subject matches given Mask with optional case sensitivity.
  139. Mask can contain ? and * special characters: ? matches
  140. one character, * matches zero or more characters.}
  141. function StrMaskMatch(const Subject, Mask: string; CaseSensitive: Boolean = False): Boolean;
  142. { This function fills Files string list with names of files found
  143. with FindFirst/FindNext functions (See details on Path/Atrr here).
  144. - BuildFileList('c:\*.*', faAnyFile, List, [flRecursive]) returns
  145. list of all files (only name.ext - no path) on C drive
  146. - BuildFileList('d:\*.*', faDirectory, List, [flFullNames]) returns
  147. list of all directories (d:\dirxxx) in root of D drive.}
  148. function BuildFileList(Path: string; Attr: LongInt; Files: TStrings;
  149. Options: TFileListOptions = []): Boolean;
  150. { Similar to RTL's Pos function but with optional Offset where search will start.
  151. This function is in the RTL StrUtils unit but }
  152. function PosEx(const SubStr, S: string; Offset: LongInt = 1): LongInt;
  153. { Same as PosEx but without case sensitivity.}
  154. function PosNoCase(const SubStr, S: string; Offset: LongInt = 1): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  155. { Returns a sub-string from S which is followed by
  156. Sep separator and deletes the sub-string from S including the separator.}
  157. function StrToken(var S: string; Sep: Char): string;
  158. { Same as StrToken but searches from the end of S string.}
  159. function StrTokenEnd(var S: string; Sep: Char): string;
  160. { Fills instance of TStrings with tokens from string S where tokens are separated by
  161. one of Seps characters.}
  162. procedure StrTokensToList(const S: string; Sep: Char; Tokens: TStrings);
  163. { Returns string representation of integer number (with digit grouping).
  164. Uses current locale.}
  165. function IntToStrFmt(const I: Int64): string; {$IFDEF USE_INLINE}inline;{$ENDIF}
  166. { Returns string representation of float number (with digit grouping).
  167. Uses current locale.}
  168. function FloatToStrFmt(const F: Double; Precision: Integer = 2): string; {$IFDEF USE_INLINE}inline;{$ENDIF}
  169. { Returns format settings for parsing floats (dot as decimal separator).
  170. Useful when fomatting/parsing floats etc.}
  171. function GetFormatSettingsForFloats: TFormatSettings;
  172. { Returns True if S contains at least one of the substrings in SubStrs array. Case sensitive.}
  173. function ContainsAnySubStr(const S: string; const SubStrs: array of string): Boolean;
  174. { Extracts substring starting at IdxStart ending at IdxEnd.
  175. S[IdxEnd] is not included in the result.}
  176. function SubString(const S: string; IdxStart, IdxEnd: Integer): string; {$IFDEF USE_INLINE}inline;{$ENDIF}
  177. { Clamps integer value to range <Min, Max>}
  178. function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  179. { Clamps float value to range <Min, Max>}
  180. function ClampFloat(Number: Single; Min, Max: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  181. { Clamps integer value to Byte boundaries.}
  182. function ClampToByte(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  183. { Clamps integer value to Word boundaries.}
  184. function ClampToWord(Value: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  185. { Returns True if Num is power of 2.}
  186. function IsPow2(Num: LongInt): Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  187. { Returns next power of 2 greater than or equal to Num
  188. (if Num itself is power of 2 then it retuns Num).}
  189. function NextPow2(Num: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  190. { Raises 2 to the given integer power (in range [0, 30]).}
  191. function Pow2Int(Exponent: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  192. { Raises Base to any power.}
  193. function Power(const Base, Exponent: Single): Single;
  194. { Returns log base 2 of integer X (max 2^30) or -1 if X is not power of 2.}
  195. function Log2Int(X: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  196. { Returns log base 2 of X.}
  197. function Log2(X: Single): Single;
  198. { Returns log base 10 of X.}
  199. function Log10(X: Single): Single;
  200. { Returns largest integer <= Val (for 5.9 returns 5).}
  201. function Floor(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  202. { Returns smallest integer >= Val (for 5.1 returns 6).}
  203. function Ceil(Value: Single): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  204. { Returns lesser of two integer numbers.}
  205. function Min(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  206. { Returns lesser of two float numbers.}
  207. function MinFloat(A, B: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  208. { Returns greater of two integer numbers.}
  209. function Max(A, B: LongInt): LongInt; {$IFDEF USE_INLINE}inline;{$ENDIF}
  210. { Returns greater of two float numbers.}
  211. function MaxFloat(A, B: Single): Single; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  212. { Returns greater of two float numbers.}
  213. function MaxFloat(const A, B: Double): Double; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  214. { Returns result from multiplying Number by Numerator and then dividing by Denominator.
  215. Denominator must be greater than 0.}
  216. function MulDiv(Number, Numerator, Denominator: Word): Word; {$IFDEF USE_INLINE}inline;{$ENDIF}
  217. { Returns true if give floats are the equal within given delta.}
  218. function SameFloat(A, B: Single; Delta: Single = 0.001): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  219. { Returns true if give floats are the equal within given delta.}
  220. function SameFloat(const A, B: Double; const Delta: Double = 0.000001): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  221. { Switches Boolean value.}
  222. procedure Switch(var Value: Boolean); {$IFDEF USE_INLINE}inline;{$ENDIF}
  223. { If Condition is True then TruePart is retured, otherwise
  224. FalsePart is returned.}
  225. function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  226. { If Condition is True then TruePart is retured, otherwise
  227. FalsePart is returned.}
  228. function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  229. { If Condition is True then TruePart is retured, otherwise
  230. FalsePart is returned.}
  231. function Iff(Condition, TruePart, FalsePart: Boolean): Boolean; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  232. { If Condition is True then TruePart is retured, otherwise
  233. FalsePart is returned.}
  234. function Iff(Condition: Boolean; const TruePart, FalsePart: string): string; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  235. { If Condition is True then TruePart is retured, otherwise
  236. FalsePart is returned.}
  237. function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char; overload; {$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: Pointer): Pointer; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  241. { If Condition is True then TruePart is retured, otherwise
  242. FalsePart is returned.}
  243. function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  244. { If Condition is True then TruePart is retured, otherwise
  245. FalsePart is returned.}
  246. function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single; {$IFDEF USE_INLINE}inline;{$ENDIF}
  247. { Swaps two Boolean values}
  248. procedure SwapValues(var A, B: Boolean); overload;
  249. { Swaps two Byte values}
  250. procedure SwapValues(var A, B: Byte); overload;
  251. { Swaps two Word values}
  252. procedure SwapValues(var A, B: Word); overload;
  253. { Swaps two LongInt values}
  254. procedure SwapValues(var A, B: LongInt); overload;
  255. { Swaps two Single values}
  256. procedure SwapValues(var A, B: Single); overload;
  257. { Swaps two LongInt values if necessary to ensure that Min <= Max.}
  258. procedure SwapMin(var Min, Max: LongInt); {$IFDEF USE_INLINE}inline;{$ENDIF}
  259. { This function returns True if running on little endian machine.}
  260. function IsLittleEndian: Boolean; {$IFDEF USE_INLINE}inline;{$ENDIF}
  261. { Swaps byte order of Word value.}
  262. function SwapEndianWord(Value: Word): Word; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  263. { Swaps byte order of multiple Word values.}
  264. procedure SwapEndianWord(P: PWordArray; Count: LongInt); overload;
  265. { Swaps byte order of LongWord value.}
  266. function SwapEndianLongWord(Value: LongWord): LongWord; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  267. { Swaps byte order of multiple LongWord values.}
  268. procedure SwapEndianLongWord(P: PLongWord; Count: LongInt); overload;
  269. { Calculates CRC32 for the given data.}
  270. procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt);
  271. { Fills given memory with given Byte value. Size is size of buffer in bytes.}
  272. procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte);
  273. { Fills given memory with given Word value. Size is size of buffer in bytes.}
  274. procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word);
  275. { Fills given memory with given LongWord value. Size is size of buffer in bytes.}
  276. procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord);
  277. { Fills given memory zeroes.}
  278. {$EXTERNALSYM ZeroMemory} // Conflicts with WinAPI ZeroMemory in C++ Builder
  279. procedure ZeroMemory(Data: Pointer; Size: Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
  280. { Returns how many mipmap levels can be created for image of given size.}
  281. function GetNumMipMapLevels(Width, Height: LongInt): LongInt;
  282. { Returns total number of levels of volume texture with given depth and
  283. mipmap count (this is not depth * mipmaps!).}
  284. function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
  285. { Returns rectangle (X, Y, X + Width, Y + Height).}
  286. function BoundsToRect(X, Y, Width, Height: LongInt): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  287. { Returns rectangle (R.Left, R.Top, R.Left + R.Right, R.Top + R.Bottom).}
  288. function BoundsToRect(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  289. { Returns rectangle (R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top).}
  290. function RectToBounds(const R: TRect): TRect; overload; {$IFDEF USE_INLINE}inline;{$ENDIF}
  291. { Clips given bounds to Clip rectangle.}
  292. procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect);
  293. { Clips given source bounds and dest position. It is used by various CopyRect
  294. functions that copy rect from one image to another. It handles clipping the same way
  295. as Win32 BitBlt function. }
  296. procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt;
  297. SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  298. { Clips given source bounds and dest bounds. It is used by various StretchRect
  299. functions that stretch rectangle of pixels from one image to another.
  300. It handles clipping the same way as Win32 StretchBlt function. }
  301. procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY,
  302. DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  303. { Scales one rectangle to fit into another. Proportions are preserved so
  304. it could be used for 'Stretch To Fit Window' image drawing for instance.}
  305. function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect;
  306. { Scales given size to fit into max size while keeping the original ascpect ration.
  307. Useful for calculating thumbnail dimensions etc.}
  308. function ScaleSizeToFit(const CurrentSize, MaxSize: TSize): TSize;
  309. { Returns width of given rect. Part of RTL in newer Delphi.}
  310. function RectWidth(const Rect: TRect): Integer;
  311. { Returns height of given rect. Part of RTL in newer Delphi.}
  312. function RectHeight(const Rect: TRect): Integer;
  313. { Returns True if R1 fits into R2.}
  314. function RectInRect(const R1, R2: TRect): Boolean;
  315. { Returns True if R1 and R2 intersects.}
  316. function RectIntersects(const R1, R2: TRect): Boolean;
  317. { Converts pixel size in micrometers to corrensponding DPI.}
  318. function PixelSizeToDpi(SizeInMicroMeters: Single): Single;
  319. { Converts DPI to corrensponding pixel size in micrometers.}
  320. function DpiToPixelSize(Dpi: Single): Single;
  321. function FloatPoint(AX, AY: Single): TFloatPoint; {$IFDEF USE_INLINE}inline;{$ENDIF}
  322. function FloatRect(ALeft, ATop, ARight, ABottom: Single): TFloatRect;
  323. function FloatRectWidth(const R: TFloatRect): Single;
  324. function FloatRectHeight(const R: TFloatRect): Single;
  325. function FloatRectFromRect(const R: TRect): TFloatRect;
  326. { Formats given message for usage in Exception.Create(..). Use only
  327. in except block - returned message contains message of last raised exception.}
  328. function FormatExceptMsg(const Msg: string; const Args: array of const): string;
  329. { Outputs debug message - shows message dialog in Windows and writes to console
  330. in Linux/Unix.}
  331. procedure DebugMsg(const Msg: string; const Args: array of const);
  332. implementation
  333. uses
  334. {$IF Defined(MSWINDOWS)}
  335. Windows;
  336. {$ELSEIF Defined(FPC)}
  337. Dos, BaseUnix, Unix;
  338. {$ELSEIF Defined(DELPHI)}
  339. Posix.SysTime;
  340. {$IFEND}
  341. var
  342. FloatFormatSettings: TFormatSettings;
  343. constructor ENotImplemented.Create;
  344. begin
  345. inherited Create('Not implemented');
  346. end;
  347. procedure FreeAndNil(var Obj);
  348. var
  349. Temp: TObject;
  350. begin
  351. Temp := TObject(Obj);
  352. Pointer(Obj) := nil;
  353. Temp.Free;
  354. end;
  355. procedure FreeMemNil(var P);
  356. begin
  357. FreeMem(Pointer(P));
  358. Pointer(P) := nil;
  359. end;
  360. procedure FreeMem(P: Pointer);
  361. begin
  362. if P <> nil then
  363. System.FreeMem(P);
  364. end;
  365. function GetExceptObject: Exception;
  366. begin
  367. Result := Exception(ExceptObject);
  368. end;
  369. {$IF Defined(MSWINDOWS)}
  370. var
  371. PerfFrequency: Int64;
  372. InvPerfFrequency: Extended;
  373. function GetTimeMicroseconds: Int64;
  374. var
  375. Time: Int64;
  376. begin
  377. QueryPerformanceCounter(Time);
  378. Result := Round(1000000 * InvPerfFrequency * Time);
  379. end;
  380. {$ELSEIF Defined(DELPHI)}
  381. function GetTimeMicroseconds: Int64;
  382. var
  383. Time: TimeVal;
  384. begin
  385. Posix.SysTime.GetTimeOfDay(Time, nil);
  386. Result := Int64(Time.tv_sec) * 1000000 + Time.tv_usec;
  387. end;
  388. {$ELSEIF Defined(FPC)}
  389. function GetTimeMicroseconds: Int64;
  390. var
  391. TimeVal: TTimeVal;
  392. begin
  393. fpGetTimeOfDay(@TimeVal, nil);
  394. Result := Int64(TimeVal.tv_sec) * 1000000 + TimeVal.tv_usec;
  395. end;
  396. {$IFEND}
  397. function GetTimeMilliseconds: Int64;
  398. begin
  399. Result := GetTimeMicroseconds div 1000;
  400. end;
  401. function GetFileExt(const FileName: string): string;
  402. begin
  403. Result := ExtractFileExt(FileName);
  404. if Length(Result) > 1 then
  405. Delete(Result, 1, 1);
  406. end;
  407. function GetAppExe: string;
  408. {$IF Defined(MSWINDOWS)}
  409. var
  410. FileName: array[0..MAX_PATH] of Char;
  411. begin
  412. SetString(Result, FileName,
  413. Windows.GetModuleFileName(MainInstance, FileName, SizeOf(FileName)));
  414. {$ELSEIF Defined(DELPHI)} // Delphi non Win targets
  415. var
  416. FileName: array[0..1024] of Char;
  417. begin
  418. SetString(Result, FileName,
  419. System.GetModuleFileName(MainInstance, FileName, SizeOf(FileName)));
  420. {$ELSE}
  421. begin
  422. Result := ExpandFileName(ParamStr(0));
  423. {$IFEND}
  424. end;
  425. function GetAppDir: string;
  426. begin
  427. Result := ExtractFileDir(GetAppExe);
  428. end;
  429. function GetFileName(const FileName: string): string;
  430. var
  431. I: Integer;
  432. begin
  433. I := LastDelimiter('\/' + DriveDelim, FileName);
  434. Result := Copy(FileName, I + 1, MaxInt);
  435. end;
  436. function GetFileDir(const FileName: string): string;
  437. const
  438. Delims = '\/' + DriveDelim;
  439. var
  440. I: Integer;
  441. begin
  442. I := LastDelimiter(Delims, Filename);
  443. if (I > 1) and
  444. ((FileName[I] = Delims[1]) or (FileName[I] = Delims[2])) and
  445. (not IsDelimiter(Delims, FileName, I - 1)) then Dec(I);
  446. Result := Copy(FileName, 1, I);
  447. end;
  448. function StrMaskMatch(const Subject, Mask: string; CaseSensitive: Boolean): Boolean;
  449. var
  450. MaskLen, KeyLen : LongInt;
  451. function CharMatch(A, B: Char): Boolean;
  452. begin
  453. if CaseSensitive then
  454. Result := A = B
  455. else
  456. Result := AnsiUpperCase (A) = AnsiUpperCase (B);
  457. end;
  458. function MatchAt(MaskPos, KeyPos: LongInt): Boolean;
  459. begin
  460. while (MaskPos <= MaskLen) and (KeyPos <= KeyLen) do
  461. begin
  462. case Mask[MaskPos] of
  463. '?' :
  464. begin
  465. Inc(MaskPos);
  466. Inc(KeyPos);
  467. end;
  468. '*' :
  469. begin
  470. while (MaskPos <= MaskLen) and (Mask[MaskPos] = '*') do
  471. Inc(MaskPos);
  472. if MaskPos > MaskLen then
  473. begin
  474. Result := True;
  475. Exit;
  476. end;
  477. repeat
  478. if MatchAt(MaskPos, KeyPos) then
  479. begin
  480. Result := True;
  481. Exit;
  482. end;
  483. Inc(KeyPos);
  484. until KeyPos > KeyLen;
  485. Result := False;
  486. Exit;
  487. end;
  488. else
  489. if not CharMatch(Mask[MaskPos], Subject[KeyPos]) then
  490. begin
  491. Result := False;
  492. Exit;
  493. end
  494. else
  495. begin
  496. Inc(MaskPos);
  497. Inc(KeyPos);
  498. end;
  499. end;
  500. end;
  501. while (MaskPos <= MaskLen) and (AnsiChar(Mask[MaskPos]) in ['?', '*']) do
  502. Inc(MaskPos);
  503. if (MaskPos <= MaskLen) or (KeyPos <= KeyLen) then
  504. begin
  505. Result := False;
  506. Exit;
  507. end;
  508. Result := True;
  509. end;
  510. begin
  511. MaskLen := Length(Mask);
  512. KeyLen := Length(Subject);
  513. if MaskLen = 0 then
  514. begin
  515. Result := True;
  516. Exit;
  517. end;
  518. Result := MatchAt(1, 1);
  519. end;
  520. function BuildFileList(Path: string; Attr: LongInt;
  521. Files: TStrings; Options: TFileListOptions): Boolean;
  522. var
  523. FileMask: string;
  524. RootDir: string;
  525. Folders: TStringList;
  526. CurrentItem: LongInt;
  527. Counter: LongInt;
  528. LocAttr: LongInt;
  529. procedure BuildFolderList;
  530. var
  531. FindInfo: TSearchRec;
  532. Rslt: LongInt;
  533. begin
  534. Counter := Folders.Count - 1;
  535. CurrentItem := 0;
  536. while CurrentItem <= Counter do
  537. begin
  538. // Searching for subfolders
  539. Rslt := SysUtils.FindFirst(Folders[CurrentItem] + '*', faDirectory, FindInfo);
  540. try
  541. while Rslt = 0 do
  542. begin
  543. if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') and
  544. (FindInfo.Attr and faDirectory = faDirectory) then
  545. Folders.Add(Folders[CurrentItem] + FindInfo.Name + PathDelim);
  546. Rslt := SysUtils.FindNext(FindInfo);
  547. end;
  548. finally
  549. SysUtils.FindClose(FindInfo);
  550. end;
  551. Counter := Folders.Count - 1;
  552. Inc(CurrentItem);
  553. end;
  554. end;
  555. procedure FillFileList(CurrentCounter: LongInt);
  556. var
  557. FindInfo: TSearchRec;
  558. Res: LongInt;
  559. CurrentFolder: string;
  560. begin
  561. CurrentFolder := Folders[CurrentCounter];
  562. Res := SysUtils.FindFirst(CurrentFolder + FileMask, LocAttr, FindInfo);
  563. if flRelNames in Options then
  564. CurrentFolder := ExtractRelativePath(RootDir, CurrentFolder);
  565. try
  566. while Res = 0 do
  567. begin
  568. if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') then
  569. begin
  570. if (flFullNames in Options) or (flRelNames in Options) then
  571. Files.Add(CurrentFolder + FindInfo.Name)
  572. else
  573. Files.Add(FindInfo.Name);
  574. end;
  575. Res := SysUtils.FindNext(FindInfo);
  576. end;
  577. finally
  578. SysUtils.FindClose(FindInfo);
  579. end;
  580. end;
  581. begin
  582. FileMask := ExtractFileName(Path);
  583. RootDir := ExtractFilePath(Path);
  584. Folders := TStringList.Create;
  585. Folders.Add(RootDir);
  586. Files.Clear;
  587. {$IFDEF DCC}
  588. {$WARN SYMBOL_PLATFORM OFF}
  589. {$ENDIF}
  590. if Attr = faAnyFile then
  591. LocAttr := faSysFile or faHidden or faArchive or faReadOnly
  592. else
  593. LocAttr := Attr;
  594. {$IFDEF DCC}
  595. {$WARN SYMBOL_PLATFORM ON}
  596. {$ENDIF}
  597. // Here's the recursive search for nested folders
  598. if flRecursive in Options then
  599. BuildFolderList;
  600. if Attr <> faDirectory then
  601. for Counter := 0 to Folders.Count - 1 do
  602. FillFileList(Counter)
  603. else
  604. Files.AddStrings(Folders);
  605. Folders.Free;
  606. Result := True;
  607. end;
  608. function PosEx(const SubStr, S: string; Offset: LongInt = 1): LongInt;
  609. var
  610. I, X: LongInt;
  611. Len, LenSubStr: LongInt;
  612. begin
  613. I := Offset;
  614. LenSubStr := Length(SubStr);
  615. Len := Length(S) - LenSubStr + 1;
  616. while I <= Len do
  617. begin
  618. if S[I] = SubStr[1] then
  619. begin
  620. X := 1;
  621. while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do
  622. Inc(X);
  623. if (X = LenSubStr) then
  624. begin
  625. Result := I;
  626. Exit;
  627. end;
  628. end;
  629. Inc(I);
  630. end;
  631. Result := 0;
  632. end;
  633. function PosNoCase(const SubStr, S: string; Offset: LongInt): LongInt;
  634. begin
  635. Result := PosEx(AnsiLowerCase(SubStr), AnsiLowerCase(S), Offset);
  636. end;
  637. function StrToken(var S: string; Sep: Char): string;
  638. var
  639. I: LongInt;
  640. begin
  641. I := Pos(Sep, S);
  642. if I <> 0 then
  643. begin
  644. Result := Copy(S, 1, I - 1);
  645. Delete(S, 1, I);
  646. end
  647. else
  648. begin
  649. Result := S;
  650. S := '';
  651. end;
  652. end;
  653. function StrTokenEnd(var S: string; Sep: Char): string;
  654. var
  655. I, J: LongInt;
  656. begin
  657. J := 0;
  658. I := Pos(Sep, S);
  659. while I <> 0 do
  660. begin
  661. J := I;
  662. I := PosEx(Sep, S, J + 1);
  663. end;
  664. if J <> 0 then
  665. begin
  666. Result := Copy(S, J + 1, MaxInt);
  667. Delete(S, J, MaxInt);
  668. end
  669. else
  670. begin
  671. Result := S;
  672. S := '';
  673. end;
  674. end;
  675. procedure StrTokensToList(const S: string; Sep: Char; Tokens: TStrings);
  676. var
  677. Token, Str: string;
  678. begin
  679. Tokens.Clear;
  680. Str := S;
  681. while Str <> '' do
  682. begin
  683. Token := StrToken(Str, Sep);
  684. Tokens.Add(Token);
  685. end;
  686. end;
  687. function IntToStrFmt(const I: Int64): string;
  688. begin
  689. Result := Format('%.0n', [I * 1.0]);
  690. end;
  691. function FloatToStrFmt(const F: Double; Precision: Integer): string;
  692. begin
  693. Result := Format('%.' + IntToStr(Precision) + 'n', [F]);
  694. end;
  695. function GetFormatSettingsForFloats: TFormatSettings;
  696. begin
  697. Result := FloatFormatSettings;
  698. end;
  699. function ContainsAnySubStr(const S: string; const SubStrs: array of string): Boolean;
  700. var
  701. I: Integer;
  702. begin
  703. Result := False;
  704. for I := 0 to High(SubStrs) do
  705. begin
  706. Result := Pos(SubStrs[I], S) > 0;
  707. if Result then
  708. Exit;
  709. end;
  710. end;
  711. function SubString(const S: string; IdxStart, IdxEnd: Integer): string;
  712. begin
  713. Result := Copy(S, IdxStart, IdxEnd - IdxStart);
  714. end;
  715. function ClampInt(Number: LongInt; Min, Max: LongInt): LongInt;
  716. begin
  717. Result := Number;
  718. if Result < Min then
  719. Result := Min
  720. else if Result > Max then
  721. Result := Max;
  722. end;
  723. function ClampFloat(Number: Single; Min, Max: Single): Single;
  724. begin
  725. Result := Number;
  726. if Result < Min then
  727. Result := Min
  728. else if Result > Max then
  729. Result := Max;
  730. end;
  731. function ClampToByte(Value: LongInt): LongInt;
  732. begin
  733. Result := Value;
  734. if Result > 255 then
  735. Result := 255
  736. else if Result < 0 then
  737. Result := 0;
  738. end;
  739. function ClampToWord(Value: LongInt): LongInt;
  740. begin
  741. Result := Value;
  742. if Result > 65535 then
  743. Result := 65535
  744. else if Result < 0 then
  745. Result := 0;
  746. end;
  747. function IsPow2(Num: LongInt): Boolean;
  748. begin
  749. Result := (Num and -Num) = Num;
  750. end;
  751. function NextPow2(Num: LongInt): LongInt;
  752. begin
  753. Result := Num and -Num;
  754. while Result < Num do
  755. Result := Result shl 1;
  756. end;
  757. function Pow2Int(Exponent: LongInt): LongInt;
  758. begin
  759. Result := 1 shl Exponent;
  760. end;
  761. function Power(const Base, Exponent: Single): Single;
  762. begin
  763. if Exponent = 0.0 then
  764. Result := 1.0
  765. else if (Base = 0.0) and (Exponent > 0.0) then
  766. Result := 0.0
  767. else
  768. Result := Exp(Exponent * Ln(Base));
  769. end;
  770. function Log2Int(X: LongInt): LongInt;
  771. begin
  772. case X of
  773. 1: Result := 0;
  774. 2: Result := 1;
  775. 4: Result := 2;
  776. 8: Result := 3;
  777. 16: Result := 4;
  778. 32: Result := 5;
  779. 64: Result := 6;
  780. 128: Result := 7;
  781. 256: Result := 8;
  782. 512: Result := 9;
  783. 1024: Result := 10;
  784. 2048: Result := 11;
  785. 4096: Result := 12;
  786. 8192: Result := 13;
  787. 16384: Result := 14;
  788. 32768: Result := 15;
  789. 65536: Result := 16;
  790. 131072: Result := 17;
  791. 262144: Result := 18;
  792. 524288: Result := 19;
  793. 1048576: Result := 20;
  794. 2097152: Result := 21;
  795. 4194304: Result := 22;
  796. 8388608: Result := 23;
  797. 16777216: Result := 24;
  798. 33554432: Result := 25;
  799. 67108864: Result := 26;
  800. 134217728: Result := 27;
  801. 268435456: Result := 28;
  802. 536870912: Result := 29;
  803. 1073741824: Result := 30;
  804. else
  805. Result := -1;
  806. end;
  807. end;
  808. function Log2(X: Single): Single;
  809. {$IFDEF USE_ASM}
  810. asm
  811. FLD1
  812. FLD X
  813. FYL2X
  814. FWAIT
  815. end;
  816. {$ELSE}
  817. const
  818. Ln2: Single = 0.6931471;
  819. begin
  820. Result := Ln(X) / Ln2;
  821. end;
  822. {$ENDIF}
  823. function Log10(X: Single): Single;
  824. {$IFDEF USE_ASM}
  825. asm
  826. FLDLG2
  827. FLD X
  828. FYL2X
  829. FWAIT
  830. end;
  831. {$ELSE}
  832. const
  833. Ln10: Single = 2.30258509299405;
  834. begin
  835. Result := Ln(X) / Ln10;
  836. end;
  837. {$ENDIF}
  838. function Floor(Value: Single): LongInt;
  839. begin
  840. Result := Trunc(Value);
  841. if Value < Result then
  842. Dec(Result);
  843. end;
  844. function Ceil(Value: Single): LongInt;
  845. begin
  846. Result := Trunc(Value);
  847. if Value > Result then
  848. Inc(Result);
  849. end;
  850. procedure Switch(var Value: Boolean);
  851. begin
  852. Value := not Value;
  853. end;
  854. function Iff(Condition: Boolean; TruePart, FalsePart: LongInt): LongInt;
  855. begin
  856. if Condition then
  857. Result := TruePart
  858. else
  859. Result := FalsePart;
  860. end;
  861. function IffUnsigned(Condition: Boolean; TruePart, FalsePart: LongWord): LongWord;
  862. begin
  863. if Condition then
  864. Result := TruePart
  865. else
  866. Result := FalsePart;
  867. end;
  868. function Iff(Condition, TruePart, FalsePart: Boolean): Boolean;
  869. begin
  870. if Condition then
  871. Result := TruePart
  872. else
  873. Result := FalsePart;
  874. end;
  875. function Iff(Condition: Boolean; const TruePart, FalsePart: string): string;
  876. begin
  877. if Condition then
  878. Result := TruePart
  879. else
  880. Result := FalsePart;
  881. end;
  882. function Iff(Condition: Boolean; TruePart, FalsePart: Char): Char;
  883. begin
  884. if Condition then
  885. Result := TruePart
  886. else
  887. Result := FalsePart;
  888. end;
  889. function Iff(Condition: Boolean; TruePart, FalsePart: Pointer): Pointer;
  890. begin
  891. if Condition then
  892. Result := TruePart
  893. else
  894. Result := FalsePart;
  895. end;
  896. function Iff(Condition: Boolean; const TruePart, FalsePart: Int64): Int64;
  897. begin
  898. if Condition then
  899. Result := TruePart
  900. else
  901. Result := FalsePart;
  902. end;
  903. function IffFloat(Condition: Boolean; TruePart, FalsePart: Single): Single;
  904. begin
  905. if Condition then
  906. Result := TruePart
  907. else
  908. Result := FalsePart;
  909. end;
  910. procedure SwapValues(var A, B: Boolean);
  911. var
  912. Tmp: Boolean;
  913. begin
  914. Tmp := A;
  915. A := B;
  916. B := Tmp;
  917. end;
  918. procedure SwapValues(var A, B: Byte);
  919. var
  920. Tmp: Byte;
  921. begin
  922. Tmp := A;
  923. A := B;
  924. B := Tmp;
  925. end;
  926. procedure SwapValues(var A, B: Word);
  927. var
  928. Tmp: Word;
  929. begin
  930. Tmp := A;
  931. A := B;
  932. B := Tmp;
  933. end;
  934. procedure SwapValues(var A, B: LongInt);
  935. var
  936. Tmp: LongInt;
  937. begin
  938. Tmp := A;
  939. A := B;
  940. B := Tmp;
  941. end;
  942. procedure SwapValues(var A, B: Single);
  943. var
  944. Tmp: Single;
  945. begin
  946. Tmp := A;
  947. A := B;
  948. B := Tmp;
  949. end;
  950. procedure SwapMin(var Min, Max: LongInt);
  951. var
  952. Tmp: LongInt;
  953. begin
  954. if Min > Max then
  955. begin
  956. Tmp := Min;
  957. Min := Max;
  958. Max := Tmp;
  959. end;
  960. end;
  961. function Min(A, B: LongInt): LongInt;
  962. begin
  963. if A < B then
  964. Result := A
  965. else
  966. Result := B;
  967. end;
  968. function MinFloat(A, B: Single): Single;
  969. begin
  970. if A < B then
  971. Result := A
  972. else
  973. Result := B;
  974. end;
  975. function Max(A, B: LongInt): LongInt;
  976. begin
  977. if A > B then
  978. Result := A
  979. else
  980. Result := B;
  981. end;
  982. function MaxFloat(A, B: Single): Single;
  983. begin
  984. if A > B then
  985. Result := A
  986. else
  987. Result := B;
  988. end;
  989. function MaxFloat(const A, B: Double): Double;
  990. begin
  991. if A > B then
  992. Result := A
  993. else
  994. Result := B;
  995. end;
  996. function MulDiv(Number, Numerator, Denominator: Word): Word;
  997. {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
  998. asm
  999. MUL DX
  1000. DIV CX
  1001. end;
  1002. {$ELSE}
  1003. begin
  1004. Result := Number * Numerator div Denominator;
  1005. end;
  1006. {$IFEND}
  1007. function SameFloat(A, B: Single; Delta: Single): Boolean;
  1008. begin
  1009. Result := Abs(A - B) <= Delta;
  1010. end;
  1011. function SameFloat(const A, B: Double; const Delta: Double): Boolean;
  1012. begin
  1013. Result := Abs(A - B) <= Delta;
  1014. end;
  1015. function IsLittleEndian: Boolean;
  1016. var
  1017. W: Word;
  1018. begin
  1019. W := $00FF;
  1020. Result := PByte(@W)^ = $FF;
  1021. end;
  1022. function SwapEndianWord(Value: Word): Word;
  1023. {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
  1024. asm
  1025. XCHG AH, AL
  1026. end;
  1027. {$ELSE}
  1028. begin
  1029. TWordRec(Result).Low := TWordRec(Value).High;
  1030. TWordRec(Result).High := TWordRec(Value).Low;
  1031. end;
  1032. {$IFEND}
  1033. procedure SwapEndianWord(P: PWordArray; Count: LongInt);
  1034. {$IFDEF USE_ASM}
  1035. asm
  1036. @Loop:
  1037. MOV CX, [EAX]
  1038. XCHG CH, CL
  1039. MOV [EAX], CX
  1040. ADD EAX, 2
  1041. DEC EDX
  1042. JNZ @Loop
  1043. end;
  1044. {$ELSE}
  1045. var
  1046. I: LongInt;
  1047. Temp: Word;
  1048. begin
  1049. for I := 0 to Count - 1 do
  1050. begin
  1051. Temp := P[I];
  1052. TWordRec(P[I]).Low := TWordRec(Temp).High;
  1053. TWordRec(P[I]).High := TWordRec(Temp).Low;
  1054. end;
  1055. end;
  1056. {$ENDIF}
  1057. function SwapEndianLongWord(Value: LongWord): LongWord;
  1058. {$IF Defined(USE_ASM) and (not Defined(USE_INLINE))}
  1059. asm
  1060. BSWAP EAX
  1061. end;
  1062. {$ELSE}
  1063. begin
  1064. TLongWordRec(Result).Bytes[0] := TLongWordRec(Value).Bytes[3];
  1065. TLongWordRec(Result).Bytes[1] := TLongWordRec(Value).Bytes[2];
  1066. TLongWordRec(Result).Bytes[2] := TLongWordRec(Value).Bytes[1];
  1067. TLongWordRec(Result).Bytes[3] := TLongWordRec(Value).Bytes[0];
  1068. end;
  1069. {$IFEND}
  1070. procedure SwapEndianLongWord(P: PLongWord; Count: LongInt);
  1071. {$IFDEF USE_ASM}
  1072. asm
  1073. @Loop:
  1074. MOV ECX, [EAX]
  1075. BSWAP ECX
  1076. MOV [EAX], ECX
  1077. ADD EAX, 4
  1078. DEC EDX
  1079. JNZ @Loop
  1080. end;
  1081. {$ELSE}
  1082. var
  1083. I: LongInt;
  1084. Temp: LongWord;
  1085. begin
  1086. for I := 0 to Count - 1 do
  1087. begin
  1088. Temp := PLongWordArray(P)[I];
  1089. TLongWordRec(PLongWordArray(P)[I]).Bytes[0] := TLongWordRec(Temp).Bytes[3];
  1090. TLongWordRec(PLongWordArray(P)[I]).Bytes[1] := TLongWordRec(Temp).Bytes[2];
  1091. TLongWordRec(PLongWordArray(P)[I]).Bytes[2] := TLongWordRec(Temp).Bytes[1];
  1092. TLongWordRec(PLongWordArray(P)[I]).Bytes[3] := TLongWordRec(Temp).Bytes[0];
  1093. end;
  1094. end;
  1095. {$ENDIF}
  1096. type
  1097. TCrcTable = array[Byte] of LongWord;
  1098. var
  1099. CrcTable: TCrcTable;
  1100. procedure InitCrcTable;
  1101. const
  1102. Polynom = $EDB88320;
  1103. var
  1104. I, J: LongInt;
  1105. C: LongWord;
  1106. begin
  1107. for I := 0 to 255 do
  1108. begin
  1109. C := I;
  1110. for J := 0 to 7 do
  1111. begin
  1112. if (C and $01) <> 0 then
  1113. C := Polynom xor (C shr 1)
  1114. else
  1115. C := C shr 1;
  1116. end;
  1117. CrcTable[I] := C;
  1118. end;
  1119. end;
  1120. procedure CalcCrc32(var Crc: LongWord; Data: Pointer; Size: LongInt);
  1121. var
  1122. I: LongInt;
  1123. B: PByte;
  1124. begin
  1125. B := Data;
  1126. for I := 0 to Size - 1 do
  1127. begin
  1128. Crc := (Crc shr 8) xor CrcTable[B^ xor Byte(Crc)];
  1129. Inc(B);
  1130. end
  1131. end;
  1132. procedure FillMemoryByte(Data: Pointer; Size: LongInt; Value: Byte);
  1133. {$IFDEF USE_ASM}
  1134. asm
  1135. PUSH EDI
  1136. MOV EDI, EAX
  1137. MOV EAX, ECX
  1138. MOV AH, AL
  1139. MOV CX, AX
  1140. SHL EAX, 16
  1141. MOV AX, CX
  1142. MOV ECX, EDX
  1143. SAR ECX, 2
  1144. JS @Exit
  1145. REP STOSD
  1146. MOV ECX, EDX
  1147. AND ECX, 3
  1148. REP STOSB
  1149. POP EDI
  1150. @Exit:
  1151. end;
  1152. {$ELSE}
  1153. begin
  1154. FillChar(Data^, Size, Value);
  1155. end;
  1156. {$ENDIF}
  1157. procedure FillMemoryWord(Data: Pointer; Size: LongInt; Value: Word);
  1158. {$IFDEF USE_ASM}
  1159. asm
  1160. PUSH EDI
  1161. PUSH EBX
  1162. MOV EBX, EDX
  1163. MOV EDI, EAX
  1164. MOV EAX, ECX
  1165. MOV CX, AX
  1166. SHL EAX, 16
  1167. MOV AX, CX
  1168. MOV ECX, EDX
  1169. SHR ECX, 2
  1170. JZ @Word
  1171. REP STOSD
  1172. @Word:
  1173. MOV ECX, EBX
  1174. AND ECX, 2
  1175. JZ @Byte
  1176. MOV [EDI], AX
  1177. ADD EDI, 2
  1178. @Byte:
  1179. MOV ECX, EBX
  1180. AND ECX, 1
  1181. JZ @Exit
  1182. MOV [EDI], AL
  1183. @Exit:
  1184. POP EBX
  1185. POP EDI
  1186. end;
  1187. {$ELSE}
  1188. var
  1189. I, V: LongWord;
  1190. begin
  1191. V := Value * $10000 + Value;
  1192. for I := 0 to Size div 4 - 1 do
  1193. PLongWordArray(Data)[I] := V;
  1194. case Size mod 4 of
  1195. 1: PByteArray(Data)[Size - 1] := Lo(Value);
  1196. 2: PWordArray(Data)[Size div 2] := Value;
  1197. 3:
  1198. begin
  1199. PWordArray(Data)[Size div 2 - 1] := Value;
  1200. PByteArray(Data)[Size - 1] := Lo(Value);
  1201. end;
  1202. end;
  1203. end;
  1204. {$ENDIF}
  1205. procedure FillMemoryLongWord(Data: Pointer; Size: LongInt; Value: LongWord);
  1206. {$IFDEF USE_ASM}
  1207. asm
  1208. PUSH EDI
  1209. PUSH EBX
  1210. MOV EBX, EDX
  1211. MOV EDI, EAX
  1212. MOV EAX, ECX
  1213. MOV ECX, EDX
  1214. SHR ECX, 2
  1215. JZ @Word
  1216. REP STOSD
  1217. @Word:
  1218. MOV ECX, EBX
  1219. AND ECX, 2
  1220. JZ @Byte
  1221. MOV [EDI], AX
  1222. ADD EDI, 2
  1223. @Byte:
  1224. MOV ECX, EBX
  1225. AND ECX, 1
  1226. JZ @Exit
  1227. MOV [EDI], AL
  1228. @Exit:
  1229. POP EBX
  1230. POP EDI
  1231. end;
  1232. {$ELSE}
  1233. var
  1234. I: LongInt;
  1235. begin
  1236. for I := 0 to Size div 4 - 1 do
  1237. PLongWordArray(Data)[I] := Value;
  1238. case Size mod 4 of
  1239. 1: PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0];
  1240. 2: PWordArray(Data)[Size div 2] := TLongWordRec(Value).Words[0];
  1241. 3:
  1242. begin
  1243. PWordArray(Data)[Size div 2 - 1] := TLongWordRec(Value).Words[0];
  1244. PByteArray(Data)[Size - 1] := TLongWordRec(Value).Bytes[0];
  1245. end;
  1246. end;
  1247. end;
  1248. {$ENDIF}
  1249. procedure ZeroMemory(Data: Pointer; Size: Integer);
  1250. begin
  1251. FillMemoryByte(Data, Size, 0);
  1252. end;
  1253. function GetNumMipMapLevels(Width, Height: LongInt): LongInt;
  1254. begin
  1255. Result := 0;
  1256. if (Width > 0) and (Height > 0) then
  1257. begin
  1258. Result := 1;
  1259. while (Width <> 1) or (Height <> 1) do
  1260. begin
  1261. Width := Width div 2;
  1262. Height := Height div 2;
  1263. if Width < 1 then Width := 1;
  1264. if Height < 1 then Height := 1;
  1265. Inc(Result);
  1266. end;
  1267. end;
  1268. end;
  1269. function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
  1270. var
  1271. I: LongInt;
  1272. begin
  1273. Result := Depth;
  1274. for I := 1 to MipMaps - 1 do
  1275. Inc(Result, ClampInt(Depth shr I, 1, Depth));
  1276. end;
  1277. function BoundsToRect(X, Y, Width, Height: LongInt): TRect;
  1278. begin
  1279. Result.Left := X;
  1280. Result.Top := Y;
  1281. Result.Right := X + Width;
  1282. Result.Bottom := Y + Height;
  1283. end;
  1284. function BoundsToRect(const R: TRect): TRect;
  1285. begin
  1286. Result.Left := R.Left;
  1287. Result.Top := R.Top;
  1288. Result.Right := R.Left + R.Right;
  1289. Result.Bottom := R.Top + R.Bottom;
  1290. end;
  1291. function RectToBounds(const R: TRect): TRect;
  1292. begin
  1293. Result.Left := R.Left;
  1294. Result.Top := R.Top;
  1295. Result.Right := R.Right - R.Left;
  1296. Result.Bottom := R.Bottom - R.Top;
  1297. end;
  1298. procedure ClipRectBounds(var X, Y, Width, Height: LongInt; const Clip: TRect);
  1299. procedure ClipDim(var AStart, ALength: LongInt; ClipMin, ClipMax: LongInt);
  1300. begin
  1301. if AStart < ClipMin then
  1302. begin
  1303. ALength := ALength - (ClipMin - AStart);
  1304. AStart := ClipMin;
  1305. end;
  1306. if AStart + ALength > ClipMax then ALength := Max(0, ClipMax - AStart);
  1307. end;
  1308. begin
  1309. ClipDim(X, Width, Clip.Left, Clip.Right);
  1310. ClipDim(Y, Height, Clip.Top, Clip.Bottom);
  1311. end;
  1312. procedure ClipCopyBounds(var SrcX, SrcY, Width, Height, DstX, DstY: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  1313. procedure ClipDim(var SrcPos, DstPos, Size: LongInt; SrcClipMax,
  1314. DstClipMin, DstClipMax: LongInt);
  1315. var
  1316. OldDstPos: LongInt;
  1317. Diff: LongInt;
  1318. begin
  1319. OldDstPos := Iff(DstPos < 0, DstPos, 0);
  1320. if DstPos < DstClipMin then
  1321. begin
  1322. Diff := DstClipMin - DstPos;
  1323. Size := Size - Diff;
  1324. SrcPos := SrcPos + Diff;
  1325. DstPos := DstClipMin;
  1326. end;
  1327. if SrcPos < 0 then
  1328. begin
  1329. Size := Size + SrcPos - OldDstPos;
  1330. DstPos := DstPos - SrcPos + OldDstPos;
  1331. SrcPos := 0;
  1332. end;
  1333. if SrcPos + Size > SrcClipMax then Size := SrcClipMax - SrcPos;
  1334. if DstPos + Size > DstClipMax then Size := DstClipMax - DstPos;
  1335. end;
  1336. begin
  1337. ClipDim(SrcX, DstX, Width, SrcImageWidth, DstClip.Left, DstClip.Right);
  1338. ClipDim(SrcY, DstY, Height, SrcImageHeight, DstClip.Top, DstClip.Bottom);
  1339. end;
  1340. procedure ClipStretchBounds(var SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY,
  1341. DstWidth, DstHeight: LongInt; SrcImageWidth, SrcImageHeight: LongInt; const DstClip: TRect);
  1342. procedure ClipDim(var SrcPos, DstPos, SrcSize, DstSize: LongInt; SrcClipMax,
  1343. DstClipMin, DstClipMax: LongInt);
  1344. var
  1345. OldSize: LongInt;
  1346. Diff: LongInt;
  1347. Scale: Single;
  1348. begin
  1349. Scale := DstSize / SrcSize;
  1350. if DstPos < DstClipMin then
  1351. begin
  1352. Diff := DstClipMin - DstPos;
  1353. DstSize := DstSize - Diff;
  1354. SrcPos := SrcPos + Round(Diff / Scale);
  1355. SrcSize := SrcSize - Round(Diff / Scale);
  1356. DstPos := DstClipMin;
  1357. end;
  1358. if SrcPos < 0 then
  1359. begin
  1360. SrcSize := SrcSize + SrcPos;
  1361. DstPos := DstPos - Round(SrcPos * Scale);
  1362. DstSize := DstSize + Round(SrcPos * Scale);
  1363. SrcPos := 0;
  1364. end;
  1365. if SrcPos + SrcSize > SrcClipMax then
  1366. begin
  1367. OldSize := SrcSize;
  1368. SrcSize := SrcClipMax - SrcPos;
  1369. DstSize := Round(DstSize * (SrcSize / OldSize));
  1370. end;
  1371. if DstPos + DstSize > DstClipMax then
  1372. begin
  1373. OldSize := DstSize;
  1374. DstSize := DstClipMax - DstPos;
  1375. SrcSize := Round(SrcSize * (DstSize / OldSize));
  1376. end;
  1377. end;
  1378. begin
  1379. ClipDim(SrcX, DstX, SrcWidth, DstWidth, SrcImageWidth, DstClip.Left, DstClip.Right);
  1380. ClipDim(SrcY, DstY, SrcHeight, DstHeight, SrcImageHeight, DstClip.Top, DstClip.Bottom);
  1381. end;
  1382. function ScaleRectToRect(const SourceRect, TargetRect: TRect): TRect;
  1383. var
  1384. SourceWidth: LongInt;
  1385. SourceHeight: LongInt;
  1386. TargetWidth: LongInt;
  1387. TargetHeight: LongInt;
  1388. ScaledWidth: LongInt;
  1389. ScaledHeight: LongInt;
  1390. begin
  1391. SourceWidth := SourceRect.Right - SourceRect.Left;
  1392. SourceHeight := SourceRect.Bottom - SourceRect.Top;
  1393. TargetWidth := TargetRect.Right - TargetRect.Left;
  1394. TargetHeight := TargetRect.Bottom - TargetRect.Top;
  1395. if SourceWidth * TargetHeight < SourceHeight * TargetWidth then
  1396. begin
  1397. ScaledWidth := (SourceWidth * TargetHeight) div SourceHeight;
  1398. Result := BoundsToRect(TargetRect.Left + ((TargetWidth - ScaledWidth) div 2),
  1399. TargetRect.Top, ScaledWidth, TargetHeight);
  1400. end
  1401. else
  1402. begin
  1403. ScaledHeight := (SourceHeight * TargetWidth) div SourceWidth;
  1404. Result := BoundsToRect(TargetRect.Left, TargetRect.Top + ((TargetHeight - ScaledHeight) div 2),
  1405. TargetWidth, ScaledHeight);
  1406. end;
  1407. end;
  1408. function ScaleSizeToFit(const CurrentSize, MaxSize: Types.TSize): Types.TSize;
  1409. var
  1410. SR, TR, ScaledRect: TRect;
  1411. begin
  1412. SR := Types.Rect(0, 0, CurrentSize.CX, CurrentSize.CY);
  1413. TR := Types.Rect(0, 0, MaxSize.CX, MaxSize.CY);
  1414. ScaledRect := ScaleRectToRect(SR, TR);
  1415. Result.CX := ScaledRect.Right - ScaledRect.Left;
  1416. Result.CY := ScaledRect.Bottom - ScaledRect.Top;
  1417. end;
  1418. function RectWidth(const Rect: TRect): Integer;
  1419. begin
  1420. Result := Rect.Right - Rect.Left;
  1421. end;
  1422. function RectHeight(const Rect: TRect): Integer;
  1423. begin
  1424. Result := Rect.Bottom - Rect.Top;
  1425. end;
  1426. function RectInRect(const R1, R2: TRect): Boolean;
  1427. begin
  1428. Result:=
  1429. (R1.Left >= R2.Left) and
  1430. (R1.Top >= R2.Top) and
  1431. (R1.Right <= R2.Right) and
  1432. (R1.Bottom <= R2.Bottom);
  1433. end;
  1434. function RectIntersects(const R1, R2: TRect): Boolean;
  1435. begin
  1436. Result :=
  1437. not (R1.Left > R2.Right) and
  1438. not (R1.Top > R2.Bottom) and
  1439. not (R1.Right < R2.Left) and
  1440. not (R1.Bottom < R2.Top);
  1441. end;
  1442. function PixelSizeToDpi(SizeInMicroMeters: Single): Single;
  1443. begin
  1444. Result := 25400 / SizeInMicroMeters;
  1445. end;
  1446. function DpiToPixelSize(Dpi: Single): Single;
  1447. begin
  1448. Result := 1e03 / (Dpi / 25.4);
  1449. end;
  1450. function FloatPoint(AX, AY: Single): TFloatPoint;
  1451. begin
  1452. Result.X := AX;
  1453. Result.Y := AY;
  1454. end;
  1455. function FloatRect(ALeft, ATop, ARight, ABottom: Single): TFloatRect;
  1456. begin
  1457. with Result do
  1458. begin
  1459. Left := ALeft;
  1460. Top := ATop;
  1461. Right := ARight;
  1462. Bottom := ABottom;
  1463. end;
  1464. end;
  1465. function FloatRectWidth(const R: TFloatRect): Single;
  1466. begin
  1467. Result := R.Right - R.Left;
  1468. end;
  1469. function FloatRectHeight(const R: TFloatRect): Single;
  1470. begin
  1471. Result := R.Bottom - R.Top;
  1472. end;
  1473. function FloatRectFromRect(const R: TRect): TFloatRect;
  1474. begin
  1475. Result := FloatRect(R.Left, R.Top, R.Right, R.Bottom);
  1476. end;
  1477. function FormatExceptMsg(const Msg: string; const Args: array of const): string;
  1478. begin
  1479. Result := Format(Msg + SLineBreak + 'Message: ' + GetExceptObject.Message, Args);
  1480. end;
  1481. procedure DebugMsg(const Msg: string; const Args: array of const);
  1482. var
  1483. FmtMsg: string;
  1484. begin
  1485. FmtMsg := Format(Msg, Args);
  1486. {$IFDEF MSWINDOWS}
  1487. if IsConsole then
  1488. WriteLn('DebugMsg: ' + FmtMsg)
  1489. else
  1490. MessageBox(GetActiveWindow, PChar(FmtMsg), 'DebugMsg', MB_OK);
  1491. {$ENDIF}
  1492. {$IFDEF UNIX}
  1493. WriteLn('DebugMsg: ' + FmtMsg);
  1494. {$ENDIF}
  1495. {$IFDEF MSDOS}
  1496. WriteLn('DebugMsg: ' + FmtMsg);
  1497. {$ENDIF}
  1498. end;
  1499. initialization
  1500. InitCrcTable;
  1501. {$IFDEF MSWINDOWS}
  1502. QueryPerformanceFrequency(PerfFrequency);
  1503. InvPerfFrequency := 1.0 / PerfFrequency;
  1504. {$ENDIF}
  1505. {$IF Defined(DELPHI)}
  1506. {$IF CompilerVersion >= 23}
  1507. FloatFormatSettings := TFormatSettings.Create('en-US');
  1508. {$ELSE}
  1509. GetLocaleFormatSettings(1033, FloatFormatSettings);
  1510. {$IFEND}
  1511. {$ELSE FPC}
  1512. FloatFormatSettings := DefaultFormatSettings;
  1513. FloatFormatSettings.DecimalSeparator := '.';
  1514. FloatFormatSettings.ThousandSeparator := ',';
  1515. {$IFEND}
  1516. {
  1517. File Notes:
  1518. -- TODOS ----------------------------------------------------
  1519. - nothing now
  1520. -- 0.77.1 ----------------------------------------------------
  1521. - Added GetFileName, GetFileDir, RectWidth, RectHeight function.
  1522. - Added ScaleSizeToFit function.
  1523. - Added ZeroMemory and SwapValues for Booleans.
  1524. - Added Substring function.
  1525. - Renamed MatchFileNameMask to StrMaskMatch (it's for general use not
  1526. just filenames).
  1527. - Delphi XE2 new targets (Win64, OSX32) compatibility changes.
  1528. - Added GetFormatSettingsForFloats function.
  1529. -- 0.26.5 Changes/Bug Fixes -----------------------------------
  1530. - Added Log10 function.
  1531. - Added TFloatRect type and helper functions FloatRect, FloatRectWidth,
  1532. FloatRectHeight.
  1533. - Added string function ContainsAnySubStr.
  1534. - Added functions PixelSizeToDpi, DpiToPixelSize.
  1535. -- 0.26.1 Changes/Bug Fixes -----------------------------------
  1536. - Some formatting changes.
  1537. - Changed some string functions to work with localized strings.
  1538. - ASM version of PosEx had bugs, removed it.
  1539. - Added StrTokensToList function.
  1540. -- 0.25.0 Changes/Bug Fixes -----------------------------------
  1541. - Fixed error in ClipCopyBounds which was causing ... bad clipping!
  1542. -- 0.24.3 Changes/Bug Fixes -----------------------------------
  1543. - Added GetTimeMilliseconds function.
  1544. - Added IntToStrFmt and FloatToStrFmt helper functions.
  1545. -- 0.23 Changes/Bug Fixes -----------------------------------
  1546. - Added RectInRect and RectIntersects functions
  1547. - Added some string utils: StrToken, StrTokenEnd, PosEx, PosNoCase.
  1548. - Moved BuildFileList here from DemoUtils.
  1549. -- 0.21 Changes/Bug Fixes -----------------------------------
  1550. - Moved GetVolumeLevelCount from ImagingDds here.
  1551. - Renamed FillMemory to FillMemoryByte to avoid name collision in C++ Builder.
  1552. - Added Iff function for Char, Pointer, and Int64 types.
  1553. - Added IsLittleEndian function.
  1554. - Added array types for TWordRec, TLongWordRec, and TInt64Rec.
  1555. - Added MatchFileNameMask function.
  1556. -- 0.19 Changes/Bug Fixes -----------------------------------
  1557. - added ScaleRectToRect (thanks to Paul Michell)
  1558. - added BoundsToRect, ClipBounds, ClipCopyBounds, ClipStretchBounds functions
  1559. - added MulDiv function
  1560. - FreeAndNil is not inline anymore - caused AV in one program
  1561. -- 0.17 Changes/Bug Fixes -----------------------------------
  1562. - GetAppExe didn't return absolute path in FreeBSD, fixed
  1563. - added debug message output
  1564. - fixed Unix compatibility issues (thanks to Ales Katona).
  1565. Imaging now compiles in FreeBSD and maybe in other Unixes as well.
  1566. -- 0.15 Changes/Bug Fixes -----------------------------------
  1567. - added some new utility functions
  1568. -- 0.13 Changes/Bug Fixes -----------------------------------
  1569. - added many new utility functions
  1570. - minor change in SwapEndian to avoid range check error
  1571. }
  1572. end.