ImagingUtility.pas 49 KB

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