ImagingUtility.pas 48 KB

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