GLS.Utils.pas 30 KB

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