| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- //
- // This unit is part of the GLScene Engine, http://glscene.org
- //
- unit GLCrossPlatform;
- (* Cross platform support functions and types for GLScene *)
- interface
- {$I GLScene.inc}
- uses
- Windows,
- System.Types,
- System.Classes,
- System.SysUtils,
- System.StrUtils,
- VCL.Consts,
- VCL.Graphics,
- VCL.Controls,
- VCL.Forms,
- VCL.Dialogs;
- type
- THalfFloat = type Word;
- PHalfFloat = ^THalfFloat;
- TGLMouseEvent = procedure(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer) of object;
- EGLOSError = EOSError;
- TGLComponent = class(TComponent);
- TProjectTargetNameFunc = function(): string;
- const
- FONT_CHARS_COUNT = 2024;
- var
- IsDesignTime: Boolean = False;
- vProjectTargetName: TProjectTargetNameFunc;
- function GetGLRect(const aLeft, aTop, aRight, aBottom: Integer): TRect;
- (* Increases or decreases the width and height of the specified rectangle.
- Adds dx units to the left and right ends of the rectangle and dy units to
- the top and bottom. *)
- procedure InflateGLRect(var aRect: TRect; dx, dy: Integer);
- procedure IntersectGLRect(var aRect: TRect; const rect2: TRect);
- procedure RaiseLastOSError;
- (* Number of pixels per logical inch along the screen width for the device.
- Under Win32 awaits a HDC and returns its LOGPIXELSX. *)
- function GetDeviceLogicalPixelsX(device: HDC): Integer;
- // Number of bits per pixel for the current desktop resolution.
- function GetCurrentColorDepth: Integer;
- // Returns the number of color bits associated to the given pixel format.
- function PixelFormatToColorBits(aPixelFormat: TPixelFormat): Integer;
- // Replace path delimiter to delimiter of the current platform.
- procedure FixPathDelimiter(var S: string);
- // Remove if possible part of path witch leads to project executable.
- function RelativePath(const S: string): string;
- (* Returns the current value of the highest-resolution counter.
- If the platform has none, should return a value derived from the highest
- precision time reference available, avoiding, if possible, timers that
- allocate specific system resources. *)
- procedure QueryPerformanceCounter(out val: Int64);
- (* Returns the frequency of the counter used by QueryPerformanceCounter.
- Return value is in ticks per second (Hz), returns False if no precision
- counter is available. *)
- function QueryPerformanceFrequency(out val: Int64): Boolean;
- (* Starts a precision timer.
- Returned value should just be considered as 'handle', even if it ain't so.
- Default platform implementation is to use QueryPerformanceCounter and
- QueryPerformanceFrequency, if higher precision references are available,
- they should be used. The timer will and must be stopped/terminated/released
- with StopPrecisionTimer. *)
- function StartPrecisionTimer: Int64;
- // Computes time elapsed since timer start. Return time lap in seconds.
- function PrecisionTimerLap(const precisionTimer: Int64): Double;
- // Computes time elapsed since timer start and stop timer. Return time lap in seconds.
- function StopPrecisionTimer(const precisionTimer: Int64): Double;
- // Returns time in milisecond from application start.
- function AppTime: Double;
- // Returns the number of CPU cycles since startup. Use the similarly named CPU instruction.
- function GLOKMessageBox(const Text, Caption: string): Integer;
- procedure GLLoadBitmapFromInstance(Instance: LongInt; ABitmap: TBitmap; const AName: string);
- procedure ShowHTMLUrl(const Url: string);
- procedure SetExeDirectory;
- // StrUtils.pas
- function AnsiStartsText(const ASubText, AText: string): Boolean;
- // Classes.pas
- function IsSubComponent(const AComponent: TComponent): Boolean; inline;
- procedure MakeSubComponent(const AComponent: TComponent; const Value: Boolean);
- function FindUnitName(anObject: TObject): string; overload;
- function FindUnitName(aClass: TClass): string; overload;
- function FloatToHalf(Float: Single): THalfFloat;
- function HalfToFloat(Half: THalfFloat): Single;
- function GetValueFromStringsIndex(const AStrings: TStrings; const AIndex: Integer): string;
- // Determine if the directory is writable.
- function IsDirectoryWriteable(const AName: string): Boolean;
- function CharToWideChar(const AChar: AnsiChar): WideChar;
- //-----------------------------------------------------------
- implementation
- //-----------------------------------------------------------
- uses
- ShellApi;
- var
- vInvPerformanceCounterFrequency: Double;
- vInvPerformanceCounterFrequencyReady: Boolean = False;
- vLastProjectTargetName: string;
- function IsSubComponent(const AComponent: TComponent): Boolean;
- begin
- Result := (csSubComponent in AComponent.ComponentStyle);
- end;
- procedure MakeSubComponent(const AComponent: TComponent; const Value: Boolean);
- begin
- AComponent.SetSubComponent(Value);
- end;
- function AnsiStartsText(const ASubText, AText: string): Boolean;
- begin
- Result := AnsiStartsText(ASubText, AText);
- end;
- function GLOKMessageBox(const Text, Caption: string): Integer;
- begin
- Result := Application.MessageBox(PChar(Text), PChar(Caption), MB_OK);
- end;
- procedure GLLoadBitmapFromInstance(Instance: LongInt; ABitmap: TBitmap; const AName: string);
- begin
- ABitmap.Handle := LoadBitmap(Instance, PChar(AName));
- end;
- procedure ShowHTMLUrl(const Url: string);
- begin
- ShellExecute(0, 'open', PChar(Url), nil, nil, SW_SHOW);
- end;
- function GetGLRect(const aLeft, aTop, aRight, aBottom: Integer): TRect;
- begin
- Result.Left := aLeft;
- Result.Top := aTop;
- Result.Right := aRight;
- Result.Bottom := aBottom;
- end;
- procedure InflateGLRect(var aRect: TRect; dx, dy: Integer);
- begin
- aRect.Left := aRect.Left - dx;
- aRect.Right := aRect.Right + dx;
- if aRect.Right < aRect.Left then
- aRect.Right := aRect.Left;
- aRect.Top := aRect.Top - dy;
- aRect.Bottom := aRect.Bottom + dy;
- if aRect.Bottom < aRect.Top then
- aRect.Bottom := aRect.Top;
- end;
- procedure IntersectGLRect(var aRect: TRect; const rect2: TRect);
- var
- a: Integer;
- begin
- if (aRect.Left > rect2.Right) or (aRect.Right < rect2.Left)
- or (aRect.Top > rect2.Bottom) or (aRect.Bottom < rect2.Top) then
- begin
- // no intersection
- a := 0;
- aRect.Left := a;
- aRect.Right := a;
- aRect.Top := a;
- aRect.Bottom := a;
- end
- else
- begin
- if aRect.Left < rect2.Left then
- aRect.Left := rect2.Left;
- if aRect.Right > rect2.Right then
- aRect.Right := rect2.Right;
- if aRect.Top < rect2.Top then
- aRect.Top := rect2.Top;
- if aRect.Bottom > rect2.Bottom then
- aRect.Bottom := rect2.Bottom;
- end;
- end;
- procedure RaiseLastOSError;
- var
- e: EGLOSError;
- begin
- e := EGLOSError.Create('OS Error : ' + SysErrorMessage(GetLastError));
- raise e;
- end;
- type
- TDeviceCapabilities = record
- Xdpi, Ydpi: integer; // Number of pixels per logical inch.
- Depth: integer; // The bit depth.
- NumColors: integer; // Number of entries in the device's color table.
- end;
- function GetDeviceCapabilities: TDeviceCapabilities;
- var
- Device: HDC;
- begin
- Device := GetDC(0);
- try
- result.Xdpi := GetDeviceCaps(Device, LOGPIXELSX);
- result.Ydpi := GetDeviceCaps(Device, LOGPIXELSY);
- result.Depth := GetDeviceCaps(Device, BITSPIXEL);
- result.NumColors := GetDeviceCaps(Device, NUMCOLORS);
- finally
- ReleaseDC(0, Device);
- end;
- end;
- function GetDeviceLogicalPixelsX(device: HDC): Integer;
- begin
- result := GetDeviceCapabilities().Xdpi;
- end;
- function GetCurrentColorDepth: Integer;
- begin
- result := GetDeviceCapabilities().Depth;
- end;
- function PixelFormatToColorBits(aPixelFormat: TPixelFormat): Integer;
- begin
- case aPixelFormat of
- pfCustom{$IFDEF WIN32}, pfDevice{$ENDIF}: // use current color depth
- Result := GetCurrentColorDepth;
- pf1bit: Result := 1;
- {$IFDEF WIN32}
- pf4bit: Result := 4;
- pf15bit: Result := 15;
- {$ENDIF}
- pf8bit: Result := 8;
- pf16bit: Result := 16;
- pf32bit: Result := 32;
- else
- Result := 24;
- end;
- end;
- procedure FixPathDelimiter(var S: string);
- var
- I: Integer;
- begin
- for I := Length(S) downto 1 do
- if (S[I] = '/') or (S[I] = '\') then
- S[I] := PathDelim;
- end;
- function RelativePath(const S: string): string;
- var
- path: string;
- begin
- Result := S;
- if IsDesignTime then
- begin
- if Assigned(vProjectTargetName) then
- begin
- path := vProjectTargetName();
- if Length(path) = 0 then
- path := vLastProjectTargetName
- else
- vLastProjectTargetName := path;
- path := IncludeTrailingPathDelimiter(ExtractFilePath(path));
- end
- else
- exit;
- end
- else
- begin
- path := ExtractFilePath(ParamStr(0));
- path := IncludeTrailingPathDelimiter(path);
- end;
- if Pos(path, S) = 1 then
- Delete(Result, 1, Length(path));
- end;
- procedure QueryPerformanceCounter(out val: Int64);
- begin
- Windows.QueryPerformanceCounter(val);
- end;
- function QueryPerformanceFrequency(out val: Int64): Boolean;
- begin
- Result := Boolean(Windows.QueryPerformanceFrequency(val));
- end;
- function StartPrecisionTimer: Int64;
- begin
- QueryPerformanceCounter(Result);
- end;
- function PrecisionTimerLap(const precisionTimer: Int64): Double;
- begin
- // we can do this, because we don't really stop anything
- Result := StopPrecisionTimer(precisionTimer);
- end;
- function StopPrecisionTimer(const precisionTimer: Int64): Double;
- var
- cur, freq: Int64;
- begin
- QueryPerformanceCounter(cur);
- if not vInvPerformanceCounterFrequencyReady then
- begin
- QueryPerformanceFrequency(freq);
- vInvPerformanceCounterFrequency := 1.0 / freq;
- vInvPerformanceCounterFrequencyReady := True;
- end;
- Result := (cur - precisionTimer) * vInvPerformanceCounterFrequency;
- end;
- var
- vGLSStartTime : TDateTime;
- vLastTime: TDateTime;
- vDeltaMilliSecond: TDateTime;
- function AppTime: Double;
- var
- SystemTime: TSystemTime;
- begin
- GetLocalTime(SystemTime);
- with SystemTime do
- Result := (wHour * (MinsPerHour * SecsPerMin * MSecsPerSec) +
- wMinute * (SecsPerMin * MSecsPerSec) +
- wSecond * MSecsPerSec +
- wMilliSeconds) - vGLSStartTime;
- // Hack to fix time precession
- if Result - vLastTime = 0 then
- begin
- Result := Result + vDeltaMilliSecond;
- vDeltaMilliSecond := vDeltaMilliSecond + 0.1;
- end
- else begin
- vLastTime := Result;
- vDeltaMilliSecond := 0.1;
- end;
- end;
- function FindUnitName(anObject: TObject): string;
- begin
- if Assigned(anObject) then
- Result := anObject.UnitName
- else
- Result := '';
- end;
- function FindUnitName(aClass: TClass): string;
- begin
- if Assigned(aClass) then
- Result := aClass.UnitName
- else
- Result := '';
- end;
- procedure SetExeDirectory;
- var
- path: string;
- begin
- if IsDesignTime then
- begin
- if Assigned(vProjectTargetName) then
- begin
- path := vProjectTargetName();
- if Length(path) = 0 then
- path := vLastProjectTargetName
- else
- vLastProjectTargetName := path;
- path := IncludeTrailingPathDelimiter(ExtractFilePath(path));
- SetCurrentDir(path);
- end;
- end
- else
- begin
- path := ExtractFilePath(ParamStr(0));
- path := IncludeTrailingPathDelimiter(path);
- SetCurrentDir(path);
- end;
- end;
- function HalfToFloat(Half: THalfFloat): Single;
- var
- Dst, Sign, Mantissa: LongWord;
- Exp: LongInt;
- begin
- // extract sign, exponent, and mantissa from half number
- Sign := Half shr 15;
- Exp := (Half and $7C00) shr 10;
- Mantissa := Half and 1023;
- if (Exp > 0) and (Exp < 31) then
- begin
- // common normalized number
- Exp := Exp + (127 - 15);
- Mantissa := Mantissa shl 13;
- Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
- // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
- end
- else if (Exp = 0) and (Mantissa = 0) then
- begin
- // zero - preserve sign
- Dst := Sign shl 31;
- end
- else if (Exp = 0) and (Mantissa <> 0) then
- begin
- // denormalized number - renormalize it
- while (Mantissa and $00000400) = 0 do
- begin
- Mantissa := Mantissa shl 1;
- Dec(Exp);
- end;
- Inc(Exp);
- Mantissa := Mantissa and not $00000400;
- // now assemble normalized number
- Exp := Exp + (127 - 15);
- Mantissa := Mantissa shl 13;
- Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
- // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
- end
- else if (Exp = 31) and (Mantissa = 0) then
- begin
- // +/- infinity
- Dst := (Sign shl 31) or $7F800000;
- end
- else //if (Exp = 31) and (Mantisa <> 0) then
- begin
- // not a number - preserve sign and mantissa
- Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
- end;
- // reinterpret LongWord as Single
- Result := PSingle(@Dst)^;
- end;
- function FloatToHalf(Float: Single): THalfFloat;
- var
- Src: LongWord;
- Sign, Exp, Mantissa: LongInt;
- begin
- Src := PLongWord(@Float)^;
- // extract sign, exponent, and mantissa from Single number
- Sign := Src shr 31;
- Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15;
- Mantissa := Src and $007FFFFF;
- if (Exp > 0) and (Exp < 30) then
- begin
- // simple case - round the significand and combine it with the sign and exponent
- Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
- end
- else if Src = 0 then
- begin
- // input float is zero - return zero
- Result := 0;
- end
- else
- begin
- // difficult case - lengthy conversion
- if Exp <= 0 then
- begin
- if Exp < -10 then
- begin
- // input float's value is less than HalfMin, return zero
- Result := 0;
- end
- else
- begin
- // Float is a normalized Single whose magnitude is less than HalfNormMin.
- // We convert it to denormalized half.
- Mantissa := (Mantissa or $00800000) shr (1 - Exp);
- // round to nearest
- if (Mantissa and $00001000) > 0 then
- Mantissa := Mantissa + $00002000;
- // assemble Sign and Mantissa (Exp is zero to get denotmalized number)
- Result := (Sign shl 15) or (Mantissa shr 13);
- end;
- end
- else if Exp = 255 - 127 + 15 then
- begin
- if Mantissa = 0 then
- begin
- // input float is infinity, create infinity half with original sign
- Result := (Sign shl 15) or $7C00;
- end
- else
- begin
- // input float is NaN, create half NaN with original sign and mantissa
- Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
- end;
- end
- else
- begin
- // Exp is > 0 so input float is normalized Single
- // round to nearest
- if (Mantissa and $00001000) > 0 then
- begin
- Mantissa := Mantissa + $00002000;
- if (Mantissa and $00800000) > 0 then
- begin
- Mantissa := 0;
- Exp := Exp + 1;
- end;
- end;
- if Exp > 30 then
- begin
- // exponent overflow - return infinity half
- Result := (Sign shl 15) or $7C00;
- end
- else
- // assemble normalized half
- Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
- end;
- end;
- end;
- function GetValueFromStringsIndex(const AStrings: TStrings; const AIndex: Integer): string;
- begin
- Result := AStrings.ValueFromIndex[AIndex];
- end;
- function IsDirectoryWriteable(const AName: string): Boolean;
- var
- LFileName: String;
- LHandle: THandle;
- begin
- LFileName := IncludeTrailingPathDelimiter(AName) + 'chk.tmp';
- LHandle := CreateFile(PChar(LFileName), GENERIC_READ or GENERIC_WRITE, 0, nil,
- CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
- Result := LHandle <> INVALID_HANDLE_VALUE;
- if Result then
- CloseHandle(LHandle);
- end;
- function CharToWideChar(const AChar: AnsiChar): WideChar;
- var
- lResult: PWideChar;
- begin
- GetMem(lResult, 2);
- MultiByteToWideChar(CP_ACP, 0, @AChar, 1, lResult, 2);
- Result := lResult^;
- FreeMem(lResult, 2);
- end;
- //----------------------------------------
- initialization
- //----------------------------------------
- vGLSStartTime := AppTime;
- end.
|