2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Miscellaneous support utilities & classes.
8
02/01/13 - Yar - Added SetGLSceneMediaDir
9
07/01/11 - Yar - Added SaveModelDialog, OpenModelDialog
10
04/03/10 - DanB - Now uses CharInSet
11
27/05/09 - DanB - re-added TryStrToFloat, since it ignores user's locale.
12
24/03/09 - DanB - removed TryStrToFloat (exists in SysUtils or GLCrossPlatform already)
13
changed StrToFloatDef to accept only 1 param + now overloaded
14
24/03/09 - DanB - Moved Dialog utilities here from GLCrossPlatform, because
15
they work on all platforms (with FPC)
16
16/10/08 - UweR - corrected typo in TryStringToColorAdvanced parameter
17
16/10/08 - DanB - renamed Save/LoadStringFromFile to Save/LoadAnsiStringFromFile
18
24/03/08 - DaStr - Removed OpenGL1x dependancy
19
Moved TGLMinFilter and TGLMagFilter from GLUtils.pas
20
to GLGraphics.pas (BugTracker ID = 1923844)
21
25/03/07 - DaStr - Replaced StrUtils with GLCrossPlatform
22
23/03/07 - DaStr - Removed compiler warnings caused by
23
SaveComponentToFile and LoadComponentFromFile
24
22/03/07 - DaStr - Added SaveComponentToFile, LoadComponentFromFile
25
07/02/07 - DaStr - Added StringToColorAdvanced() functions
26
05/09/03 - EG - Creation from GLMisc split
36
Classes, SysUtils, types,
37
Graphics, Controls, FileUtil, LazUTF8,
38
LazFileUtils, Dialogs, ExtDlgs,
40
GLVectorGeometry, GLCrossPlatform;
43
EGLUtilsException = class(Exception);
45
TSqrt255Array = array[0..255] of Byte;
46
PSqrt255Array = ^TSqrt255Array;
48
// Copies the values of Source to Dest (converting word values to integer values)
49
procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray; Count: Cardinal);
50
// Round ups to the nearest power of two, value must be positive
51
function RoundUpToPowerOf2(value: Integer): Integer;
52
// Round down to the nearest power of two, value must be strictly positive
53
function RoundDownToPowerOf2(value: Integer): Integer;
54
// Returns True if value is a true power of two
55
function IsPowerOf2(value: Integer): Boolean;
56
{ Read a CRLF terminated string from a stream.
57
The CRLF is NOT in the returned string. }
58
function ReadCRLFString(aStream: TStream): AnsiString;
59
// Write the string and a CRLF in the stream
60
procedure WriteCRLFString(aStream: TStream; const aString: AnsiString);
61
// Similar to SysUtils.TryStrToFloat, but ignores user's locale
62
function TryStrToFloat(const strValue: string; var val: Extended): Boolean;
63
// Similar to SysUtils.StrToFloatDef, but ignores user's locale
64
function StrToFloatDef(const strValue: string; defValue: Extended = 0): Extended;
66
// Converts a string into color
67
function StringToColorAdvancedSafe(const Str: string; const Default: TColor): TColor;
68
// Converts a string into color
69
function TryStringToColorAdvanced(const Str: string; var OutColor: TColor): Boolean;
70
// Converts a string into color
71
function StringToColorAdvanced(const Str: string): TColor;
73
{ Parses the next integer in the string.
74
Initial non-numeric characters are skipper, p is altered, returns 0 if none
75
found. '+' and '-' are acknowledged. }
76
function ParseInteger(var p: PChar): Integer;
77
{ Parses the next integer in the string.
78
Initial non-numeric characters are skipper, p is altered, returns 0 if none
79
found. Both '.' and ',' are accepted as decimal separators. }
80
function ParseFloat(var p: PChar): Extended;
82
{ Saves "data" to "filename". }
83
procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
84
{ Returns the content of "filename". }
85
function LoadAnsiStringFromFile(const fileName: string): AnsiString;
87
{ Saves component to a file. }
88
procedure SaveComponentToFile(const Component: TComponent; const FileName: string; const AsText: Boolean = True);
89
{ Loads component from a file. }
90
procedure LoadComponentFromFile(const Component: TComponent; const FileName: string; const AsText: Boolean = True);
92
{ Returns the size of "filename".
93
Returns 0 (zero) is file does not exists. }
94
function SizeOfFile(const fileName: string): Int64;
96
{ Returns a pointer to an array containing the results of "255*sqrt(i/255)". }
97
function GetSqrt255Array: PSqrt255Array;
99
{ Pops up a simple dialog with msg and an Ok button. }
100
procedure InformationDlg(const msg: string);
101
{ Pops up a simple question dialog with msg and yes/no buttons.
102
Returns True if answer was "yes". }
103
function QuestionDlg(const msg: string): Boolean;
104
{ Posp a simple dialog with a string input. }
105
function InputDlg(const aCaption, aPrompt, aDefault: string): string;
107
{ Pops up a simple save picture dialog. }
108
function SavePictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
109
{ Pops up a simple open picture dialog. }
110
function OpenPictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
112
//procedure SetGLSceneMediaDir();
113
Function SetGLSceneMediaDir:string;
116
//------------------------------------------------------
117
//------------------------------------------------------
118
//------------------------------------------------------
120
//------------------------------------------------------
121
//------------------------------------------------------
122
//------------------------------------------------------
130
vSqrt255: TSqrt255Array;
133
gluInvalidColor = '''%s'' is not a valid color format!';
135
// WordToIntegerArray
137
{$IFNDEF GEOMETRY_NO_ASM}
139
procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray; Count: Cardinal); assembler;
140
// EAX contains Source
160
procedure WordToIntegerArray(Source: PWordArray; Dest: PIntegerArray; Count: Cardinal);
164
for i := 0 to Count - 1 do
165
Dest^[i] := Source^[i];
172
function RoundUpToPowerOf2(value: Integer): Integer;
175
while (Result < value) do
176
Result := Result shl 1;
179
// RoundDownToPowerOf2
182
function RoundDownToPowerOf2(value: Integer): Integer;
187
while Result > value do
188
Result := Result shr 1;
197
function IsPowerOf2(value: Integer): Boolean;
199
Result := (RoundUpToPowerOf2(value) = value);
205
function ReadCRLFString(aStream: TStream): AnsiString;
210
while Copy(Result, Length(Result) - 1, 2) <> #13#10 do
213
Result := Result + c;
215
Result := Copy(Result, 1, Length(Result) - 2);
221
procedure WriteCRLFString(aStream: TStream; const aString: AnsiString);
223
cCRLF: Integer = $0A0D;
227
Write(aString[1], Length(aString));
235
function TryStrToFloat(const strValue: string; var val: Extended): Boolean;
237
i, j, divider, lLen, exponent: Integer;
241
if strValue = '' then
248
lLen := Length(strValue);
249
while (lLen > 0) and (strValue[lLen] = ' ') do
253
for i := 1 to lLen do
262
'0'..'9': v := (v * 10) + Integer(c) - Integer('0');
265
if (divider > lLen) then
273
'-', '+': if i > 1 then
285
for j := i + 1 to lLen do
289
'-', '+': if j <> i + 1 then
294
'0'..'9': exponent := (exponent * 10) + Integer(c) - Integer('0');
300
if strValue[i + 1] <> '-' then
301
exponent := -exponent;
302
exponent := exponent - 1;
304
if divider > lLen then
313
divider := lLen - divider + exponent + 1;
314
if strValue[1] = '-' then
319
v := v * Exp(-divider * Ln(10));
327
function StrToFloatDef(const strValue: string; defValue: Extended = 0): Extended;
329
if not TryStrToFloat(strValue, Result) then
333
// StringToColorAdvancedSafe
336
function StringToColorAdvancedSafe(const Str: string; const Default: TColor): TColor;
338
if not TryStringToColorAdvanced(Str, Result) then
342
// StringToColorAdvanced
345
function StringToColorAdvanced(const Str: string): TColor;
347
if not TryStringToColorAdvanced(Str, Result) then
348
raise EGLUtilsException.CreateResFmt(@gluInvalidColor, [Str]);
351
// TryStringToColorAdvanced
354
function TryStringToColorAdvanced(const Str: string; var OutColor: TColor): Boolean;
362
Val(Temp, I, Code); //to see if it is a number
364
OutColor := TColor(I) //Str = $0000FF
367
if not IdentToColor(Temp, Longint(OutColor)) then //Str = clRed
369
if AnsiStartsText('clr', Temp) then //Str = clrRed
372
if not IdentToColor(Temp, Longint(OutColor)) then
375
else if not IdentToColor('cl' + Temp, Longint(OutColor)) then //Str = Red
384
function ParseInteger(var p: PChar): Integer;
394
while not CharInSet(p^, [#0, '0'..'9', '+', '-']) do
408
if not CharInSet(c, ['0'..'9']) then
410
Result := Result * 10 + Integer(c) - Integer('0');
420
function ParseFloat(var p: PChar): Extended;
422
decimals, expSign, exponent: Integer;
430
while not CharInSet(p^, [#0, '0'..'9', '+', '-']) do
446
while CharInSet(p^, ['0'..'9']) do
448
Result := Result * 10 + (Integer(p^) - Integer('0'));
451
// parse dot, then decimals, if any
456
while CharInSet(p^, ['0'..'9']) do
458
Result := Result * 10 + (Integer(p^) - Integer('0'));
463
// parse exponent, if any
464
if CharInSet(p^, ['e', 'E']) then
467
// parse exponent sign
483
while CharInSet(p^, ['0'..'9']) do
485
exponent := exponent * 10 + (Integer(p^) - Integer('0'));
488
decimals := decimals + expSign * exponent;
490
if decimals <> 0 then
491
Result := Result * Exp(decimals * Ln(10));
499
procedure SaveAnsiStringToFile(const fileName: string; const data: AnsiString);
504
fs := CreateFileStream(fileName, fmCreate);
508
fs.Write(data[1], n);
517
function LoadAnsiStringFromFile(const fileName: string): AnsiString;
522
if FileExists(fileName) then
524
fs := CreateFileStream(fileName, fmOpenRead + fmShareDenyNone);
527
SetLength(Result, n);
529
fs.Read(Result[1], n);
538
// SaveComponentToFile
541
procedure SaveComponentToFile(const Component: TComponent; const FileName: string; const AsText: Boolean);
544
MemStream: TMemoryStream;
546
Stream := CreateFileStream(FileName, fmCreate);
550
MemStream := TMemoryStream.Create;
552
MemStream.WriteComponent(Component);
553
MemStream.Position := 0;
554
ObjectBinaryToText(MemStream, Stream);
560
Stream.WriteComponent(Component);
566
// LoadComponentFromFile
569
procedure LoadComponentFromFile(const Component: TComponent; const FileName: string; const AsText: Boolean = True);
572
MemStream: TMemoryStream;
574
Stream := CreateFileStream(FileName, fmOpenRead);
578
MemStream := TMemoryStream.Create;
580
ObjectTextToBinary(Stream, MemStream);
581
MemStream.Position := 0;
582
MemStream.ReadComponent(Component);
588
Stream.ReadComponent(Component);
597
function SizeOfFile(const fileName: string): Int64;
601
if FileExists(fileName) then
603
fs := CreateFileStream(fileName, fmOpenRead + fmShareDenyNone);
617
function GetSqrt255Array: PSqrt255Array;
619
cOneDiv255 = 1 / 255;
623
if vSqrt255[255] <> 255 then
626
vSqrt255[i] := Integer(Trunc(255 * Sqrt(i * cOneDiv255)));
634
procedure InformationDlg(const msg: string);
642
function QuestionDlg(const msg: string): Boolean;
644
Result := (MessageDlg(msg, mtConfirmation, [mbYes, mbNo], 0) = mrYes);
650
function InputDlg(const aCaption, aPrompt, aDefault: string): string;
652
Result := InputBox(aCaption, aPrompt, aDefault);
658
function SavePictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
660
saveDialog: TSavePictureDialog;
662
saveDialog := TSavePictureDialog.Create(nil);
666
Options := [ofHideReadOnly, ofNoReadOnlyReturn];
669
FileName := aFileName;
672
aFileName := FileName;
682
function OpenPictureDialog(var aFileName: string; const aTitle: string = ''): Boolean;
684
openDialog: TOpenPictureDialog;
686
openDialog := TOpenPictureDialog.Create(nil);
690
Options := [ofHideReadOnly, ofNoReadOnlyReturn];
693
FileName := aFileName;
696
aFileName := FileName;
703
function SetGLSceneMediaDir:string;
710
// We need to lower case path because the functions are case sensitive
711
path := lowercase(ExtractFilePath(ParamStrUTF8(0)));
712
p := Pos('samples', path);
713
Delete(path, p + 7, Length(path));
714
path := IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(path) + 'media');
716
// SetCurrentDirUTF8(path) --> NOT WORKING ON W10 64Bits !
717
// We need to store the result in a global var "MediaPath"
718
// The function SetCurrentDirUTF8 return TRUE but we are always in the application's folder
719
// NB These functions provide from LazFileUtils unit and not from deprecated functions in FileUtils unit.