Stage.Utils.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  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. implementation //--------------------------------------------------------------
  137. var
  138. vSqrt255: TSqrt255Array;
  139. vInvPerformanceCounterFrequency: Double;
  140. vInvPerformanceCounterFrequencyReady: Boolean = False;
  141. vLastProjectTargetName: string;
  142. procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray;
  143. Count: Cardinal);
  144. var
  145. i: Integer;
  146. begin
  147. for i := 0 to Count - 1 do
  148. Dest^[i] := Source^[i];
  149. end;
  150. function RoundUpToPowerOf2(value: Integer): Integer;
  151. begin
  152. Result := 1;
  153. while (Result < value) do
  154. Result := Result shl 1;
  155. end;
  156. function RoundDownToPowerOf2(value: Integer): Integer;
  157. begin
  158. if value > 0 then
  159. begin
  160. Result := 1 shl 30;
  161. while Result > value do
  162. Result := Result shr 1;
  163. end
  164. else
  165. Result := 1;
  166. end;
  167. function IsPowerOf2(value: Integer): Boolean;
  168. begin
  169. Result := (RoundUpToPowerOf2(value) = value);
  170. end;
  171. function ReadCRLFString(aStream: TStream): String;
  172. var
  173. c: Char;
  174. begin
  175. Result := '';
  176. while Copy(Result, Length(Result) - 1, 2) <> #13#10 do
  177. begin
  178. aStream.Read(c, 1);
  179. Result := Result + c;
  180. end;
  181. Result := Copy(Result, 1, Length(Result) - 2);
  182. end;
  183. procedure WriteCRLFString(aStream: TStream; const aString: String);
  184. const
  185. cCRLF: Integer = $0A0D;
  186. begin
  187. with aStream do
  188. begin
  189. Write(aString[1], Length(aString));
  190. Write(cCRLF, 2);
  191. end;
  192. end;
  193. function StrToFloatDef(const strValue: string; defValue: Extended = 0)
  194. : Extended;
  195. begin
  196. if not TryStrToFloat(strValue, Result) then
  197. Result := defValue;
  198. end;
  199. function Str2Float(const S: string): Single;
  200. var
  201. DSeparator: Char;
  202. fs: TFormatSettings;
  203. begin
  204. DSeparator := fs.DecimalSeparator; fs.DecimalSeparator := ',';
  205. try
  206. fs.DecimalSeparator := ',';
  207. if not TryStrToFloat(S, Result, fs) then
  208. begin
  209. fs.DecimalSeparator := '.';
  210. if not TryStrToFloat(S, Result, fs) then
  211. Result := 0;
  212. end;
  213. finally
  214. fs.DecimalSeparator := DSeparator;
  215. end;
  216. end;
  217. //---------------------------------------------------------------------------
  218. function ParseInteger(var p: PChar): Integer;
  219. var
  220. neg: Boolean;
  221. c: Char;
  222. begin
  223. Result := 0;
  224. if p = nil then
  225. Exit;
  226. neg := False;
  227. // skip non-numerics
  228. while not CharInSet(p^, [#0, '0' .. '9', '+', '-']) do
  229. Inc(p);
  230. c := p^;
  231. if c = '+' then
  232. Inc(p)
  233. else if c = '-' then
  234. begin
  235. neg := True;
  236. Inc(p);
  237. end;
  238. // Parse numerics
  239. while True do
  240. begin
  241. c := p^;
  242. if not CharInSet(c, ['0' .. '9']) then
  243. Break;
  244. Result := Result * 10 + Integer(c) - Integer('0');
  245. Inc(p);
  246. end;
  247. if neg then
  248. Result := -Result;
  249. end;
  250. function ParseFloat(var p: PChar): Extended;
  251. var
  252. decimals, expSign, exponent: Integer;
  253. c: Char;
  254. neg: Boolean;
  255. begin
  256. Result := 0;
  257. if p = nil then
  258. Exit;
  259. // skip non-numerics
  260. while not CharInSet(p^, [#0, '0' .. '9', '+', '-']) do
  261. Inc(p);
  262. c := p^;
  263. if c = '+' then
  264. begin
  265. neg := False;
  266. Inc(p);
  267. end
  268. else if c = '-' then
  269. begin
  270. neg := True;
  271. Inc(p);
  272. end
  273. else
  274. neg := False;
  275. // parse numbers
  276. while CharInSet(p^, ['0' .. '9']) do
  277. begin
  278. Result := Result * 10 + (Integer(p^) - Integer('0'));
  279. Inc(p);
  280. end;
  281. // parse dot, then decimals, if any
  282. decimals := 0;
  283. if (p^ = '.') then
  284. begin
  285. Inc(p);
  286. while CharInSet(p^, ['0' .. '9']) do
  287. begin
  288. Result := Result * 10 + (Integer(p^) - Integer('0'));
  289. Inc(p);
  290. Dec(decimals);
  291. end;
  292. end;
  293. // parse exponent, if any
  294. if CharInSet(p^, ['e', 'E']) then
  295. begin
  296. Inc(p);
  297. // parse exponent sign
  298. c := p^;
  299. if c = '-' then
  300. begin
  301. expSign := -1;
  302. Inc(p);
  303. end
  304. else if c = '+' then
  305. begin
  306. expSign := 1;
  307. Inc(p);
  308. end
  309. else
  310. expSign := 1;
  311. // parse exponent
  312. exponent := 0;
  313. while CharInSet(p^, ['0' .. '9']) do
  314. begin
  315. exponent := exponent * 10 + (Integer(p^) - Integer('0'));
  316. Inc(p);
  317. end;
  318. decimals := decimals + expSign * exponent;
  319. end;
  320. if decimals <> 0 then
  321. Result := Result * Exp(decimals * Ln(10));
  322. if neg then
  323. Result := -Result;
  324. end;
  325. procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
  326. var
  327. n: Cardinal;
  328. fs: TStream;
  329. begin
  330. fs := TFileStream.Create(fileName, fmCreate);
  331. try
  332. n := Length(data);
  333. if n > 0 then
  334. fs.Write(data[1], n);
  335. finally
  336. fs.Free;
  337. end;
  338. end;
  339. function LoadAnsiStringFromFile(const fileName: string): AnsiString;
  340. var
  341. n: Cardinal;
  342. fs: TStream;
  343. begin
  344. if FileExists(fileName) then
  345. begin
  346. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  347. try
  348. n := fs.Size;
  349. SetLength(Result, n);
  350. if n > 0 then
  351. fs.Read(Result[1], n);
  352. finally
  353. fs.Free;
  354. end;
  355. end
  356. else
  357. Result := '';
  358. end;
  359. procedure SaveStringToFile(const fileName: string; const data: String);
  360. var
  361. n: Cardinal;
  362. fs: TStream;
  363. begin
  364. fs := TFileStream.Create(fileName, fmCreate);
  365. try
  366. n := Length(data);
  367. if n > 0 then
  368. fs.Write(data[1], n);
  369. finally
  370. fs.Free;
  371. end;
  372. end;
  373. function LoadStringFromFile(const fileName: string): String;
  374. var
  375. n: Cardinal;
  376. fs: TStream;
  377. begin
  378. if FileExists(fileName) then
  379. begin
  380. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  381. try
  382. n := fs.Size;
  383. SetLength(Result, n);
  384. if n > 0 then
  385. fs.Read(Result[1], n);
  386. finally
  387. fs.Free;
  388. end;
  389. end
  390. else
  391. Result := '';
  392. end;
  393. procedure SaveComponentToFile(const Component: TComponent;
  394. const fileName: string; const AsText: Boolean);
  395. var
  396. Stream: TStream;
  397. MemStream: TMemoryStream;
  398. begin
  399. Stream := TFileStream.Create(fileName, fmCreate);
  400. try
  401. if AsText then
  402. begin
  403. MemStream := TMemoryStream.Create;
  404. try
  405. MemStream.WriteComponent(Component);
  406. MemStream.Position := 0;
  407. ObjectBinaryToText(MemStream, Stream);
  408. finally
  409. MemStream.Free;
  410. end;
  411. end
  412. else
  413. Stream.WriteComponent(Component);
  414. finally
  415. Stream.Free;
  416. end;
  417. end;
  418. procedure LoadComponentFromFile(const Component: TComponent;
  419. const fileName: string; const AsText: Boolean = True);
  420. var
  421. Stream: TStream;
  422. MemStream: TMemoryStream;
  423. begin
  424. Stream := TFileStream.Create(fileName, fmOpenRead);
  425. try
  426. if AsText then
  427. begin
  428. MemStream := TMemoryStream.Create;
  429. try
  430. ObjectTextToBinary(Stream, MemStream);
  431. MemStream.Position := 0;
  432. MemStream.ReadComponent(Component);
  433. finally
  434. MemStream.Free;
  435. end;
  436. end
  437. else
  438. Stream.ReadComponent(Component);
  439. finally
  440. Stream.Free;
  441. end;
  442. end;
  443. function SizeOfFile(const fileName: string): Int64;
  444. var
  445. fs: TStream;
  446. begin
  447. if FileExists(fileName) then
  448. begin
  449. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
  450. try
  451. Result := fs.Size;
  452. finally
  453. fs.Free;
  454. end;
  455. end
  456. else
  457. Result := 0;
  458. end;
  459. function GetSqrt255Array: PSqrt255Array;
  460. const
  461. cOneDiv255 = 1 / 255;
  462. var
  463. i: Integer;
  464. begin
  465. if vSqrt255[255] <> 255 then
  466. begin
  467. for i := 0 to 255 do
  468. vSqrt255[i] := Integer(Trunc(255 * Sqrt(i * cOneDiv255)));
  469. end;
  470. Result := @vSqrt255;
  471. end;
  472. function GetCurrentAssetPath(): TFileName;
  473. var
  474. Path: TFileName;
  475. N: Integer;
  476. begin
  477. Path := LowerCase(ExtractFilePath(ParamStr(0)));
  478. N := Pos('examples', Path);
  479. Delete(Path, N, Length(path));
  480. Path := IncludeTrailingPathDelimiter(Path) + 'assets';
  481. SetCurrentDir(Path);
  482. Result := Path;
  483. end;
  484. procedure RaiseLastOSError;
  485. var
  486. e: EGLOSError;
  487. begin
  488. e := EGLOSError.Create('OS Error : ' + SysErrorMessage(GetLastError));
  489. raise e;
  490. end;
  491. function IsSubComponent(const AComponent: TComponent): Boolean;
  492. begin
  493. Result := (csSubComponent in AComponent.ComponentStyle);
  494. end;
  495. procedure MakeSubComponent(const AComponent: TComponent; const value: Boolean);
  496. begin
  497. AComponent.SetSubComponent(value);
  498. end;
  499. function AnsiStartsText(const ASubText, AText: string): Boolean;
  500. begin
  501. Result := AnsiStartsText(ASubText, AText);
  502. end;
  503. procedure ShowHTMLUrl(const Url: string);
  504. begin
  505. ShellExecute(0, 'open', PChar(Url), nil, nil, SW_SHOW);
  506. end;
  507. function GetGLRect(const aLeft, aTop, aRight, aBottom: Integer): TRect;
  508. begin
  509. Result.Left := aLeft;
  510. Result.Top := aTop;
  511. Result.Right := aRight;
  512. Result.Bottom := aBottom;
  513. end;
  514. procedure InflateGLRect(var aRect: TRect; dx, dy: Integer);
  515. begin
  516. aRect.Left := aRect.Left - dx;
  517. aRect.Right := aRect.Right + dx;
  518. if aRect.Right < aRect.Left then
  519. aRect.Right := aRect.Left;
  520. aRect.Top := aRect.Top - dy;
  521. aRect.Bottom := aRect.Bottom + dy;
  522. if aRect.Bottom < aRect.Top then
  523. aRect.Bottom := aRect.Top;
  524. end;
  525. procedure IntersectGLRect(var aRect: TRect; const rect2: TRect);
  526. var
  527. a: Integer;
  528. begin
  529. if (aRect.Left > rect2.Right) or (aRect.Right < rect2.Left) or
  530. (aRect.Top > rect2.Bottom) or (aRect.Bottom < rect2.Top) then
  531. begin
  532. // no intersection
  533. a := 0;
  534. aRect.Left := a;
  535. aRect.Right := a;
  536. aRect.Top := a;
  537. aRect.Bottom := a;
  538. end
  539. else
  540. begin
  541. if aRect.Left < rect2.Left then
  542. aRect.Left := rect2.Left;
  543. if aRect.Right > rect2.Right then
  544. aRect.Right := rect2.Right;
  545. if aRect.Top < rect2.Top then
  546. aRect.Top := rect2.Top;
  547. if aRect.Bottom > rect2.Bottom then
  548. aRect.Bottom := rect2.Bottom;
  549. end;
  550. end;
  551. procedure FixPathDelimiter(var S: string);
  552. var
  553. i: Integer;
  554. begin
  555. for i := Length(S) downto 1 do
  556. if (S[i] = '/') or (S[i] = '\') then
  557. S[i] := PathDelim;
  558. end;
  559. function RelativePath(const S: string): string;
  560. var
  561. path: string;
  562. begin
  563. Result := S;
  564. if IsDesignTime then
  565. begin
  566. if Assigned(vProjectTargetName) then
  567. begin
  568. path := vProjectTargetName();
  569. if Length(path) = 0 then
  570. path := vLastProjectTargetName
  571. else
  572. vLastProjectTargetName := path;
  573. path := IncludeTrailingPathDelimiter(ExtractFilePath(path));
  574. end
  575. else
  576. Exit;
  577. end
  578. else
  579. begin
  580. path := ExtractFilePath(ParamStr(0));
  581. path := IncludeTrailingPathDelimiter(path);
  582. end;
  583. if Pos(path, S) = 1 then
  584. Delete(Result, 1, Length(path));
  585. end;
  586. procedure QueryPerformanceCounter(out val: Int64);
  587. begin
  588. Winapi.Windows.QueryPerformanceCounter(val);
  589. end;
  590. function QueryPerformanceFrequency(out val: Int64): Boolean;
  591. begin
  592. Result := Boolean(Winapi.Windows.QueryPerformanceFrequency(val));
  593. end;
  594. function StartPrecisionTimer: Int64;
  595. begin
  596. QueryPerformanceCounter(Result);
  597. end;
  598. function PrecisionTimerLap(const precisionTimer: Int64): Double;
  599. begin
  600. // we can do this, because we don't really stop anything
  601. Result := StopPrecisionTimer(precisionTimer);
  602. end;
  603. function StopPrecisionTimer(const precisionTimer: Int64): Double;
  604. var
  605. cur, freq: Int64;
  606. begin
  607. QueryPerformanceCounter(cur);
  608. if not vInvPerformanceCounterFrequencyReady then
  609. begin
  610. QueryPerformanceFrequency(freq);
  611. vInvPerformanceCounterFrequency := 1.0 / freq;
  612. vInvPerformanceCounterFrequencyReady := True;
  613. end;
  614. Result := (cur - precisionTimer) * vInvPerformanceCounterFrequency;
  615. end;
  616. var
  617. vSStartTime: TDateTime;
  618. vLastTime: TDateTime;
  619. vDeltaMilliSecond: TDateTime;
  620. function AppTime: Double;
  621. var
  622. SystemTime: TSystemTime;
  623. begin
  624. GetLocalTime(SystemTime);
  625. with SystemTime do
  626. Result := (wHour * (MinsPerHour * SecsPerMin * MSecsPerSec) + wMinute *
  627. (SecsPerMin * MSecsPerSec) + wSecond * MSecsPerSec + wMilliSeconds) -
  628. vSStartTime;
  629. // Hack to fix time precession
  630. if Result - vLastTime = 0 then
  631. begin
  632. Result := Result + vDeltaMilliSecond;
  633. vDeltaMilliSecond := vDeltaMilliSecond + 0.1;
  634. end
  635. else
  636. begin
  637. vLastTime := Result;
  638. vDeltaMilliSecond := 0.1;
  639. end;
  640. end;
  641. function FindUnitName(anObject: TObject): string;
  642. begin
  643. if Assigned(anObject) then
  644. Result := anObject.UnitName
  645. else
  646. Result := '';
  647. end;
  648. function FindUnitName(aClass: TClass): string;
  649. begin
  650. if Assigned(aClass) then
  651. Result := aClass.UnitName
  652. else
  653. Result := '';
  654. end;
  655. procedure SetExeDirectory;
  656. var
  657. path: string;
  658. begin
  659. if IsDesignTime then
  660. begin
  661. if Assigned(vProjectTargetName) then
  662. begin
  663. path := vProjectTargetName();
  664. if Length(path) = 0 then
  665. path := vLastProjectTargetName
  666. else
  667. vLastProjectTargetName := path;
  668. path := IncludeTrailingPathDelimiter(ExtractFilePath(path));
  669. SetCurrentDir(path);
  670. end;
  671. end
  672. else
  673. begin
  674. path := ExtractFilePath(ParamStr(0));
  675. path := IncludeTrailingPathDelimiter(path);
  676. SetCurrentDir(path);
  677. end;
  678. end;
  679. function GetValueFromStringsIndex(const AStrings: TStrings;
  680. const AIndex: Integer): string;
  681. begin
  682. Result := AStrings.ValueFromIndex[AIndex];
  683. end;
  684. function IsDirectoryWriteable(const AName: string): Boolean;
  685. var
  686. LFileName: String;
  687. LHandle: THandle;
  688. begin
  689. LFileName := IncludeTrailingPathDelimiter(AName) + 'chk.tmp';
  690. LHandle := CreateFile(PChar(LFileName), GENERIC_READ or GENERIC_WRITE, 0, nil,
  691. CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
  692. Result := LHandle <> INVALID_HANDLE_VALUE;
  693. if Result then
  694. CloseHandle(LHandle);
  695. end;
  696. function CharToWideChar(const AChar: AnsiChar): WideChar;
  697. var
  698. lResult: PWideChar;
  699. begin
  700. GetMem(lResult, 2);
  701. MultiByteToWideChar(CP_ACP, 0, @AChar, 1, lResult, 2);
  702. Result := lResult^;
  703. FreeMem(lResult, 2);
  704. end;
  705. function HalfToFloat(Half: THalfFloat): Single;
  706. var
  707. Dst, Sign, Mantissa: LongWord;
  708. Exp: LongInt;
  709. begin
  710. // extract sign, exponent, and mantissa from half number
  711. Sign := Half shr 15;
  712. Exp := (Half and $7C00) shr 10;
  713. Mantissa := Half and 1023;
  714. if (Exp > 0) and (Exp < 31) then
  715. begin
  716. // common normalized number
  717. Exp := Exp + (127 - 15);
  718. Mantissa := Mantissa shl 13;
  719. Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
  720. // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
  721. end
  722. else if (Exp = 0) and (Mantissa = 0) then
  723. begin
  724. // zero - preserve sign
  725. Dst := Sign shl 31;
  726. end
  727. else if (Exp = 0) and (Mantissa <> 0) then
  728. begin
  729. // denormalized number - renormalize it
  730. while (Mantissa and $00000400) = 0 do
  731. begin
  732. Mantissa := Mantissa shl 1;
  733. Dec(Exp);
  734. end;
  735. Inc(Exp);
  736. Mantissa := Mantissa and not $00000400;
  737. // now assemble normalized number
  738. Exp := Exp + (127 - 15);
  739. Mantissa := Mantissa shl 13;
  740. Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
  741. // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
  742. end
  743. else if (Exp = 31) and (Mantissa = 0) then
  744. begin
  745. // +/- infinity
  746. Dst := (Sign shl 31) or $7F800000;
  747. end
  748. else // if (Exp = 31) and (Mantisa <> 0) then
  749. begin
  750. // not a number - preserve sign and mantissa
  751. Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
  752. end;
  753. // reinterpret LongWord as Single
  754. Result := PSingle(@Dst)^;
  755. end;
  756. function FloatToHalf(Float: Single): THalfFloat;
  757. var
  758. Src: LongWord;
  759. Sign, Exp, Mantissa: LongInt;
  760. begin
  761. Src := PLongWord(@Float)^;
  762. // extract sign, exponent, and mantissa from Single number
  763. Sign := Src shr 31;
  764. Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15;
  765. Mantissa := Src and $007FFFFF;
  766. if (Exp > 0) and (Exp < 30) then
  767. begin
  768. // simple case - round the significand and combine it with the sign and exponent
  769. Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
  770. end
  771. else if Src = 0 then
  772. begin
  773. // input float is zero - return zero
  774. Result := 0;
  775. end
  776. else
  777. begin
  778. // difficult case - lengthy conversion
  779. if Exp <= 0 then
  780. begin
  781. if Exp < -10 then
  782. begin
  783. // input float's value is less than HalfMin, return zero
  784. Result := 0;
  785. end
  786. else
  787. begin
  788. // Float is a normalized Single whose magnitude is less than HalfNormMin.
  789. // We convert it to denormalized half.
  790. Mantissa := (Mantissa or $00800000) shr (1 - Exp);
  791. // round to nearest
  792. if (Mantissa and $00001000) > 0 then
  793. Mantissa := Mantissa + $00002000;
  794. // assemble Sign and Mantissa (Exp is zero to get denotmalized number)
  795. Result := (Sign shl 15) or (Mantissa shr 13);
  796. end;
  797. end
  798. else if Exp = 255 - 127 + 15 then
  799. begin
  800. if Mantissa = 0 then
  801. begin
  802. // input float is infinity, create infinity half with original sign
  803. Result := (Sign shl 15) or $7C00;
  804. end
  805. else
  806. begin
  807. // input float is NaN, create half NaN with original sign and mantissa
  808. Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
  809. end;
  810. end
  811. else
  812. begin
  813. // Exp is > 0 so input float is normalized Single
  814. // round to nearest
  815. if (Mantissa and $00001000) > 0 then
  816. begin
  817. Mantissa := Mantissa + $00002000;
  818. if (Mantissa and $00800000) > 0 then
  819. begin
  820. Mantissa := 0;
  821. Exp := Exp + 1;
  822. end;
  823. end;
  824. if Exp > 30 then
  825. begin
  826. // exponent overflow - return infinity half
  827. Result := (Sign shl 15) or $7C00;
  828. end
  829. else
  830. // assemble normalized half
  831. Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
  832. end;
  833. end;
  834. end;
  835. // By PAL, added to fix problem with decimal separator in non En configurations
  836. function GLStrToFloatDef(const S: string; const Default: Extended;
  837. fs: TFormatSettings): Extended; overload;
  838. begin
  839. fs.DecimalSeparator := '.';
  840. if not TextToFloat(S, Result, fs) then
  841. Result := Default;
  842. end;
  843. // By PAL, added to fix problem with decimal separator in non En configurations
  844. function GLStrToFloatDef(const S: string; const Default: Extended)
  845. : Extended; overload;
  846. var
  847. fs: TFormatSettings;
  848. begin
  849. fs.DecimalSeparator := '.';
  850. if not TextToFloat(S, Result, fs) then
  851. Result := Default;
  852. end;
  853. // By PAL, added to fix problem with decimal separator in non En configurations
  854. function GLStrToFloatDef(const S: string): Extended; overload;
  855. var
  856. fs: TFormatSettings;
  857. begin
  858. fs.DecimalSeparator := '.';
  859. if not TextToFloat(S, Result, fs) then
  860. Result := 0;
  861. end;
  862. initialization // -----------------------------------------------------------
  863. vSStartTime := AppTime;
  864. end.