From: <tw...@us...> - 2022-05-29 13:49:01
|
Revision: 3849 http://sourceforge.net/p/gexperts/code/3849 Author: twm Date: 2022-05-29 13:48:58 +0000 (Sun, 29 May 2022) Log Message: ----------- update to latest version from OSDN Modified Paths: -------------- trunk/ExternalSource/dzlib/dzlib.inc trunk/ExternalSource/dzlib/u_dzAdvancedObject.pas trunk/ExternalSource/dzlib/u_dzClassUtils.pas trunk/ExternalSource/dzlib/u_dzConvertUtils.pas trunk/ExternalSource/dzlib/u_dzCriticalSection.pas trunk/ExternalSource/dzlib/u_dzDpiScaleUtils.pas trunk/ExternalSource/dzlib/u_dzDpiScaleUtilsDummy.pas trunk/ExternalSource/dzlib/u_dzGraphicsUtils.pas trunk/ExternalSource/dzlib/u_dzOsUtils.pas trunk/ExternalSource/dzlib/u_dzStopwatch.pas trunk/ExternalSource/dzlib/u_dzTranslator.pas trunk/ExternalSource/dzlib/u_dzVclUtils.pas Modified: trunk/ExternalSource/dzlib/dzlib.inc =================================================================== --- trunk/ExternalSource/dzlib/dzlib.inc 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/dzlib.inc 2022-05-29 13:48:58 UTC (rev 3849) @@ -116,6 +116,9 @@ {$DEFINE CUSTOMINIFILE_HAS_READSUBSECTIONS} {$ENDIF} +{$IFDEF DELPHIX_SYDNEY_UP} + {$DEFINE SUPPORTS_PER_MONITOR_DPI} +{$ENDIF} {$IFOPT T+} {$DEFINE TYPEDADDRESS_IS_ON} Modified: trunk/ExternalSource/dzlib/u_dzAdvancedObject.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzAdvancedObject.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzAdvancedObject.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -297,13 +297,8 @@ class function TAdvancedObject.SetIntProperty(_Instance: TObject; const _Name: string; _Value: Integer): Boolean; -var - PropInfo: PPropInfo; begin - PropInfo := GetPropInfo(_Instance.ClassInfo, _Name); - Result := Assigned(PropInfo) and (PropInfo.PropType^.Kind = tkInteger); - if Result then - TypInfo.SetOrdProp(_Instance, PropInfo, _Value); + Result := u_dzTypInfo.TrySetIntProperty(_Instance, _Name, _Value); end; {$IFDEF SUPPORTS_EXTENDED} Modified: trunk/ExternalSource/dzlib/u_dzClassUtils.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzClassUtils.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzClassUtils.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -1095,18 +1095,17 @@ var Len: Integer; ErrCode: DWORD; + s: AnsiString; begin - Len := Length(_s); - if Len > 0 then begin - Result := _Stream.Write(_s[1], Len); - if Result <> Len then begin - ErrCode := GetLastError; - RaiseLastOSErrorEx(ErrCode, - Format(_('Error writing string of length %d to stream, wrote only %d bytes: %%1:s (%%0:d)'), - [Len, Result])); - end; - end else - Result := 0; + s := AnsiString(_s); + Len := Length(s); + Result := _Stream.Write(PAnsiChar(s)^, Len); + if Result <> Len then begin + ErrCode := GetLastError; + RaiseLastOSErrorEx(ErrCode, + Format(_('Error writing string of length %d to stream, wrote only %d bytes: %%1:s (%%0:d)'), + [Len, Result])); + end; end; {$IFDEF UNICODE} Modified: trunk/ExternalSource/dzlib/u_dzConvertUtils.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzConvertUtils.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzConvertUtils.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -667,6 +667,8 @@ // Inlined method must be implemented before it is called function ReduceToUInt8(const _Value: Integer): UInt8; begin + // this could call EnsureRange(0, MaxUIn8) in Math, but I am not sure which Delphi + // versions support these functions. if _Value < 0 then Result := 0 else if _Value > MaxUInt8 then @@ -682,6 +684,8 @@ function ReduceToInt8(const _Value: Integer): Int8; begin + // this could call EnsureRange(MinInt8, MaxIn8) in Math, but I am not sure which Delphi + // versions support these functions. if _Value < MinInt8 then Result := MinInt8 else if _Value > MaxInt8 then @@ -692,6 +696,8 @@ function ReduceToUInt16(const _Value: Integer): UInt16; begin + // this could call EnsureRange(0, MaxUIn16) in Math, but I am not sure which Delphi + // versions support these functions. if _Value < 0 then Result := 0 else if _Value > MaxUInt16 then @@ -702,6 +708,8 @@ function ReduceToInt16(const _Value: Integer): Int16; begin + // this could call EnsureRange(MinInt16, MaxIn16) in Math, but I am not sure which Delphi + // versions support these functions. if _Value < MinInt16 then Result := MinInt16 else if _Value > MaxInt16 then Modified: trunk/ExternalSource/dzlib/u_dzCriticalSection.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzCriticalSection.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzCriticalSection.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -115,6 +115,7 @@ uses u_dzMiscUtils, + u_dzOsUtils, u_dzTypes; var @@ -155,89 +156,6 @@ end; {$ENDIF DEBUG_CRIT_SECT} -{$IF not declared(PSystemLogicalProcessorInformation)} -{$ALIGN ON} -{$MINENUMSIZE 4} - -{$IF not declared(ULONG_PTR)} -type - ULONG_PTR = LongWord; -{$IFEND} - -{$IF not declared(ULONGLONG)} - ULONGLONG = UInt64; -{$IFEND} - -type - _PROCESSOR_CACHE_TYPE = (CacheUnified { = 0}, CacheInstruction { = 1}, CacheData { = 2}, CacheTrace { = 3}); - PROCESSOR_CACHE_TYPE = _PROCESSOR_CACHE_TYPE; - TProcessorCacheType = PROCESSOR_CACHE_TYPE; -type - TCacheDescriptor = record - Level: BYTE; - Associativity: BYTE; - LineSize: Word; - Size: DWORD; - _Type: PROCESSOR_CACHE_TYPE; - end; - -type - TLogicalProcessorRelationship = (RelationProcessorCore { = 0}, - RelationNumaNode { = 1}, - RelationCache { = 2}, - RelationProcessorPackage { = 3}, - RelationGroup { = 4}, RelationAll = $FFFF); - -type - TSystemLogicalProcessorInformation = record - ProcessorMask: ULONG_PTR; - Relationship: TLogicalProcessorRelationship; - case Integer of - 0: (Flags: BYTE); // ProcessorCore - 1: (NodeNumber: DWORD); // NumaNode - 2: (Cache: TCacheDescriptor); //Cache - 3: (Reserved: array[0..1] of ULONGLONG); - end; - PSystemLogicalProcessorInformation = ^TSystemLogicalProcessorInformation; - -function GetLogicalProcessorInformation(Buffer: PSystemLogicalProcessorInformation; var ReturnedLength: DWORD): BOOL; stdcall; - external kernel32 Name 'GetLogicalProcessorInformation'; -{$IFEND} - -function GetCacheLineSize: Integer; -var - ProcInfo: PSystemLogicalProcessorInformation; - CurInfo: PSystemLogicalProcessorInformation; - Len: DWORD; - Err: DWORD; -begin - Result := 64; - - Len := 0; - if not GetLogicalProcessorInformation(nil, Len) then begin - Err := GetLastError; - if Err = ERROR_INSUFFICIENT_BUFFER then begin - GetMem(ProcInfo, Len); - try - if GetLogicalProcessorInformation(ProcInfo, Len) then begin - // it should not be possible that the second call still returns, but ... - CurInfo := ProcInfo; - while Len > 0 do begin - if (CurInfo.Relationship = RelationCache) and (CurInfo.Cache.Level = 1) then begin - Result := CurInfo.Cache.LineSize; - Exit; - end; - Inc(CurInfo); - Dec(Len, SizeOf(CurInfo^)); - end; - end; - finally - FreeMem(ProcInfo); - end; - end; - end; -end; - {$IFDEF SUPPORTS_ENHANCED_RECORDS} { TdzRTLCriticalSection } @@ -432,7 +350,7 @@ {$ENDIF SUPPORTS_ENHANCED_RECORDS} initialization - CacheLineSize := GetCacheLineSize; + CacheLineSize := GetCpuCacheLineSize; {$IFDEF SUPPORTS_ENHANCED_RECORDS} Assert(SizeOf(TdzRTLCriticalSection) = SizeOf(TRTLCriticalSection)); TryInitInitializeCriticalSectionEx; Modified: trunk/ExternalSource/dzlib/u_dzDpiScaleUtils.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzDpiScaleUtils.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzDpiScaleUtils.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -92,11 +92,48 @@ uses StdCtrls, CommCtrl, - u_dzAdvancedObject, - u_dzVclUtils, + u_dzTypInfo, u_dzTypesUtils, u_dzMiscUtils; +type + TFormHack = class(TForm) + end; + +function TForm_GetDesignDPI(_Frm: TForm): UINT; +begin +{$IFDEF HAS_TFORM_GETDESIGNDPI} + Result := TFormHack(_Frm).GetDesignDpi; +{$ELSE} + Result := 96; +{$ENDIF} +end; + +type + TGetDpiForWindow = function(_HWnd: HWND): UINT; stdcall; +var + GetDpiForWindow: TGetDpiForWindow = nil; + +procedure InitApiCalls; +var + Handle: Cardinal; +begin + Handle := LoadLibrary('user32.dll'); + if Handle <> 0 then begin + GetDpiForWindow := GetProcAddress(Handle, 'GetDpiForWindow'); + end; +end; + +function TScreen_GetDpiForForm(_Frm: TCustomForm): UINT; +begin + Result := Screen.PixelsPerInch; + if _Frm is TForm then begin + if Assigned(GetDpiForWindow) then + Result := GetDpiForWindow(_Frm.Handle); + end; +end; + + {$IFDEF DPI_SCALER_LOGGING} var LogFile: Textfile; @@ -258,7 +295,7 @@ Ctrl.BoundsRect := br; if ItemHeight <> 0 then begin - TAdvancedObject.SetIntProperty(Ctrl, 'ItemHeight', _Scaler.Calc(ItemHeight)); + TrySetIntProperty(Ctrl, 'ItemHeight', _Scaler.Calc(ItemHeight)); end; // if we don't do this, the text is truncated on the left @@ -274,13 +311,13 @@ begin Ctrl := _Ctrl; BoundsRect := Ctrl.BoundsRect; - if not TAdvancedObject.TryGetObjectProperty(_Ctrl, 'Font', TObject(fnt)) then begin + if not TryGetObjectProperty(_Ctrl, 'Font', TObject(fnt)) then begin FontSize := 0; end else begin FontSize := GetFontSize(fnt); end; - if not TAdvancedObject.TryGetIntProperty(_Ctrl, 'ItemHeight', ItemHeight) then begin + if not TryGetIntProperty(_Ctrl, 'ItemHeight', ItemHeight) then begin ItemHeight := 0; end; @@ -294,8 +331,8 @@ ParentFontValue: Boolean; OldFontSize: Integer; begin - if TAdvancedObject.TryGetObjectProperty(Ctrl, 'Font', TObject(fnt)) then begin - if not TAdvancedObject.TryGetBoolProperty(Ctrl, 'ParentFont', ParentFontValue) + if TryGetObjectProperty(Ctrl, 'Font', TObject(fnt)) then begin + if not TryGetBoolProperty(Ctrl, 'ParentFont', ParentFontValue) or not ParentFontValue then begin Assert(FontSize <> 0); Modified: trunk/ExternalSource/dzlib/u_dzDpiScaleUtilsDummy.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzDpiScaleUtilsDummy.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzDpiScaleUtilsDummy.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -1,5 +1,7 @@ unit u_dzDpiScaleUtilsDummy; +{$I 'dzlib.inc'} + interface uses @@ -9,7 +11,32 @@ Controls, Forms; +{$IFDEF SUPPORTS_ENHANCED_RECORDS} type + TDpiScaler = record + private + FDesignDpi: Integer; + FCurrentDpi: Integer; + public + procedure Init(_frm: TCustomForm); overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + procedure Init(_Dpi: Integer); overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + procedure Init(_DesignDpi, _CurrentDpi: Integer); overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + procedure SetCurrentDpi(_frm: TCustomForm); overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + procedure SetCurrentDpi(_Dpi: Integer); overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + function Calc(_Value: Integer): Integer; overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + function Calc(const _Value: TRect): TRect; overload; +{$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} + function ScaleFactorPercent: Integer; + end; +{$ENDIF} + +type PScaledImagesRec = ^TScaledImagesRec; TScaledImagesRec = record FDpi: Integer; @@ -22,7 +49,7 @@ FOriginal: TImageList; public constructor Create(_Owner: TComponent; _Original: TImageList); reintroduce; - function GetScaledList(_DPI: Integer): TImageList; + function GetScaledList(_Dpi: Integer): TImageList; end; type @@ -60,10 +87,57 @@ FOriginal := _Original; end; -function TImageListScaler.GetScaledList(_DPI: Integer): TImageList; +function TImageListScaler.GetScaledList(_Dpi: Integer): TImageList; begin Result := FOriginal; end; +{$IFDEF SUPPORTS_ENHANCED_RECORDS} +{ TDpiScaler } + +function TDpiScaler.Calc(_Value: Integer): Integer; +begin + Result := _Value; +end; + +function TDpiScaler.Calc(const _Value: TRect): TRect; +begin + Result := _Value; +end; + +procedure TDpiScaler.Init(_frm: TCustomForm); +begin + FDesignDpi := 96; + FCurrentDpi := 96; +end; + +procedure TDpiScaler.Init(_DesignDpi, _CurrentDpi: Integer); +begin + FDesignDpi := _DesignDpi; + FCurrentDpi := _CurrentDpi; +end; + +procedure TDpiScaler.Init(_Dpi: Integer); +begin + FCurrentDpi := _Dpi; + FDesignDpi := _Dpi; +end; + +function TDpiScaler.ScaleFactorPercent: Integer; +begin + Result := 100; +end; + +procedure TDpiScaler.SetCurrentDpi(_Dpi: Integer); +begin + FCurrentDpi := _Dpi; +end; + +procedure TDpiScaler.SetCurrentDpi(_frm: TCustomForm); +begin + FCurrentDpi := 96; +end; +{$ENDIF} + end. Modified: trunk/ExternalSource/dzlib/u_dzGraphicsUtils.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzGraphicsUtils.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzGraphicsUtils.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -2,11 +2,14 @@ {$INCLUDE 'dzlib.inc'} -{.$IFDEF DELPHI2005} -// the Delphi 2005 cmpiler crashes if this is compiled with typed @ operator +{.$UNDEF OPTIMIZE_DZ_GRAPHIC_UTILS} +{.$UNDEF SUPPORTS_INLINE} + +{$IFDEF DELPHI2005} +// the Delphi 2005 compiler crashes if this is compiled with typed @ operator // turned on {$TYPEDADDRESS OFF} -{.$ENDIF} +{$ENDIF} {$IFDEF OPTIMIZE_DZ_GRAPHIC_UTILS} {$OPTIMIZATION ON} @@ -57,6 +60,11 @@ u_dzTypesUtils; type + EdzPixelFormatNotSupported = class(EdzException) + constructor Create(_PixelFormat: TPixelFormat); overload; + end; + +type TRgbBrightnessChannelEnum = (rcbAverage, rcbFastLuminance, rcbRed, rcbGreen, rcbBlue, rcbLuminance); type @@ -94,7 +102,14 @@ function GetValues(_Idx: TValueIdxTriple): Byte; inline; procedure SetValues(_Idx: TValueIdxTriple; _Value: Byte); inline; function GetColor: TColor; + ///<summary> + /// Sets Blue, Green and Red for the given Color, supporting system colors in addition to RGB colors + /// @Note this is marginally slower than SetRgbColor. </summary> procedure SetColor(_Color: TColor); + ///<summary> + /// Sets Blue, Green and Red for the given Color assuming that it is an RGB color and not a system color. + /// @Note this is marginally faster than SetColor. </summary> + procedure SetRgbColor(_Color: TColor); procedure SetGray(_Value: Byte); function GetLuminance: Byte; function GetFastLuminance: Byte; overload; @@ -109,22 +124,39 @@ function GetRgbBrightness(_Red, _Green, _Blue: Byte; _Channel: TRgbBrightnessChannelEnum): Byte; -// if you are using Delphi 2007 or older you need to correct the NativeInt declaration from 8 bytes to 4 bytes: -{$IF SizeOf(Pointer) = 4} -type - NativeInt = Integer; -{$IFEND} +function CalcBytesPerPixel(_PixelFormat: TPixelFormat): Integer; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} -function AddToPtr(const _Ptr: Pointer; _Offset: NativeInt): Pointer; +function CalcBytesPerLine(_Width, _BytesPerPixel: Integer): Integer; overload; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} +function CalcBytesPerLine(_Width: Integer; _PixelFormat: TPixelFormat): Integer; overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} +function CalcBytesPerLine(_Width: Integer; _bmp: TBitmap): Integer; overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} -function PtrDiff(const _Ptr1, _Ptr2: Pointer): NativeInt; +procedure IncPtr(var _Ptr: Pointer; _Offset: IntPtr); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF} +function AddToPtr(const _Ptr: Pointer; _Offset: IntPtr): Pointer; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} + +function PtrDiff(const _Ptr1, _Ptr2: Pointer): IntPtr; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} + function TdzRgbTriple_GetFastLuminance(const _Triple: TdzRgbTriple): Byte; {$IFDEF SUPPORTS_INLINE} inline; @@ -238,6 +270,26 @@ inline; {$ENDIF} +procedure TCanvas_DrawLine(_cnv: TCanvas; _x1, _y1, _x2, _y2: Integer); overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} + +procedure TCanvas_DrawLine(_cnv: TCanvas; _pnt1, _pnt2: TPoint); overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} + +procedure TCanvas_DrawHorizontalLine(_cnv: TCanvas; _x1, _x2, _y: Integer); +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} + +procedure TCanvas_DrawVerticalLine(_cnv: TCanvas; _x, _y1, _y2: Integer); +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} + ///<summary> calls Windows.SaveDC and returns an interface which will automatically call /// Windows.RestoreDC when destroyed </summary> function TCanvas_SaveDC(_Canvas: TCanvas): IInterface; @@ -287,17 +339,31 @@ ///<summary> abbreviation for StretchBlt that takes TCanvas and TPoint values. </summary> function dzStretchBlt(_DestCnv: TCanvas; _DestTopLeft: TPoint; _DestSize: TPoint; _SrcCnv: TCanvas; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; overload; -{$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} +{$IFDEF SUPPORTS_INLINE}inline; +{$ENDIF} +function dzStretchBlt(_DestCnv: TCanvas; _DestLeft, _DestTop: Integer; _DestSize: TPoint; + _SrcCnv: TCanvas; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; overload; +{$IFDEF SUPPORTS_INLINE}inline; +{$ENDIF} + ///<summary> abbreviation for StretchBlt that takes TCanvas, TBitmap and TPoint values. </summary> function dzStretchBlt(_DestCnv: TCanvas; _DestTopLeft: TPoint; _DestSize: TPoint; _SrcBmp: TBitmap; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; overload; -{$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} +{$IFDEF SUPPORTS_INLINE}inline; +{$ENDIF} +///<summary> abbreviation for StretchBlt that takes TCanvas, TBitmap and TPoint values. </summary> +function dzStretchBlt(_DestCnv: TCanvas; _DestLeft, _DestTop: Integer; _DestSize: TPoint; + _SrcBmp: TBitmap; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; overload; +{$IFDEF SUPPORTS_INLINE}inline; +{$ENDIF} + ///<summary> abbreviation for StretchBlt that takes two TBitmap and TPoint values. </summary> function dzStretchBlt(_DestBmp: TBitmap; _DestTopLeft: TPoint; _DestSize: TPoint; _SrcBmp: TBitmap; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; overload; -{$IFDEF SUPPORTS_INLINE}inline; {$ENDIF} +{$IFDEF SUPPORTS_INLINE}inline; +{$ENDIF} ///<summary> abbreviation for StretchBlt that takes TRect </summary> function dzStretchBlt(_DestHandle: Hdc; _DestRect: TRect; @@ -457,7 +523,6 @@ inline; {$ENDIF} - ///<summary> /// Calculates the positive y coordinate for the given x coordinate for an ellipse /// with horizontal and vertical axes, centered on the coordinate origin (0/0). @@ -526,7 +591,18 @@ {$ENDIF} ///<summary> -/// Calculates the average brightness of an bitmap with PixelFormat = pf8Bit +/// Balances the brightness of the SrcBmp bitmap and returns the result in the DstBmp bitmap +/// If possible, the contrast will also be maximized using histogram stretching. +/// @param SrcBmp ist the input bitmap which will remain unchanged +/// @param DstBmp will be filled with the result. The Result will be an 8 bit grayscal bitmap. +/// @param Offset gives the radius of the area used to calculage the average brightness for each +/// pixel. The default is 3*64 (=192) +/// @NOTE: This currently leaves an outer frame of Offset pixels unchanged. A future version will +/// probably also process that area. </summary> +procedure TBitmap8_BalanceBrightness(_SrcBmp, _DstBmp: TBitmap; _Offset: Word = 3 * 64); // 192 + +///<summary> +/// Calculates the average brightness of a bitmap with PixelFormat = pf8Bit /// @param bmp is the bitmap to process /// @param LowCutoff is the lower brightness limit for pixels to be included in the calculation /// @param HighCutoff is the upper brightness limit for pixels to be included in the calculation @@ -537,7 +613,7 @@ out _Average: Byte): Boolean; ///<summary> -/// Calculates the average brightness of an bitmap with PixelFormat = pf24Bit +/// Calculates the average brightness of a bitmap with PixelFormat = pf24Bit /// @param bmp is the bitmap to process /// @param LowCutoff is the lower brightness limit for pixels to be included in the calculation /// @param HighCutoff is the upper brightness limit for pixels to be included in the calculation @@ -550,7 +626,7 @@ out _Average: Byte): Boolean; ///<summary> -/// Calculates the average brightness of an bitmap with PixelFormat = pf24Bit thereby only +/// Calculates the average brightness of a bitmap with PixelFormat = pf24Bit thereby only /// using the blue channel. /// @param bmp is the bitmap to process /// @param LowCutoff is the lower brightness limit for pixels to be included in the calculation @@ -589,8 +665,21 @@ procedure TBitmap24_GetHistograms(_bmp: TBitmap; _BrightnessChannel: TRgbBrightnessChannelEnum; out _Red, _Green, _Blue, _Brightness: TUInt64Array256); overload; -function TBitmap8_GetHistogram(_bmp: TBitmap): TUInt64Array256; overload; +///<summary> +/// Calcluates the histogram of a grayscale 8, 24 or 32 bit bitmap </summary> +function TBitmapMono_GetHistogram(_bmp: TBitmap): TUInt64Array256; overload; +procedure TBitmapMono_GetHistogram(_bmp: TBitmap; out _Histogram: TUInt64Array256; out _Average: UInt8); overload; +function TBitmap8_GetHistogram(_bmp: TBitmap): TUInt64Array256; overload; deprecated; // use TBitmapMono_GetHistogram +///<summary> +/// Calculates the histogram for an 8 bit grayscale bitmap +/// @param bmp is the bitmap to analyze +/// @param Histogram is an array which will be filled with the histogram +/// @param Average is set to the average brightness of the bitmap +/// todo: This should maybe also (or instead) return the Median </summary> +procedure TBitmap8_GetHistogram(_bmp: TBitmap; out _Histogram: TUInt64Array256; + out _Average: UInt8); overload; deprecated; // use TBitmapMono_GetHistogram + type // Note: The bitmap is stored upside down, so the y coordinates are reversed! TPixel24FilterCallback = procedure(_x, _y: Integer; var _Pixel: TdzRgbTriple) of object; @@ -614,7 +703,7 @@ ///</summary> /// Apply the given gamma curve to an 8 bit gray scale bitmap </summary> -procedure TBitmap8_ApplyGamma(_SrcBmp, _DstBmp: TBitmap; const _Gamma: TGammaCurve); overload; +procedure TBitmap8_ApplyGamma(_SrcBmp, _DstBmp: TBitmap; const _Gamma: TGammaCurve); ///</summary> /// Apply the given gamma curve to a 24 bit gray scale bitmap and convert it to 8 bit @@ -624,11 +713,11 @@ /// @param DstBmp is the destinateion bitmap, it will be set to the same size as the source /// and to PixelFormat pf8bit and a gray scale palette. /// @param Gamma is an array representing the gamma curve to apply. </summary> -procedure TBitmap24_ApplyGammaTo8(_SrcBmp, _DstBmp: TBitmap; const _Gamma: TGammaCurve); overload; +procedure TBitmap24_ApplyGammaTo8(_SrcBmp, _DstBmp: TBitmap; const _Gamma: TGammaCurve); ///</summary> /// Sequentially Apply the given gamma curves to an 8 bit gray scale bitmap </summary> -procedure TBitmap8_ApplyMultiGamma(_SrcBmp, _DstBmp: TBitmap; const _GammaArr: array of TGammaCurve); overload; +procedure TBitmap8_ApplyMultiGamma(_SrcBmp, _DstBmp: TBitmap; const _GammaArr: array of TGammaCurve); type ///<summary> @@ -700,8 +789,30 @@ /// @returns clWhite or clBlack depending on the brightness (luminance) of the color </summary> function BestForegroundForColor(_Color: TColor): TColor; overload; +///<summary> +/// @param Hue is a value between 0 and 1 </summary> function RainbowColor(_Hue: Double): TColor; overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} +///<summary> +/// @param Brightness is a grayscale value </summary> +function RainbowColor(_Brightness: Byte): TColor; overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} +procedure RainbowColor(_Brightness: Byte; out _Red, _Green, _Blue: Byte); overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} +procedure RainbowColor(_Brightness: Byte; out _Pixel: TdzRgbTriple); overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} function RainbowColor(_MinHue, _MaxHue, _Hue: Integer): TColor; overload; +{$IFDEF SUPPORTS_INLINE} +inline; +{$ENDIF} function TryStr2Color(const _s: string; out _Color: TColor): Boolean; @@ -713,6 +824,7 @@ uses Math, + TypInfo, jpeg, // if you get a compile error here you might need to add Vcl.Imaging to the unit scope names {$IFDEF HAS_UNIT_PNGIMAGE} pngimage, // support for TImage.LoadGraphics for PNG files @@ -739,6 +851,17 @@ _SrcSize.x, _SrcSize.y, _Rop); end; +function dzStretchBlt(_DestCnv: TCanvas; _DestLeft, _DestTop: Integer; _DestSize: TPoint; + _SrcCnv: TCanvas; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; +begin + Result := StretchBlt(_DestCnv.Handle, + _DestLeft, _DestTop, + _DestSize.x, _DestSize.y, + _SrcCnv.Handle, + _SrcTopLeft.x, _SrcTopLeft.y, + _SrcSize.x, _SrcSize.y, _Rop); +end; + function dzStretchBlt(_DestCnv: TCanvas; _DestTopLeft: TPoint; _DestSize: TPoint; _SrcBmp: TBitmap; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; begin @@ -746,6 +869,13 @@ _SrcBmp.Canvas, _SrcTopLeft, _SrcSize, _Rop); end; +function dzStretchBlt(_DestCnv: TCanvas; _DestLeft, _DestTop: Integer; _DestSize: TPoint; + _SrcBmp: TBitmap; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; +begin + Result := dzStretchBlt(_DestCnv, _DestLeft, _DestTop, _DestSize, + _SrcBmp.Canvas, _SrcTopLeft, _SrcSize, _Rop); +end; + function dzStretchBlt(_DestBmp: TBitmap; _DestTopLeft: TPoint; _DestSize: TPoint; _SrcBmp: TBitmap; _SrcTopLeft: TPoint; _SrcSize: TPoint; _Rop: DWORD = SRCCOPY): BOOL; overload; begin @@ -1052,6 +1182,29 @@ Result := TCanvas_DrawText(_Canvas, _Text, _Rect, Flags) end; +procedure TCanvas_DrawVerticalLine(_cnv: TCanvas; _x, _y1, _y2: Integer); +begin + _cnv.MoveTo(_x, _y1); + _cnv.LineTo(_x, _y2); +end; + +procedure TCanvas_DrawHorizontalLine(_cnv: TCanvas; _x1, _x2, _y: Integer); +begin + _cnv.MoveTo(_x1, _y); + _cnv.LineTo(_x2, _y); +end; + +procedure TCanvas_DrawLine(_cnv: TCanvas; _x1, _y1, _x2, _y2: Integer); +begin + _cnv.MoveTo(_x1, _y1); + _cnv.LineTo(_x2, _y2); +end; + +procedure TCanvas_DrawLine(_cnv: TCanvas; _pnt1, _pnt2: TPoint); +begin + TCanvas_DrawLine(_cnv, _pnt1.x, _pnt1.y, _pnt2.x, _pnt2.y); +end; + type TCanvasSaveDC = class(TInterfacedObject) private @@ -1158,7 +1311,11 @@ procedure GetRgbHls(_Red, _Green, _Blue: Byte; out _Hls: THlsRec); begin +{$IFDEF dzUseGraphics32} + GR32.RGBtoHSL(GR32.Color32(RGB(_Red, _Green, _Blue)), _Hls.Hue, _Hls.Saturation, _Hls.Luminance); +{$ELSE} ColorRGBToHLS(RGB(_Red, _Green, _Blue), _Hls.Hue, _Hls.Luminance, _Hls.Saturation); +{$ENDIF} end; function GetRgbLuminance(_Red, _Green, _Blue: Byte): Byte; @@ -1190,6 +1347,32 @@ end; end; +function CalcBytesPerPixel(_PixelFormat: TPixelFormat): Integer; +begin + case _PixelFormat of + pf8bit: Result := SizeOf(Byte); + pf24bit: Result := SizeOf(TdzRgbTriple); + pf32bit: Result := SizeOf(TdzRgbQuad); + else + raise EdzPixelFormatNotSupported.Create(_PixelFormat); + end; +end; + +function CalcBytesPerLine(_Width, _BytesPerPixel: Integer): Integer; +begin + Result := ((_Width * 8 * _BytesPerPixel + 31) and not 31) div 8; +end; + +function CalcBytesPerLine(_Width: Integer; _PixelFormat: TPixelFormat): Integer; +begin + Result := CalcBytesPerLine(_Width, CalcBytesPerPixel(_PixelFormat)); +end; + +function CalcBytesPerLine(_Width: Integer; _bmp: TBitmap): Integer; +begin + Result := CalcBytesPerLine(_Width, _bmp.PixelFormat); +end; + {$IFDEF SUPPORTS_ENHANCED_RECORDS} { TdzRgbTriple } @@ -1221,6 +1404,13 @@ Result := RGB(Red, Green, Blue); end; +procedure TdzRgbTriple.SetRgbColor(_Color: TColor); +begin + Red := GetRValue(_Color); + Green := GetGValue(_Color); + Blue := GetBValue(_Color); +end; + procedure TdzRgbTriple.SetColor(_Color: TColor); begin _Color := ColorToRGB(_Color); @@ -1456,6 +1646,9 @@ Red := _Value; Green := _Value; Blue := _Value; + // According to the Win32 API documenation this member is reserved and must be zero, + // but apparently it can be non-zero and contain an alpha value. + Reserved := 0; end; procedure TdzRgbQuad.SetHls(_Hue, _Luminance, _Saturation: Word); @@ -1519,24 +1712,89 @@ end; end; -function MakeGrayPalette(_NumColors: TNumColors): HPALETTE; var + // global structure for the most common case of 256 colors, initialized on demand + GreyPalette256: TMaxLogPalette = ( + palVersion: 0; // 0 => has not been initialized + ); + +procedure InternalMakeGrayPalette(_NumColors: TNumColors; out lp: TMaxLogPalette); +var i: Integer; - lp: TMaxLogPalette; Grey: Byte; + MaxValue: Integer; begin - lp.palVersion := $300; lp.palNumEntries := _NumColors; + MaxValue := _NumColors - 1; for i := 0 to _NumColors - 1 do begin - Grey := i * 255 div _NumColors; + Grey := i * 255 div MaxValue; lp.palPalEntry[i].peRed := Grey; lp.palPalEntry[i].peGreen := Grey; lp.palPalEntry[i].peBlue := Grey; lp.palPalEntry[i].peFlags := PC_RESERVED; end; - Result := CreatePalette(pLogPalette(@lp)^); + // set it last, so we don't create a race condition with multithreaded access + lp.palVersion := $300; end; +function MakeGrayPalette(_NumColors: TNumColors): HPALETTE; +var + lp: TMaxLogPalette; + plp: PMaxLogPalette; +begin + if _NumColors = 256 then begin + if GreyPalette256.palVersion = 0 then + InternalMakeGrayPalette(_NumColors, GreyPalette256); + plp := @GreyPalette256; + end else begin + InternalMakeGrayPalette(_NumColors, lp); + plp := @lp; + end; + Result := CreatePalette(pLogPalette(plp)^); +end; + +///<sumamry> +/// Calls to this function are meant to be enclosed into Assert() so the compiler creates code for +/// it only if assertions are enabled </summary> +function AssertPixelFormat(_bmp: TBitmap; _Expected: TPixelFormat): Boolean; overload; +var + ActualName: string; + ExpectedName: string; +begin + Assert(Assigned(_bmp), 'bitmap is not assigned'); + Result := (_bmp.PixelFormat = _Expected); + if not Result then begin + ActualName := GetEnumName(TypeInfo(TPixelFormat), Ord(_bmp.PixelFormat)); + ExpectedName := GetEnumName(TypeInfo(TPixelFormat), Ord(_Expected)); + Assert(False, 'unexpected PixelFormat ' + ActualName + ' (expected ' + ExpectedName + ')'); + end; +end; + +type + TPixelFormatSet = set of TPixelFormat; + +///<sumamry> +/// Calls to this function are meant to be enclosed into Assert() so the compiler creates code for +/// it only if assertions are enabled </summary> +function AssertPixelFormat(_bmp: TBitmap; _Expected: TPixelFormatSet): Boolean; overload; +var + ActualName: string; + ExpectedNames: string; + pf: TPixelFormat; +begin + Assert(Assigned(_bmp), 'bitmap is not assigned'); + Result := (_bmp.PixelFormat in _Expected); + if not Result then begin + ActualName := GetEnumName(TypeInfo(TPixelFormat), Ord(_bmp.PixelFormat)); + ExpectedNames := ''; + for pf := Low(TPixelFormat) to High(TPixelFormat) do begin + ExpectedNames := ExpectedNames + ',' + GetEnumName(TypeInfo(TPixelFormat), Ord(pf)); + end; + ExpectedNames := Copy(ExpectedNames, 2, 255); + Assert(False, 'unexpected PixelFormat ' + ActualName + ' (expected one of' + ExpectedNames + ')'); + end; +end; + procedure TBitmap_AssignBgr8(_Buffer: PByte; _bmp: TBitmap; _YIsReversed: Boolean); const BytesPerPixel = 3; @@ -1551,7 +1809,7 @@ // bfh: TBitmapFileHeader; // bih: TBitmapInfoHeader; begin - Assert(_bmp.PixelFormat = pf24bit, 'unexpected PixelFormat (expected pf24bit)'); + Assert(AssertPixelFormat(_bmp, pf24bit)); h := _bmp.Height; w := _bmp.Width; @@ -1607,7 +1865,7 @@ ScanLine: PdzRgbTripleArray; h: Integer; begin - Assert(_bmp.PixelFormat = pf24bit, 'unexpected PixelFormat (expected pf24bit)'); + Assert(AssertPixelFormat(_bmp, pf24bit)); h := _bmp.Height; for y := 0 to h - 1 do begin @@ -1637,7 +1895,7 @@ h: Integer; Value: Byte; begin - Assert(_bmp.PixelFormat = pf24bit, 'unexpected PixelFormat (expected pf24bit)'); + Assert(AssertPixelFormat(_bmp, pf24bit)); h := _bmp.Height; for y := 0 to h - 1 do begin if _YIsReversed then begin @@ -1661,7 +1919,7 @@ y: Integer; ScanLine: PByte; begin - Assert(_bmp.PixelFormat = pf8bit, 'unexpected PixelFormat (expected pf8bit)'); + Assert(AssertPixelFormat(_bmp, pf8bit)); // Unfortunately the y coordinates of TBitmap are reversed (the picture is upside down). // So we can only copy the whole picture in one go, if the buffer is also upside down @@ -1680,51 +1938,96 @@ type PByteArray = SysUtils.PByteArray; - TCopyScanline = procedure(_Width: Integer; _SrcLine: Pointer; _DestLine: PByteArray); + TCopyScanline = procedure(_Width: Integer; _SrcLine: Pointer; _DestLine: Pointer); -procedure Copy24Bit(_Width: Integer; _SrcLine: Pointer; _DestLine: PByteArray); +procedure Copy8Bit(_Width: Integer; _SrcLine: Pointer; _DestLine: Pointer); +begin + Move(PByte(_SrcLine)^, PByte(_DestLine)^, _Width); +end; + +procedure Copy24Bit(_Width: Integer; _SrcLine: Pointer; _DestLine: Pointer); var x: Integer; - SrcLine: PdzRgbTripleArray absolute _SrcLine; + SrcPixel: PdzRgbTriple; + DstPixel: PByte; begin + SrcPixel := _SrcLine; + DstPixel := _DestLine; for x := 0 to _Width - 1 do begin - _DestLine[x] := SrcLine[x].Blue; + DstPixel^ := SrcPixel.Blue; + Inc(SrcPixel); + Inc(DstPixel); end; end; -procedure Copy32Bit(_Width: Integer; _SrcLine: Pointer; _DestLine: PByteArray); +procedure Copy32Bit(_Width: Integer; _SrcLine: Pointer; _DestLine: Pointer); var x: Integer; - SrcLine: PdzRgbQuadArray absolute _SrcLine; + SrcPixel: PdzRgbQuad; + DstPixel: PByte; begin + SrcPixel := _SrcLine; + DstPixel := _DestLine; for x := 0 to _Width - 1 do begin - _DestLine[x] := SrcLine[x].Blue; + DstPixel^ := SrcPixel.Blue; + Inc(SrcPixel); + Inc(DstPixel); end; end; procedure TBitmap_MonoToMono8(_InBmp, _OutBmp: TBitmap); +const + DstBytesPerPixel = 1; var CopyScanLine: TCopyScanline; + SrcBytesPerPixel: Integer; + SrcBytesPerLine: Integer; + DstBytesPerLine: Integer; w: Integer; h: Integer; y: Integer; + SrcLine: PByte; + DstLine: PByte; begin - _OutBmp.PixelFormat := pf8bit; - _OutBmp.Palette := MakeGrayPalette(); + Assert(AssertPixelFormat(_InBmp, [pf8bit, pf24bit, pf32bit])); - if _InBmp.PixelFormat = pf24bit then begin - CopyScanLine := Copy24Bit - end else if _InBmp.PixelFormat = pf32bit then begin - CopyScanLine := Copy32Bit; - end else - raise Exception.Create(_('Only bitmaps with PixelFormat = pf32bit or pf24bit are supported')); + case _InBmp.PixelFormat of + pf8bit: begin + CopyScanLine := Copy8Bit; + SrcBytesPerPixel := 1; + end; + pf24bit: begin + CopyScanLine := Copy24Bit; + SrcBytesPerPixel := 3; + end; + pf32bit: begin + CopyScanLine := Copy32Bit; + SrcBytesPerPixel := 4; + end; + else + raise EdzPixelFormatNotSupported.Create(_InBmp.PixelFormat) + end; w := _InBmp.Width; h := _InBmp.Height; + + _OutBmp.PixelFormat := pf8bit; + _OutBmp.Palette := MakeGrayPalette(); + _OutBmp.Width := w; _OutBmp.Height := h; + + SrcBytesPerLine := ((w * 8 * SrcBytesPerPixel + 31) and not 31) div 8; + Assert(SrcBytesPerLine = Graphics.BytesPerScanline(w, SrcBytesPerPixel * 8, 32)); + DstBytesPerLine := ((w * 8 * DstBytesPerPixel + 31) and not 31) div 8; + Assert(DstBytesPerLine = Graphics.BytesPerScanline(w, DstBytesPerPixel * 8, 32)); + + SrcLine := _InBmp.ScanLine[0]; + DstLine := _OutBmp.ScanLine[0]; for y := 0 to h - 1 do begin - CopyScanLine(w, _InBmp.ScanLine[y], _OutBmp.ScanLine[y]); + CopyScanLine(w, SrcLine, DstLine); + Dec(SrcLine, SrcBytesPerLine); + Dec(DstLine, DstBytesPerLine); end; end; @@ -1761,9 +2064,20 @@ w := Length(_In[0]); SetLength(_out, h); + // Yes, this could be moved inside the loop for setting the first and last line. + // but it would bomb out there when w = 0. So we either have to check for w=0 all the time + // or we make it a separate loop. + for y := 0 to h - 1 do begin + SetLength(_out[y], w); + end; + + if (w = 0) or (h = 0) then begin + asm nop end; + Exit; + end; + // copy first and last line without changes for y := 0 to h - 1 do begin - SetLength(_out[y], w); _out[y][0] := _In[y][0]; _out[y][w - 1] := _In[y][w - 1]; end; @@ -2167,8 +2481,7 @@ SrcTriple: PdzRgbTriple; BytesPerLine: Integer; begin - Assert(Assigned(_SrcBmp)); - Assert(_SrcBmp.PixelFormat = pf24bit); + Assert(AssertPixelFormat(_SrcBmp, pf24bit)); _DstBmp.PixelFormat := pf24bit; w := _SrcBmp.Width; @@ -2218,8 +2531,7 @@ SrcTriple: PdzRgbTriple; BytesPerLine: Integer; begin - Assert(Assigned(_SrcBmp)); - Assert(_SrcBmp.PixelFormat = pf24bit); + Assert(AssertPixelFormat(_SrcBmp, pf24bit)); _DstBmp.PixelFormat := pf24bit; w := _SrcBmp.Width; @@ -2269,8 +2581,7 @@ BytesPerLineInput: Integer; BytesPerLineOutput: Integer; begin - Assert(Assigned(_SrcBmp)); - Assert(_SrcBmp.PixelFormat = pf24bit); + Assert(AssertPixelFormat(_SrcBmp, pf24bit)); w := _SrcBmp.Width; h := _SrcBmp.Height; @@ -2318,8 +2629,7 @@ DstPixel: PByte; BytesPerLine: Integer; begin - Assert(Assigned(_SrcBmp)); - Assert(_SrcBmp.PixelFormat = pf8bit); + Assert(AssertPixelFormat(_SrcBmp, pf8bit)); _DstBmp.Assign(nil); _DstBmp.PixelFormat := pf8bit; @@ -2367,8 +2677,7 @@ i: Integer; Value: Byte; begin - Assert(Assigned(_SrcBmp)); - Assert(_SrcBmp.PixelFormat = pf8bit); + Assert(AssertPixelFormat(_SrcBmp, pf8bit)); _DstBmp.Assign(nil); _DstBmp.PixelFormat := pf8bit; @@ -2491,11 +2800,11 @@ // remember: Bitmaps are stored upside down, so for the previous line, we must add BytesPerLine // and for the next line we must subtract BytesPerLine SrcPixelLeft := SrcPixelCenter; - Inc(SrcPixelCenter); + Dec(SrcPixelLeft); SrcPixelRight := SrcPixelCenter; Inc(SrcPixelRight); - SrcPixelTop := PPixel(Integer(SrcPixelCenter) + BytesPerLine); - SrcPixelBottom := PPixel(Integer(SrcPixelCenter) - BytesPerLine); + SrcPixelTop := AddToPtr(SrcPixelCenter, +BytesPerLine); + SrcPixelBottom := AddToPtr(SrcPixelCenter, -BytesPerLine); for Column := 1 to WorkAreaWidth do begin CenterBrightness := SrcPixelCenter^; @@ -2601,16 +2910,16 @@ // We could of course call _SrcBmp.Scanline[], but that would affect the efficiency // of the code due the the function call and some overhead in that code. SrcRows[0] := FirstSrcRow; - SrcRows[1] := PByte(Integer(FirstSrcRow) - BytesPerLine); - SrcRows[2] := PByte(Integer(SrcRows[1]) - BytesPerLine); + SrcRows[1] := AddToPtr(FirstSrcRow, -BytesPerLine); + SrcRows[2] := AddToPtr(SrcRows[1], -BytesPerLine); for Row := 1 to WorkAreaHeight do begin Dec(DstRow, BytesPerLine); DstPixel := PPixel(DstRow); - SrcPixels[0] := PPixel(Integer(SrcRows[0]) + 1 * BytesPerPixel); //top + SrcPixels[0] := AddToPtr(SrcRows[0], +1 * BytesPerPixel); //top SrcPixels[1] := PPixel(SrcRows[1]); //left - SrcPixels[2] := PPixel(Integer(SrcRows[1]) + 1 * BytesPerPixel); //center - SrcPixels[3] := PPixel(Integer(SrcRows[1]) + 2 * BytesPerPixel); //right - SrcPixels[4] := PPixel(Integer(SrcRows[2]) + 1 * BytesPerPixel); //bottom + SrcPixels[2] := AddToPtr(SrcRows[1], +1 * BytesPerPixel); //center + SrcPixels[3] := AddToPtr(SrcRows[1], +2 * BytesPerPixel); //right + SrcPixels[4] := AddToPtr(SrcRows[2], +1 * BytesPerPixel); //bottom DstPixel^ := SrcPixels[1]^; //1st col unchanged for Column := 1 to WorkAreaWidth do begin // calculate average weighted by -beta for each color @@ -2671,18 +2980,23 @@ end; end; -// Inlined method must be iomplemented before it is called -function AddToPtr(const _Ptr: Pointer; _Offset: NativeInt): Pointer; +procedure IncPtr(var _Ptr: Pointer; _Offset: IntPtr); begin - Result := Pointer(NativeInt(_Ptr) + _Offset); + _Ptr := Pointer(IntPtr(_Ptr) + _Offset); end; -// Inlined method must be iomplemented before it is called -function PtrDiff(const _Ptr1, _Ptr2: Pointer): NativeInt; +// Inlined method must be implemented before it is called +function AddToPtr(const _Ptr: Pointer; _Offset: IntPtr): Pointer; begin - Result := NativeInt(_Ptr1) - NativeInt(_Ptr2); + Result := Pointer(IntPtr(_Ptr) + _Offset); end; +// Inlined method must be implemented before it is called +function PtrDiff(const _Ptr1, _Ptr2: Pointer): IntPtr; +begin + Result := IntPtr(_Ptr1) - IntPtr(_Ptr2); +end; + procedure TBitmap8_Sharpen(_SrcBmp, _DstBmp: TBitmap; const _AlphaMap: TSingleMatrix); type PPixel = PByte; @@ -2706,7 +3020,7 @@ CenterBrightness: Integer; AvgBrightness: Integer; BytesPerLine: Integer; - AlphaEntrySize: NativeInt; + AlphaEntrySize: IntPtr; AlphaPtr: PSingle; begin // sharpening is blending of the current pixel @@ -2761,11 +3075,11 @@ // remember: Bitmaps are stored upside down, so for the previous line, we must add BytesPerLine // and for the next line we must subtract BytesPerLine SrcPixelLeft := SrcPixelCenter; - Inc(SrcPixelCenter); + Dec(SrcPixelLeft); SrcPixelRight := SrcPixelCenter; Inc(SrcPixelRight); - SrcPixelTop := PPixel(Integer(SrcPixelCenter) + BytesPerLine); - SrcPixelBottom := PPixel(Integer(SrcPixelCenter) - BytesPerLine); + SrcPixelTop := AddToPtr(SrcPixelCenter, +BytesPerLine); + SrcPixelBottom := AddToPtr(SrcPixelCenter, -BytesPerLine); AlphaPtr := @(_AlphaMap[Row][1]); for Column := 1 to WorkAreaWidth do begin @@ -2804,7 +3118,7 @@ Inc(SrcPixelRight); Inc(SrcPixelBottom); - AlphaPtr := AddToPtr(AlphaPtr, AlphaEntrySize); + IncPtr(Pointer(AlphaPtr), AlphaEntrySize); end; // copy Last column unchanged @@ -2881,8 +3195,8 @@ // We could of course call _SrcBmp.Scanline[], but that would affect the efficiency // of the code due the the function call and some overhead in that code. SrcRows[0] := FirstSrcRow; - SrcRows[1] := PByte(Integer(FirstSrcRow) - BytesPerLine); - SrcRows[2] := PByte(Integer(SrcRows[1]) - BytesPerLine); + SrcRows[1] := AddToPtr(FirstSrcRow, -BytesPerLine); + SrcRows[2] := AddToPtr(SrcRows[1], -BytesPerLine); for Row := 1 to WorkAreaHeight do begin Assert(Length(_AlphaMap[Row]) = _SrcBmp.Width, Format('Number of values in AlphaMap[%d] (%d) must match bitmap width (%d)', @@ -2890,11 +3204,11 @@ Dec(DstRow, BytesPerLine); DstPixel := PPixel(DstRow); - SrcPixels[0] := PPixel(Integer(SrcRows[0]) + 1 * BytesPerPixel); //top + SrcPixels[0] := AddToPtr(SrcRows[0], +1 * BytesPerPixel); //top SrcPixels[1] := PPixel(SrcRows[1]); //left - SrcPixels[2] := PPixel(Integer(SrcRows[1]) + 1 * BytesPerPixel); //center - SrcPixels[3] := PPixel(Integer(SrcRows[1]) + 2 * BytesPerPixel); //right - SrcPixels[4] := PPixel(Integer(SrcRows[2]) + 1 * BytesPerPixel); //bottom + SrcPixels[2] := AddToPtr(SrcRows[1], +1 * BytesPerPixel); //center + SrcPixels[3] := AddToPtr(SrcRows[1], +2 * BytesPerPixel); //right + SrcPixels[4] := AddToPtr(SrcRows[2], +1 * BytesPerPixel); //bottom // copy 1st col unchanged ("[1]" is not the pixel index!) DstPixel^ := SrcPixels[1]^; for Column := 1 to WorkAreaWidth do begin @@ -2958,6 +3272,273 @@ end; end; +// this calculates the average of the given value array using the median +function CalcAverageBrightness(_Arr: array of Integer): Integer; +const + Left = 0; +var + Len: Integer; + Right: Integer; + i: Integer; + j: Integer; + tmp: Integer; + Mid: Integer; +begin + Len := Length(_Arr); + Assert(Len > 0); + Right := Len - 1; + i := Left + 1; + while i <= Right do begin + j := i; + while (j > Left) and (_Arr[j - 1] > _Arr[j]) do begin + tmp := _Arr[j]; + _Arr[j] := _Arr[j - 1]; + _Arr[j - 1] := tmp; + Dec(j); + end; + Inc(i); + end; + Mid := Left + Len div 2; + if Odd(Len) then + Result := _Arr[Mid] + else + Result := (_Arr[Mid] + _Arr[Mid - 1]) div 2; +end; + +procedure TBitmap8_BalanceBrightness(_SrcBmp, _DstBmp: TBitmap; _Offset: Word); +type + PPixel = PByte; +const + BytesPerPixel = 1; + ForcedPixelFormat = pf8bit; +var + Offset: Integer; + Row: Integer; + Column: Integer; + SrcPixel: PPixel; + SrcPixel0: PPixel; + SrcPixel1: PPixel; + SrcPixel2: PPixel; + SrcPixel3: PPixel; + SrcPixel4: PPixel; + SrcPixel5: PPixel; + SrcPixel6: PPixel; + SrcPixel7: PPixel; + SrcPixel8: PPixel; + SrcPixel9: PPixel; + SrcRow: PByte; + DstRow: PByte; + DstPixel: PPixel; + BmpWidth: Integer; + BmpHeight: Integer; + WorkAreaHeight: Integer; + WorkAreaWidth: Integer; + WorkBuffer: array of array of Integer; + CenterBrightness: Integer; + AvgBrightness: Integer; + MinBrightness: Integer; + MaxBrightness: Integer; + BytesPerLine: Integer; + SrcBottomRow: PByte; + DstBottomRow: PByte; + SrcOffset0: Integer; + SrcOffset1: Integer; + SrcOffset2: Integer; + SrcOffset3: Integer; + SrcOffset4: Integer; + SrcOffset5: Integer; + SrcOffset6: Integer; + SrcOffset7: Integer; + SrcOffset8: Integer; + SrcOffset9: Integer; +// TotalBrightness: Int64; +// TotalAvgBrightness: Integer; +begin + Assert((_SrcBmp.Width > 2 * _Offset) and (_SrcBmp.Height > 2 * _Offset), Format('Bitmap must be at least %dx%d', [2 * _Offset, 2 * _Offset])); + Offset := _Offset; + + _SrcBmp.PixelFormat := ForcedPixelFormat; + _DstBmp.PixelFormat := ForcedPixelFormat; + _DstBmp.Palette := MakeGrayPalette; + BmpWidth := _SrcBmp.Width; + BmpHeight := _SrcBmp.Height; + TBitmap_SetSize(_DstBmp, BmpWidth, BmpHeight); + + SetLength(WorkBuffer, BmpHeight); + for Row := 0 to BmpHeight - 1 do + SetLength(WorkBuffer[Row], BmpWidth); + + BytesPerLine := (((BmpWidth) * 8 * BytesPerPixel + 31) and not 31) div 8; + Assert(BytesPerLine = Graphics.BytesPerScanline(BmpWidth, BytesPerPixel * 8, 32)); + + WorkAreaWidth := BmpWidth - 2 * Offset; + WorkAreaHeight := BmpHeight - 2 * Offset; + + MinBrightness := 255; + MaxBrightness := 0; + + // ScanLine[0] is the line with the highest memory address, so we decrement it by + // BytesPerLine to get the next line (which would be ScanLine[1]) + // We could of course call _SrcBmp.Scanline[], but that would affect the efficiency + // of the code due the the function call and some overhead in that code. + + SrcBottomRow := _SrcBmp.ScanLine[0]; + DstBottomRow := _DstBmp.ScanLine[0]; + + // Calculate offsets for pixels around the SrcPixel in these positions: + // + // 0 + // - 1 + // 9 - + // - 2 + // 8 * 3 + // 7 - + // - 4 + // 6 - + // 5 + // + // The distance between SrcPixel and the pixels 0, 4 5 and 8 is Offset. + // This is designed not to be too symmetric. + // I tried using only 0, 3, 5 and 8 but it generated artifacts. + SrcOffset0 := +BytesPerLine * Offset; + SrcOffset1 := +BytesPerLine * Offset * 2 div 3 + Offset * 1 div 3; + SrcOffset2 := +BytesPerLine * Offset * 1 div 3 + Offset * 2 div 3; + SrcOffset3 := +Offset; + SrcOffset4 := -BytesPerLine * Offset * 1 div 2 + Offset * 1 div 2; + SrcOffset5 := -BytesPerLine * Offset; + SrcOffset6 := -BytesPerLine * Offset * 2 div 3 - Offset * 1 div 3; + SrcOffset7 := -BytesPerLine * Offset * 1 div 3 - Offset * 2 div 3; + SrcOffset8 := -Offset; + SrcOffset9 := +BytesPerLine * Offset * 1 div 2 - Offset * 1 div 2; + + // Experimental code: Calculate the total average brightness. + // This was originally meant to adjust the total brightness of the picture back to the + // original average. This is no longer done since we use the minimum and maximum values to + // stretch the histogram at the end to get the best possible contrast. +// TotalBrightness := 0; +// SrcRow := SrcBottomRow; +// for Row := 0 to BmpHeight - 1 do begin +// SrcPixel := PPixel(SrcRow); +// for Column := 0 to BmpWidth - 1 do begin +// CenterBrightness := SrcPixel^; +// Inc(TotalBrightness, CenterBrightness); +// Inc(SrcPixel); +// end; +// Dec(SrcRow, BytesPerLine); +// end; +// TotalAvgBrightness := TotalBrightness div BmpWidth div BmpHeight; + + // Copy bottom row(s) unchanged + SrcRow := SrcBottomRow; + DstRow := DstBottomRow; + for Row := 0 to Offset - 1 do begin + SrcPixel := PPixel(SrcRow); + DstPixel := PPixel(DstRow); + for Column := 0 to BmpWidth - 1 do begin + CenterBrightness := SrcPixel^; + WorkBuffer[Row, Column] := CenterBrightness; + DstPixel^ := CenterBrightness; + Inc(SrcPixel); + Inc(DstPixel); + end; + Dec(SrcRow, BytesPerLine); + Dec(DstRow, BytesPerLine); + end; + + for Row := Offset to Offset + WorkAreaHeight - 1 do begin + SrcPixel := PPixel(SrcRow); + DstPixel := PPixel(DstRow); + + // copy the leftmost column(s) unchanged + for Column := 0 to Offset - 1 do begin + CenterBrightness := SrcPixel^; + WorkBuffer[Row, Column] := CenterBrightness; + DstPixel^ := CenterBrightness; + Inc(SrcPixel); + Inc(DstPixel); + end; + + // remember: Bitmaps are stored upside down, so for the previous line, we must add BytesPerLine + // and for the next line we must subtract BytesPerLine + + SrcPixel0 := AddToPtr(SrcPixel, SrcOffset0); + SrcPixel1 := AddToPtr(SrcPixel, SrcOffset1); + SrcPixel2 := AddToPtr(SrcPixel, SrcOffset2); + SrcPixel3 := AddToPtr(SrcPixel, SrcOffset3); + SrcPixel4 := AddToPtr(SrcPixel, SrcOffset4); + SrcPixel5 := AddToPtr(SrcPixel, SrcOffset5); + SrcPixel6 := AddToPtr(SrcPixel, SrcOffset6); + SrcPixel7 := AddToPtr(SrcPixel, SrcOffset7); + SrcPixel8 := AddToPtr(SrcPixel, SrcOffset8); + SrcPixel9 := AddToPtr(SrcPixel, SrcOffset9); + + for Column := Offset to Offset + WorkAreaWidth - 1 do begin + CenterBrightness := SrcPixel^; + AvgBrightness := CalcAverageBrightness([SrcPixel0^, SrcPixel1^, SrcPixel2^, SrcPixel3^, + SrcPixel4^, SrcPixel5^, SrcPixel6^, SrcPixel7^, SrcPixel8^, SrcPixel9^]); + CenterBrightness := (CenterBrightness - AvgBrightness); + WorkBuffer[Row, Column] := CenterBrightness; + if CenterBrightness < MinBrightness then + MinBrightness := CenterBrightness; + if CenterBrightness > MaxBrightness then + MaxBrightness := CenterBrightness; + + Inc(SrcPixel0); + Inc(SrcPixel1); + Inc(SrcPixel2); + Inc(SrcPixel3); + Inc(SrcPixel4); + Inc(SrcPixel5); + Inc(SrcPixel6); + Inc(SrcPixel7); + Inc(SrcPixel8); + Inc(SrcPixel9); + Inc(SrcPixel); + Inc(DstPixel); + end; + + // copy rightmost column(s) unchanged + for Column := Offset + WorkAreaWidth to BmpWidth - 1 do begin + CenterBrightness := SrcPixel^; + WorkBuffer[Row, Column] := CenterBrightness; + DstPixel^ := CenterBrightness; + Inc(SrcPixel); + Inc(DstPixel); + end; + + Dec(SrcRow, BytesPerLine); + Dec(DstRow, BytesPerLine); + end; + + // copy top row(s) unchanged + for Row := Offset + WorkAreaHeight to BmpHeight - 1 do begin + DstPixel := PPixel(DstRow); + SrcPixel := PPixel(SrcRow); + for Column := 0 to BmpWidth - 1 do begin + CenterBrightness := SrcPixel^; + WorkBuffer[Row, Column] := CenterBrightness; + DstPixel^ := CenterBrightness; + Inc(SrcPixel); + Inc(DstPixel); + end; + Dec(SrcRow, BytesPerLine); + Dec(DstRow, BytesPerLine); + end; + + DstRow := DstBottomRow; + for Row := 0 to BmpHeight - 1 do begin + DstPixel := PPixel(DstRow); + for Column := 0 to BmpWidth - 1 do begin + CenterBrightness := WorkBuffer[Row, Column]; + if (Row >= Offset) and (Row < Offset + WorkAreaHeight) and (Column >= Offset) and (Column < Offset + WorkAreaWidth) then + CenterBrightness := (CenterBrightness - MinBrightness) * 255 * 100 div (MaxBrightness - MinBrightness) div 100; + DstPixel^ := ReduceToByte(CenterBrightness); + Inc(DstPixel); + end; + Dec(DstRow, BytesPerLine); + end; +end; + function TBitmap_BitBlt(_DestBmp: TBitmap; _DestPos: TPoint; _Size: TPoint; _Src: TBitmap; _SrcPos: TPoint; _Rop: DWORD = SRCCOPY): LongBool; begin @@ -3024,6 +3605,8 @@ cnt: Integer; BytesPerLine: Integer; begin + Assert(AssertPixelFormat(_bmp, pf8bit)); + h := _bmp.Height; if h = 0 then begin Result := False; @@ -3073,6 +3656,8 @@ cnt: Integer; BytesPerLine: Integer; begin + Assert(AssertPixelFormat(_bmp, pf24bit)); + h := _bmp.Height; if h = 0 then begin @@ -3126,6 +3711,8 @@ cnt: Integer; BytesPerLine: Integer; begin + Assert(AssertPixelFormat(_bmp, pf24bit)); + h := _bmp.Height; if h = 0 then begin @@ -3290,31 +3877,114 @@ MoveColor(_Pixel); end; -function RainbowColor(_Hue: Double): TColor; overload; +function RainbowColor(_Hue: Double): TColor; // taken from https://stackoverflow.com/a/19719171/49925 +var + Value: Double; + IntValue: Integer; begin - _Hue := EnsureRange(_Hue, 0, 1) * 6; - case Trunc(_Hue) of - 0: Result := RGB(255, Round(Frac(_Hue) * 255), 0); - 1: Result := RGB(255 - Round(Frac(_Hue) * 255), 255, 0); - 2: Result := RGB(0, 255, Round(Frac(_Hue) * 255)); - 3: Result := RGB(0, 255 - Round(Frac(_Hue) * 255), 255); - 4: Result := RGB(Round(Frac(_Hue) * 255), 0, 255); - else - Result := RGB(255, 0, 255 - Round(Frac(_Hue) * 255)); + Value := EnsureRange(_Hue, 0, 1) * 6; + IntValue := Round(Frac(Value) * 255); + case Trunc(Value) of + 0: Result := RGB(255, IntValue, 0); + 1: Result := RGB(255 - IntValue, 255, 0); + 2: Result := RGB(0, 255, IntValue); + 3: Result := RGB(0, 255 - IntValue, 255); + 4: Result := RGB(IntValue, 0, 255); + else // 5 + Result := RGB(255, 0, 255 - IntValue); end; end; -function RainbowColor(_MinHue, _MaxHue, _Hue: Integer): TColor; overload; +procedure RainbowColor(_Brightness: Byte; out _Red, _Green, _Blue: Byte); +var + Brightness: Integer; + TruncValue: Word; + FracValue: Word; +begin + // This is supposed to be a faster version of the overloaded function that takes a double parameter + // because it uses only integer arithmethic. + // Note that I have not timed it, but it "feels" a bit faster. The result is the same. + // -- 2022-03-17 twm + Brightness := Integer(_Brightness) * 6; + DivMod(Brightness, 255, TruncValue, FracValue); + case TruncValue of + 0: begin + _Red := 255; + _Green := FracValue; + _Blue := 0; + end; + 1: begin + _Red := 255 - FracValue; + _Green := 255; + _Blue := 0; + end; + 2: begin + _Red := 0; + _Green := 255; + _Blue := FracValue; + end; + 3: begin + _Red := 0; + _Green := 255 - FracValue; + _Blue := 255; + end; + 4: begin + _Red := FracValue; + _Green := 0; + _Blue := 255; + end; + else // 5 + _Red := 255; + _Green := 0; + _Blue := 255 - FracValue; + end; +end; + +procedure RainbowColor(_Brightness: Byte; out _Pixel: TdzRgbTriple); +begin + RainbowColor(_Brightness, _Pixel.Red, _Pixel.Green, _Pixel.Blue); +end; + +function RainbowColor(_Brightness: Byte): TColor; +var + Red: Byte; + Green: Byte; + Blue: Byte; +begin + RainbowColor(_Brightness, Red, Green, Blue); + Result := RGB(Red, Green, Blue); +// Assert(Result = RainbowColor(_Brightness / 255)); +end; + +function RainbowColor(_MinHue, _MaxHue, _Hue: Integer): TColor; // taken from https://stackoverflow.com/a/19719171/49925 begin Result := RainbowColor((_Hue - _MinHue) / (_MaxHue - _MinHue + 1)); end; -function TBitmap8_GetHistogram(_bmp: TBitmap): TUInt64Array256; overload; -const - BytesPerPixel = 1; +function TBitmap8_GetHistogram(_bmp: TBitmap): TUInt64Array256; var + Average: UInt8; +begin + TBitmapMono_GetHistogram(_bmp, Result, Average); +end; + +procedure TBitmap8_GetHistogram(_bmp: TBitmap; out _Histogram: TUInt64Array256; out _Average: UInt8); +begin + TBitmapMono_GetHistogram(_bmp, _Histogram, _Average); +end; + +function TBitmapMono_GetHistogram(_bmp: TBitmap): TUInt64Array256; +var + Average: UInt8; +begin + TBitmapMono_GetHistogram(_bmp, Result, Average); +end; + +procedure TBitmapMono_GetHistogram(_bmp: TBitmap; out _Histogram: TUInt64Array256; out _Average: UInt8); +var + BytesPerPixel: Integer; w: Integer; h: Integer; x: Integer; @@ -3322,30 +3992,44 @@ ScanLine: PByte; Pixel: PByte; BytesPerLine: Integer; + Sum: Int64; begin - for x := Low(Result) to High(Result) do - Result[x] := 0; + Assert(AssertPixelFormat(_bmp, [pf8bit, pf24bit, pf32bit])); + case _bmp.PixelFormat of + pf8bit: BytesPerPixel := 1; + pf24bit: BytesPerPixel := 3; + pf32bit: BytesPerPixel := 4; + else + raise EdzPixelFormatNotSupported.Create(_bmp.PixelFormat) + end; + _Average := 0; + for x := Low(_Histogram) to High(_Histogram) do + _Histogram[x] := 0; h := _bmp.Height; - if h = 0 then begin + if h = 0 then Exit; //==> - end; w := _bmp.Width; + if w = 0 then + Exit; //==> BytesPerLine := ((w * 8 * BytesPerPixel + 31) and not 31) div 8; Assert(BytesPerLine = Graphics.BytesPerScanline(w, BytesPerPixel * 8, 32)); + Sum := 0; ScanLine := _bmp.ScanLine[0]; for y := 0 to h - 1 do begin Assert(ScanLine = _bmp.ScanLine[y]); Pixel := ScanLine; for x := 0 to w - 1 do begin - Inc(Result[Pixel^]); + Inc(_Histogram[Pixel^]); + Inc(Sum, Pixel^); Inc(Pixel, BytesPerPixel); end; Dec(ScanLine, BytesPerLine); end; + _Average := (Sum div w) div h; end; function TBitmap24_GetHistogram(_bmp: TBitmap; _Channel: TRgbBrightnessChannelEnum): TUInt64Array256; overload; @@ -3360,6 +4044,8 @@ Pixel: PByte; BytesPerLine: Integer; begin + Assert(AssertPixelFormat(_bmp, pf24bit)); + for x := Low(Result) to High(Result) do Result[x] := 0; @@ -3401,6 +4087,8 @@ Pixel: PByte; BytesPerLine: Integer; begin + Assert(AssertPixelFormat(_bmp, pf24bit)); + for x := Low(_Red) to High(_Red) do begin _Red[x] := 0; _Green[x] := 0; @@ -3551,4 +4239,15 @@ end; end; +{ EdzPixelFormatNotSupported } + +constructor EdzPixelFormatNotSupported.Create(_PixelFormat: TPixelFormat); +var + PixelFormatName: string; +begin + PixelFormatName := GetEnumName(TypeInfo(TPixelFormat), Ord(_PixelFormat)); + CreateFmt(_('PixelFormat %s not supported'), [PixelFormatName]); +end; + end. + Modified: trunk/ExternalSource/dzlib/u_dzOsUtils.pas =================================================================== --- trunk/ExternalSource/dzlib/u_dzOsUtils.pas 2022-05-29 12:55:09 UTC (rev 3848) +++ trunk/ExternalSource/dzlib/u_dzOsUtils.pas 2022-05-29 13:48:58 UTC (rev 3849) @@ -300,6 +300,14 @@ /// is lying, we might as well directly call GetKernel32Version instead. </summary> function GetKernel32Version(out _Major, _Minor, _Revision, _Build: Integer): Boolean; +///<summary> +/// Uses the GetLogicalProcessorInformation WinAPI function to get the size of the processor's cache line </summary> +function GetCpuCacheLineSize: Integer; + +///<summary> +/// Uses the GetSystemInfo WinAPI function to get number of logical processors </summary> +function GetCpuLogicalProcessorCount: Integer; + implementation uses @@ -848,7 +856,7 @@ Exit; //==> end; - while Extractfilename(fn) <> '' do begin + while ExtractFileName(fn) <> '' do begin fn := ExtractFileDir(fn); if (fn <> '') and DirectoryExists(fn) then begin ShellExecEx(fn, '', '', SW_SHOWNORMAL); @@ -1342,4 +1350,103 @@ Result := Result + ' ' + Values.CSDVersion; end; +{$IF not declared(PSystemLogicalProcessorInformation)} +{$ALIGN ON} +{$MINENUMSIZE 4} + +{$IF not... [truncated message content] |