Stage.Utils.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit Stage.Utils;
  5. (* Miscellaneous support utilities & classes *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.Windows,
  10. Winapi.ShellApi,
  11. System.Classes,
  12. System.SysUtils,
  13. System.UITypes,
  14. Stage.VectorGeometry,
  15. Stage.Strings;
  16. type
  17. THalfFloat = type Word;
  18. PHalfFloat = ^THalfFloat;
  19. EGLOSError = EOSError;
  20. EGLUtilsException = class(Exception);
  21. TSqrt255Array = array [0 .. 255] of Byte;
  22. PSqrt255Array = ^TSqrt255Array;
  23. TProjectTargetNameFunc = function(): string;
  24. const
  25. FONT_CHARS_COUNT = 2024;
  26. var
  27. IsDesignTime: Boolean = False;
  28. vProjectTargetName: TProjectTargetNameFunc;
  29. // Get current path to assets from dir examples
  30. function GetCurrentAssetPath(): TFileName;
  31. // Copies the values of Source to Dest (converting word values to integer values)
  32. procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray;
  33. Count: Cardinal);
  34. // Round ups to the nearest power of two, value must be positive
  35. function RoundUpToPowerOf2(value: Integer): Integer;
  36. // Round down to the nearest power of two, value must be strictly positive
  37. function RoundDownToPowerOf2(value: Integer): Integer;
  38. // Returns True if value is a true power of two
  39. function IsPowerOf2(value: Integer): Boolean;
  40. // Read a CRLF terminated string from a stream. The CRLF is NOT in the returned string.
  41. function ReadCRLFString(aStream: TStream): String;
  42. // Write the string and a CRLF in the stream
  43. procedure WriteCRLFString(aStream: TStream; const aString: String);
  44. // Similar to SysUtils.StrToFloatDef, but ignores user's locale
  45. function StrToFloatDef(const strValue: string; defValue: Extended = 0)
  46. : Extended;
  47. // Trying to read string otherwise using '.' as Decimal Separator
  48. function Str2Float(const S: string): Single;
  49. (* Parses the next integer in the string.
  50. Initial non-numeric characters are skipper, p is altered, returns 0 if none
  51. found. '+' and '-' are acknowledged. *)
  52. function ParseInteger(var p: PChar): Integer;
  53. (* Parses the next integer in the string.
  54. Initial non-numeric characters are skipper, p is altered, returns 0 if none
  55. found. Both '.' and ',' are accepted as decimal separators. *)
  56. function ParseFloat(var p: PChar): Extended;
  57. // Saves ansistring "data" to "filename".
  58. procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
  59. // Returns the ansistring content of "filename".
  60. function LoadAnsiStringFromFile(const fileName: string): AnsiString;
  61. // Saves string "data" to "filename".
  62. procedure SaveStringToFile(const fileName: string; const data: String);
  63. // Returns the string content of "filename".
  64. function LoadStringFromFile(const fileName: string): String;
  65. // Saves component to a file.
  66. procedure SaveComponentToFile(const Component: TComponent;
  67. const fileName: string; const AsText: Boolean = True);
  68. // Loads component from a file.
  69. procedure LoadComponentFromFile(const Component: TComponent;
  70. const fileName: string; const AsText: Boolean = True);
  71. (* Returns the size of "filename".
  72. Returns 0 (zero) is file does not exists. *)
  73. function SizeOfFile(const fileName: string): Int64;
  74. // Returns a pointer to an array containing the results of "255*sqrt(i/255)".
  75. function GetSqrt255Array: PSqrt255Array;
  76. // Rectangle as function
  77. function GetGLRect(const aLeft, aTop, aRight, aBottom: Integer): TRect;
  78. (* Increases or decreases the width and height of the specified rectangle.
  79. Adds dx units to the left and right ends of the rectangle and dy units to
  80. the top and bottom. *)
  81. procedure InflateGLRect(var aRect: TRect; dx, dy: Integer);
  82. procedure IntersectGLRect(var aRect: TRect; const rect2: TRect);
  83. procedure RaiseLastOSError;
  84. // Replace path delimiter to delimiter of the current platform.
  85. procedure FixPathDelimiter(var S: string);
  86. // Remove if possible part of path witch leads to project executable.
  87. function RelativePath(const S: string): string;
  88. (* Returns the current value of the highest-resolution counter.
  89. If the platform has none, should return a value derived from the highest
  90. precision time reference available, avoiding, if possible, timers that
  91. allocate specific system resources. *)
  92. procedure QueryPerformanceCounter(out val: Int64);
  93. (* Returns the frequency of the counter used by QueryPerformanceCounter.
  94. Return value is in ticks per second (Hz), returns False if no precision
  95. counter is available. *)
  96. function QueryPerformanceFrequency(out val: Int64): Boolean;
  97. (* Starts a precision timer.
  98. Returned value should just be considered as 'handle', even if it ain't so.
  99. Default platform implementation is to use QueryPerformanceCounter and
  100. QueryPerformanceFrequency, if higher precision references are available,
  101. they should be used. The timer will and must be stopped/terminated/released
  102. with StopPrecisionTimer. *)
  103. function StartPrecisionTimer: Int64;
  104. // Computes time elapsed since timer start. Return time lap in seconds.
  105. function PrecisionTimerLap(const precisionTimer: Int64): Double;
  106. // Computes time elapsed since timer start and stop timer. Return time lap in seconds.
  107. function StopPrecisionTimer(const precisionTimer: Int64): Double;
  108. // Returns time in milisecond from application start.
  109. function AppTime: Double;
  110. procedure ShowHTMLUrl(const Url: string);
  111. procedure SetExeDirectory;
  112. // StrUtils.pas
  113. function AnsiStartsText(const ASubText, AText: string): Boolean;
  114. // Classes.pas
  115. function IsSubComponent(const AComponent: TComponent): Boolean; inline;
  116. procedure MakeSubComponent(const AComponent: TComponent; const value: Boolean);
  117. function FindUnitName(anObject: TObject): string; overload;
  118. function FindUnitName(aClass: TClass): string; overload;
  119. function FloatToHalf(Float: Single): THalfFloat;
  120. function HalfToFloat(Half: THalfFloat): Single;
  121. function GetValueFromStringsIndex(const AStrings: TStrings;
  122. const AIndex: Integer): string;
  123. // Determine if the directory is writable.
  124. function IsDirectoryWriteable(const AName: string): Boolean;
  125. function CharToWideChar(const AChar: AnsiChar): WideChar;
  126. (*
  127. Added by PAL to fix problem with decimal separator in not En-US configurations
  128. Decimal separator in text descriptions of meshes for import/export is always '.' char
  129. But in System.SysUtils.TextToFloat is Windows char, maybe ',' or others...
  130. *)
  131. function GLStrToFloatDef(const S: string; const Default: Extended;
  132. fs: TFormatSettings): Extended; overload;
  133. function GLStrToFloatDef(const S: string; const Default: Extended)
  134. : Extended; overload;
  135. function GLStrToFloatDef(const S: string): Extended; overload;
  136. //----------------------------------------------------------------------------
  137. implementation
  138. //----------------------------------------------------------------------------
  139. var
  140. vSqrt255: TSqrt255Array;
  141. vInvPerformanceCounterFrequency: Double;
  142. vInvPerformanceCounterFrequencyReady: Boolean = False;
  143. vLastProjectTargetName: string;
  144. procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray;
  145. Count: Cardinal);
  146. var
  147. i: Integer;
  148. begin
  149. for i := 0 to Count - 1 do
  150. Dest^[i] := Source^[i];
  151. end;
  152. function RoundUpToPowerOf2(value: Integer): Integer;
  153. begin
  154. Result := 1;
  155. while (Result < value) do
  156. Result := Result shl 1;
  157. end;
  158. function RoundDownToPowerOf2(value: Integer): Integer;
  159. begin
  160. if value > 0 then
  161. begin
  162. Result := 1 shl 30;
  163. while Result > value do
  164. Result := Result shr 1;
  165. end
  166. else
  167. Result := 1;
  168. end;
  169. function IsPowerOf2(value: Integer): Boolean;
  170. begin
  171. Result := (RoundUpToPowerOf2(value) = value);
  172. end;
  173. function ReadCRLFString(aStream: TStream): String;
  174. var
  175. c: Char;
  176. begin
  177. Result := '';
  178. while Copy(Result, Length(Result) - 1, 2) <> #13#10 do
  179. begin
  180. aStream.Read(c, 1);
  181. Result := Result + c;
  182. end;
  183. Result := Copy(Result, 1, Length(Result) - 2);
  184. end;
  185. procedure WriteCRLFString(aStream: TStream; const aString: String);
  186. const
  187. cCRLF: Integer = $0A0D;
  188. begin
  189. with aStream do
  190. begin
  191. Write(aString[1], Length(aString));
  192. Write(cCRLF, 2);
  193. end;
  194. end;
  195. function StrToFloatDef(const strValue: string; defValue: Extended = 0)
  196. : Extended;
  197. begin
  198. if not TryStrToFloat(strValue, Result) then
  199. Result := defValue;
  200. end;
  201. function Str2Float(const S: string): Single;
  202. var
  203. DSeparator: Char;
  204. fs: TFormatSettings;
  205. begin
  206. DSeparator := fs.DecimalSeparator; fs.DecimalSeparator := ',';
  207. try
  208. fs.DecimalSeparator := ',';
  209. if not TryStrToFloat(S, Result, fs) then
  210. begin
  211. fs.DecimalSeparator := '.';
  212. if not TryStrToFloat(S, Result, fs) then
  213. Result := 0;
  214. end;
  215. finally
  216. fs.DecimalSeparator := DSeparator;
  217. end;
  218. end;
  219. //---------------------------------------------------------------------------
  220. function ParseInteger(var p: PChar): Integer;
  221. var
  222. neg: Boolean;
  223. c: Char;
  224. begin
  225. Result := 0;
  226. if p = nil then
  227. Exit;
  228. neg := False;
  229. // skip non-numerics
  230. while not CharInSet(p^, [#0, '0' .. '9', '+', '-']) do
  231. Inc(p);
  232. c := p^;
  233. if c = '+' then
  234. Inc(p)
  235. else if c = '-' then
  236. begin
  237. neg := True;
  238. Inc(p);
  239. end;
  240. // Parse numerics
  241. while True do
  242. begin
  243. c := p^;
  244. if not CharInSet(c, ['0' .. '9']) then
  245. Break;
  246. Result := Result * 10 + Integer(c) - Integer('0');
  247. Inc(p);
  248. end;
  249. if neg then
  250. Result := -Result;
  251. end;
  252. function ParseFloat(var p: PChar): Extended;
  253. var
  254. decimals, expSign, exponent: Integer;
  255. c: Char;
  256. neg: Boolean;
  257. begin
  258. Result := 0;
  259. if p = nil then
  260. Exit;
  261. // skip non-numerics
  262. while not CharInSet(p^, [#0, '0' .. '9', '+', '-']) do
  263. Inc(p);
  264. c := p^;
  265. if c = '+' then
  266. begin
  267. neg := False;
  268. Inc(p);
  269. end
  270. else if c = '-' then
  271. begin
  272. neg := True;
  273. Inc(p);
  274. end
  275. else
  276. neg := False;
  277. // parse numbers
  278. while CharInSet(p^, ['0' .. '9']) do
  279. begin
  280. Result := Result * 10 + (Integer(p^) - Integer('0'));
  281. Inc(p);
  282. end;
  283. // parse dot, then decimals, if any
  284. decimals := 0;
  285. if (p^ = '.') then
  286. begin
  287. Inc(p);
  288. while CharInSet(p^, ['0' .. '9']) do
  289. begin
  290. Result := Result * 10 + (Integer(p^) - Integer('0'));
  291. Inc(p);
  292. Dec(decimals);
  293. end;
  294. end;
  295. // parse exponent, if any
  296. if CharInSet(p^, ['e', 'E']) then
  297. begin
  298. Inc(p);
  299. // parse exponent sign
  300. c := p^;
  301. if c = '-' then
  302. begin
  303. expSign := -1;
  304. Inc(p);
  305. end
  306. else if c = '+' then
  307. begin
  308. expSign := 1;
  309. Inc(p);
  310. end
  311. else
  312. expSign := 1;
  313. // parse exponent
  314. exponent := 0;
  315. while CharInSet(p^, ['0' .. '9']) do
  316. begin
  317. exponent := exponent * 10 + (Integer(p^) - Integer('0'));
  318. Inc(p);
  319. end;
  320. decimals := decimals + expSign * exponent;
  321. end;
  322. if decimals <> 0 then
  323. Result := Result * Exp(decimals * Ln(10));
  324. if neg then
  325. Result := -Result;
  326. end;
  327. procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
  328. var
  329. n: Cardinal;
  330. fs: TStream;
  331. begin
  332. fs := TFileStream.Create(fileName, fmCreate);
  333. try
  334. n := Length(data);
  335. if n > 0 then
  336. fs.Write(data[1], n);
  337. finally
  338. fs.Free;
  339. end;
  340. end;
  341. function LoadAnsiStringFromFile(const fileName: string): AnsiString;
  342. var
  343. n: Cardinal;
  344. fs: TStream;
  345. begin
  346. if FileExists(fileName) then
  347. begin
  348. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  349. try
  350. n := fs.Size;
  351. SetLength(Result, n);
  352. if n > 0 then
  353. fs.Read(Result[1], n);
  354. finally
  355. fs.Free;
  356. end;
  357. end
  358. else
  359. Result := '';
  360. end;
  361. procedure SaveStringToFile(const fileName: string; const data: String);
  362. var
  363. n: Cardinal;
  364. fs: TStream;
  365. begin
  366. fs := TFileStream.Create(fileName, fmCreate);
  367. try
  368. n := Length(data);
  369. if n > 0 then
  370. fs.Write(data[1], n);
  371. finally
  372. fs.Free;
  373. end;
  374. end;
  375. function LoadStringFromFile(const fileName: string): String;
  376. var
  377. n: Cardinal;
  378. fs: TStream;
  379. begin
  380. if FileExists(fileName) then
  381. begin
  382. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  383. try
  384. n := fs.Size;
  385. SetLength(Result, n);
  386. if n > 0 then
  387. fs.Read(Result[1], n);
  388. finally
  389. fs.Free;
  390. end;
  391. end
  392. else
  393. Result := '';
  394. end;
  395. procedure SaveComponentToFile(const Component: TComponent;
  396. const fileName: string; const AsText: Boolean);
  397. var
  398. Stream: TStream;
  399. MemStream: TMemoryStream;
  400. begin
  401. Stream := TFileStream.Create(fileName, fmCreate);
  402. try
  403. if AsText then
  404. begin
  405. MemStream := TMemoryStream.Create;
  406. try
  407. MemStream.WriteComponent(Component);
  408. MemStream.Position := 0;
  409. ObjectBinaryToText(MemStream, Stream);
  410. finally
  411. MemStream.Free;
  412. end;
  413. end
  414. else
  415. Stream.WriteComponent(Component);
  416. finally
  417. Stream.Free;
  418. end;
  419. end;
  420. procedure LoadComponentFromFile(const Component: TComponent;
  421. const fileName: string; const AsText: Boolean = True);
  422. var
  423. Stream: TStream;
  424. MemStream: TMemoryStream;
  425. begin
  426. Stream := TFileStream.Create(fileName, fmOpenRead);
  427. try
  428. if AsText then
  429. begin
  430. MemStream := TMemoryStream.Create;
  431. try
  432. ObjectTextToBinary(Stream, MemStream);
  433. MemStream.Position := 0;
  434. MemStream.ReadComponent(Component);
  435. finally
  436. MemStream.Free;
  437. end;
  438. end
  439. else
  440. Stream.ReadComponent(Component);
  441. finally
  442. Stream.Free;
  443. end;
  444. end;
  445. function SizeOfFile(const fileName: string): Int64;
  446. var
  447. fs: TStream;
  448. begin
  449. if FileExists(fileName) then
  450. begin
  451. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  452. try
  453. Result := fs.Size;
  454. finally
  455. fs.Free;
  456. end;
  457. end
  458. else
  459. Result := 0;
  460. end;
  461. function GetSqrt255Array: PSqrt255Array;
  462. const
  463. cOneDiv255 = 1 / 255;
  464. var
  465. i: Integer;
  466. begin
  467. if vSqrt255[255] <> 255 then
  468. begin
  469. for i := 0 to 255 do
  470. vSqrt255[i] := Integer(Trunc(255 * Sqrt(i * cOneDiv255)));
  471. end;
  472. Result := @vSqrt255;
  473. end;
  474. function GetCurrentAssetPath(): TFileName;
  475. var
  476. Path: TFileName;
  477. N: Integer;
  478. begin
  479. Path := LowerCase(ExtractFilePath(ParamStr(0)));
  480. N := Pos('examples', Path); // if 'glscene' dirname then N + 7
  481. Delete(Path, N, Length(path));
  482. Path := IncludeTrailingPathDelimiter(Path) + 'assets';
  483. SetCurrentDir(Path);
  484. Result := Path;
  485. end;
  486. procedure RaiseLastOSError;
  487. var
  488. e: EGLOSError;
  489. begin
  490. e := EGLOSError.Create('OS Error : ' + SysErrorMessage(GetLastError));
  491. raise e;
  492. end;
  493. function IsSubComponent(const AComponent: TComponent): Boolean;
  494. begin
  495. Result := (csSubComponent in AComponent.ComponentStyle);
  496. end;
  497. procedure MakeSubComponent(const AComponent: TComponent; const value: Boolean);
  498. begin
  499. AComponent.SetSubComponent(value);
  500. end;
  501. function AnsiStartsText(const ASubText, AText: string): Boolean;
  502. begin
  503. Result := AnsiStartsText(ASubText, AText);
  504. end;
  505. procedure ShowHTMLUrl(const Url: string);
  506. begin
  507. ShellExecute(0, 'open', PChar(Url), nil, nil, SW_SHOW);
  508. end;
  509. function GetGLRect(const aLeft, aTop, aRight, aBottom: Integer): TRect;
  510. begin
  511. Result.Left := aLeft;
  512. Result.Top := aTop;
  513. Result.Right := aRight;
  514. Result.Bottom := aBottom;
  515. end;
  516. procedure InflateGLRect(var aRect: TRect; dx, dy: Integer);
  517. begin
  518. aRect.Left := aRect.Left - dx;
  519. aRect.Right := aRect.Right + dx;
  520. if aRect.Right < aRect.Left then
  521. aRect.Right := aRect.Left;
  522. aRect.Top := aRect.Top - dy;
  523. aRect.Bottom := aRect.Bottom + dy;
  524. if aRect.Bottom < aRect.Top then
  525. aRect.Bottom := aRect.Top;
  526. end;
  527. procedure IntersectGLRect(var aRect: TRect; const rect2: TRect);
  528. var
  529. a: Integer;
  530. begin
  531. if (aRect.Left > rect2.Right) or (aRect.Right < rect2.Left) or
  532. (aRect.Top > rect2.Bottom) or (aRect.Bottom < rect2.Top) then
  533. begin
  534. // no intersection
  535. a := 0;
  536. aRect.Left := a;
  537. aRect.Right := a;
  538. aRect.Top := a;
  539. aRect.Bottom := a;
  540. end
  541. else
  542. begin
  543. if aRect.Left < rect2.Left then
  544. aRect.Left := rect2.Left;
  545. if aRect.Right > rect2.Right then
  546. aRect.Right := rect2.Right;
  547. if aRect.Top < rect2.Top then
  548. aRect.Top := rect2.Top;
  549. if aRect.Bottom > rect2.Bottom then
  550. aRect.Bottom := rect2.Bottom;
  551. end;
  552. end;
  553. procedure FixPathDelimiter(var S: string);
  554. var
  555. i: Integer;
  556. begin
  557. for i := Length(S) downto 1 do
  558. if (S[i] = '/') or (S[i] = '\') then
  559. S[i] := PathDelim;
  560. end;
  561. function RelativePath(const S: string): string;
  562. var
  563. path: string;
  564. begin
  565. Result := S;
  566. if IsDesignTime then
  567. begin
  568. if Assigned(vProjectTargetName) then
  569. begin
  570. path := vProjectTargetName();
  571. if Length(path) = 0 then
  572. path := vLastProjectTargetName
  573. else
  574. vLastProjectTargetName := path;
  575. path := IncludeTrailingPathDelimiter(ExtractFilePath(path));
  576. end
  577. else
  578. Exit;
  579. end
  580. else
  581. begin
  582. path := ExtractFilePath(ParamStr(0));
  583. path := IncludeTrailingPathDelimiter(path);
  584. end;
  585. if Pos(path, S) = 1 then
  586. Delete(Result, 1, Length(path));
  587. end;
  588. procedure QueryPerformanceCounter(out val: Int64);
  589. begin
  590. Winapi.Windows.QueryPerformanceCounter(val);
  591. end;
  592. function QueryPerformanceFrequency(out val: Int64): Boolean;
  593. begin
  594. Result := Boolean(Winapi.Windows.QueryPerformanceFrequency(val));
  595. end;
  596. function StartPrecisionTimer: Int64;
  597. begin
  598. QueryPerformanceCounter(Result);
  599. end;
  600. function PrecisionTimerLap(const precisionTimer: Int64): Double;
  601. begin
  602. // we can do this, because we don't really stop anything
  603. Result := StopPrecisionTimer(precisionTimer);
  604. end;
  605. function StopPrecisionTimer(const precisionTimer: Int64): Double;
  606. var
  607. cur, freq: Int64;
  608. begin
  609. QueryPerformanceCounter(cur);
  610. if not vInvPerformanceCounterFrequencyReady then
  611. begin
  612. QueryPerformanceFrequency(freq);
  613. vInvPerformanceCounterFrequency := 1.0 / freq;
  614. vInvPerformanceCounterFrequencyReady := True;
  615. end;
  616. Result := (cur - precisionTimer) * vInvPerformanceCounterFrequency;
  617. end;
  618. var
  619. vSStartTime: TDateTime;
  620. vLastTime: TDateTime;
  621. vDeltaMilliSecond: TDateTime;
  622. function AppTime: Double;
  623. var
  624. SystemTime: TSystemTime;
  625. begin
  626. GetLocalTime(SystemTime);
  627. with SystemTime do
  628. Result := (wHour * (MinsPerHour * SecsPerMin * MSecsPerSec) + wMinute *
  629. (SecsPerMin * MSecsPerSec) + wSecond * MSecsPerSec + wMilliSeconds) -
  630. vSStartTime;
  631. // Hack to fix time precession
  632. if Result - vLastTime = 0 then
  633. begin
  634. Result := Result + vDeltaMilliSecond;
  635. vDeltaMilliSecond := vDeltaMilliSecond + 0.1;
  636. end
  637. else
  638. begin
  639. vLastTime := Result;
  640. vDeltaMilliSecond := 0.1;
  641. end;
  642. end;
  643. function FindUnitName(anObject: TObject): string;
  644. begin
  645. if Assigned(anObject) then
  646. Result := anObject.UnitName
  647. else
  648. Result := '';
  649. end;
  650. function FindUnitName(aClass: TClass): string;
  651. begin
  652. if Assigned(aClass) then
  653. Result := aClass.UnitName
  654. else
  655. Result := '';
  656. end;
  657. procedure SetExeDirectory;
  658. var
  659. path: string;
  660. begin
  661. if IsDesignTime then
  662. begin
  663. if Assigned(vProjectTargetName) then
  664. begin
  665. path := vProjectTargetName();
  666. if Length(path) = 0 then
  667. path := vLastProjectTargetName
  668. else
  669. vLastProjectTargetName := path;
  670. path := IncludeTrailingPathDelimiter(ExtractFilePath(path));
  671. SetCurrentDir(path);
  672. end;
  673. end
  674. else
  675. begin
  676. path := ExtractFilePath(ParamStr(0));
  677. path := IncludeTrailingPathDelimiter(path);
  678. SetCurrentDir(path);
  679. end;
  680. end;
  681. function GetValueFromStringsIndex(const AStrings: TStrings;
  682. const AIndex: Integer): string;
  683. begin
  684. Result := AStrings.ValueFromIndex[AIndex];
  685. end;
  686. function IsDirectoryWriteable(const AName: string): Boolean;
  687. var
  688. LFileName: String;
  689. LHandle: THandle;
  690. begin
  691. LFileName := IncludeTrailingPathDelimiter(AName) + 'chk.tmp';
  692. LHandle := CreateFile(PChar(LFileName), GENERIC_READ or GENERIC_WRITE, 0, nil,
  693. CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
  694. Result := LHandle <> INVALID_HANDLE_VALUE;
  695. if Result then
  696. CloseHandle(LHandle);
  697. end;
  698. function CharToWideChar(const AChar: AnsiChar): WideChar;
  699. var
  700. lResult: PWideChar;
  701. begin
  702. GetMem(lResult, 2);
  703. MultiByteToWideChar(CP_ACP, 0, @AChar, 1, lResult, 2);
  704. Result := lResult^;
  705. FreeMem(lResult, 2);
  706. end;
  707. function HalfToFloat(Half: THalfFloat): Single;
  708. var
  709. Dst, Sign, Mantissa: LongWord;
  710. Exp: LongInt;
  711. begin
  712. // extract sign, exponent, and mantissa from half number
  713. Sign := Half shr 15;
  714. Exp := (Half and $7C00) shr 10;
  715. Mantissa := Half and 1023;
  716. if (Exp > 0) and (Exp < 31) then
  717. begin
  718. // common normalized number
  719. Exp := Exp + (127 - 15);
  720. Mantissa := Mantissa shl 13;
  721. Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
  722. // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
  723. end
  724. else if (Exp = 0) and (Mantissa = 0) then
  725. begin
  726. // zero - preserve sign
  727. Dst := Sign shl 31;
  728. end
  729. else if (Exp = 0) and (Mantissa <> 0) then
  730. begin
  731. // denormalized number - renormalize it
  732. while (Mantissa and $00000400) = 0 do
  733. begin
  734. Mantissa := Mantissa shl 1;
  735. Dec(Exp);
  736. end;
  737. Inc(Exp);
  738. Mantissa := Mantissa and not $00000400;
  739. // now assemble normalized number
  740. Exp := Exp + (127 - 15);
  741. Mantissa := Mantissa shl 13;
  742. Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
  743. // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
  744. end
  745. else if (Exp = 31) and (Mantissa = 0) then
  746. begin
  747. // +/- infinity
  748. Dst := (Sign shl 31) or $7F800000;
  749. end
  750. else // if (Exp = 31) and (Mantisa <> 0) then
  751. begin
  752. // not a number - preserve sign and mantissa
  753. Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
  754. end;
  755. // reinterpret LongWord as Single
  756. Result := PSingle(@Dst)^;
  757. end;
  758. function FloatToHalf(Float: Single): THalfFloat;
  759. var
  760. Src: LongWord;
  761. Sign, Exp, Mantissa: LongInt;
  762. begin
  763. Src := PLongWord(@Float)^;
  764. // extract sign, exponent, and mantissa from Single number
  765. Sign := Src shr 31;
  766. Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15;
  767. Mantissa := Src and $007FFFFF;
  768. if (Exp > 0) and (Exp < 30) then
  769. begin
  770. // simple case - round the significand and combine it with the sign and exponent
  771. Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
  772. end
  773. else if Src = 0 then
  774. begin
  775. // input float is zero - return zero
  776. Result := 0;
  777. end
  778. else
  779. begin
  780. // difficult case - lengthy conversion
  781. if Exp <= 0 then
  782. begin
  783. if Exp < -10 then
  784. begin
  785. // input float's value is less than HalfMin, return zero
  786. Result := 0;
  787. end
  788. else
  789. begin
  790. // Float is a normalized Single whose magnitude is less than HalfNormMin.
  791. // We convert it to denormalized half.
  792. Mantissa := (Mantissa or $00800000) shr (1 - Exp);
  793. // round to nearest
  794. if (Mantissa and $00001000) > 0 then
  795. Mantissa := Mantissa + $00002000;
  796. // assemble Sign and Mantissa (Exp is zero to get denotmalized number)
  797. Result := (Sign shl 15) or (Mantissa shr 13);
  798. end;
  799. end
  800. else if Exp = 255 - 127 + 15 then
  801. begin
  802. if Mantissa = 0 then
  803. begin
  804. // input float is infinity, create infinity half with original sign
  805. Result := (Sign shl 15) or $7C00;
  806. end
  807. else
  808. begin
  809. // input float is NaN, create half NaN with original sign and mantissa
  810. Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
  811. end;
  812. end
  813. else
  814. begin
  815. // Exp is > 0 so input float is normalized Single
  816. // round to nearest
  817. if (Mantissa and $00001000) > 0 then
  818. begin
  819. Mantissa := Mantissa + $00002000;
  820. if (Mantissa and $00800000) > 0 then
  821. begin
  822. Mantissa := 0;
  823. Exp := Exp + 1;
  824. end;
  825. end;
  826. if Exp > 30 then
  827. begin
  828. // exponent overflow - return infinity half
  829. Result := (Sign shl 15) or $7C00;
  830. end
  831. else
  832. // assemble normalized half
  833. Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
  834. end;
  835. end;
  836. end;
  837. // By PAL, added to fix problem with decimal separator in non En configurations
  838. function GLStrToFloatDef(const S: string; const Default: Extended;
  839. fs: TFormatSettings): Extended; overload;
  840. begin
  841. fs.DecimalSeparator := '.';
  842. if not TextToFloat(S, Result, fs) then
  843. Result := Default;
  844. end;
  845. // By PAL, added to fix problem with decimal separator in non En configurations
  846. function GLStrToFloatDef(const S: string; const Default: Extended)
  847. : Extended; overload;
  848. var
  849. fs: TFormatSettings;
  850. begin
  851. fs.DecimalSeparator := '.';
  852. if not TextToFloat(S, Result, fs) then
  853. Result := Default;
  854. end;
  855. // By PAL, added to fix problem with decimal separator in non En configurations
  856. function GLStrToFloatDef(const S: string): Extended; overload;
  857. var
  858. fs: TFormatSettings;
  859. begin
  860. fs.DecimalSeparator := '.';
  861. if not TextToFloat(S, Result, fs) then
  862. Result := 0;
  863. end;
  864. initialization // -----------------------------------------------------------
  865. vSStartTime := AppTime;
  866. end.