From 968b1ab7ced471f57fc8af9d676aba81a2525f23 Mon Sep 17 00:00:00 2001 From: juv <8269353+juv@users.noreply.github.com> Date: Sat, 7 Dec 2024 17:33:43 +0100 Subject: [PATCH 1/8] Add gamma handling --- vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs | 31 ++ vibrance.GUI/FodyWeavers.xml | 5 +- vibrance.GUI/FodyWeavers.xsd | 111 +++++++ .../NVIDIA/NvidiaDynamicVibranceProxy.cs | 90 ++++++ vibrance.GUI/Properties/AssemblyInfo.cs | 4 +- vibrance.GUI/common/IVibranceProxy.cs | 1 + .../common/SetDeviceGammaRampHelper.cs | 275 ++++++++++++++++++ .../common/VibranceSettings.Designer.cs | 13 + vibrance.GUI/common/VibranceSettings.cs | 5 + vibrance.GUI/packages.config | 4 +- vibrance.GUI/vibrance.GUI.csproj | 15 +- 11 files changed, 542 insertions(+), 12 deletions(-) create mode 100644 vibrance.GUI/FodyWeavers.xsd create mode 100644 vibrance.GUI/common/SetDeviceGammaRampHelper.cs diff --git a/vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs b/vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs index 7e98d3d..9d1195c 100644 --- a/vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs +++ b/vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs @@ -13,6 +13,25 @@ namespace vibrance.GUI.AMD { public class AmdDynamicVibranceProxy : IVibranceProxy { + #region DllImports + [DllImport("gdi32.dll")] + public static extern bool GetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp); + + [DllImport("gdi32.dll")] + public static extern bool SetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct RAMP + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public UInt16[] Red; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public UInt16[] Green; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public UInt16[] Blue; + } + #endregion + private readonly IAmdAdapter _amdAdapter; private List _applicationSettings; private readonly Dictionary>> _windowsResolutionSettings; @@ -169,5 +188,17 @@ private static void PerformResolutionChange(Screen screen, ResolutionModeWrapper { ResolutionHelper.ChangeResolutionEx(resolutionSettings, screen.DeviceName); } + + public void ReadColorSettings() + { + RAMP ramp = new RAMP(); + //new WindowInteropHelper(_gameScreen).Handle + //bool ret = GetDeviceGammaRamp(new IntPtr(_gameScreen), ref ramp); + //Console.WriteLine("GetDeviceGammaRamp: " + ret); + //if (ret) + //{ + // Console.WriteLine(ramp.ToString()); + //} + } } } \ No newline at end of file diff --git a/vibrance.GUI/FodyWeavers.xml b/vibrance.GUI/FodyWeavers.xml index 2e6d4a7..633e853 100644 --- a/vibrance.GUI/FodyWeavers.xml +++ b/vibrance.GUI/FodyWeavers.xml @@ -1,5 +1,2 @@  - - - - \ No newline at end of file + \ No newline at end of file diff --git a/vibrance.GUI/FodyWeavers.xsd b/vibrance.GUI/FodyWeavers.xsd new file mode 100644 index 0000000..44a5374 --- /dev/null +++ b/vibrance.GUI/FodyWeavers.xsd @@ -0,0 +1,111 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/vibrance.GUI/NVIDIA/NvidiaDynamicVibranceProxy.cs b/vibrance.GUI/NVIDIA/NvidiaDynamicVibranceProxy.cs index 8f2a5b3..84d0f61 100644 --- a/vibrance.GUI/NVIDIA/NvidiaDynamicVibranceProxy.cs +++ b/vibrance.GUI/NVIDIA/NvidiaDynamicVibranceProxy.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; using System.IO; using System.Linq; using System.Media; @@ -113,6 +114,26 @@ class NvidiaDynamicVibranceProxy : IVibranceProxy CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] static extern int getAssociatedNvidiaDisplayHandle(string deviceName, [In] int length); + + [DllImport("gdi32.dll")] + public static extern bool GetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp); + + [DllImport("gdi32.dll")] + public static extern bool SetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct RAMP + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public UInt16[] Red; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public UInt16[] Green; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public UInt16[] Blue; + } + + [DllImport("gdi32.dll")] + static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); #endregion @@ -206,6 +227,49 @@ private void InitializeProxy() _vibranceInfo.isInitialized = true; } + private static ushort[] CalculateLUT(double brightness = 0.5, double contrast = 0.5, double gamma = 1) + { + const int dataPoints = 256; + + // Limit gamma in range [0.4-2.8] + gamma = Math.Min(Math.Max(gamma, 0.4), 2.8); + + // Normalize contrast in range [-1,1] + contrast = (Math.Min(Math.Max(contrast, 0), 1) - 0.5) * 2; + + // Normalize brightness in range [-1,1] + brightness = (Math.Min(Math.Max(brightness, 0), 1) - 0.5) * 2; + + // Calculate curve offset resulted from contrast + var offset = contrast > 0 ? contrast * -25.4 : contrast * -32; + + // Calculate the total range of curve + var range = (dataPoints - 1) + offset * 2; + + // Add brightness to the curve offset + offset += brightness * (range / 5); + + // Fill the gamma curve + var result = new ushort[dataPoints]; + for (var i = 0; i < result.Length; i++) + { + var factor = (i + offset) / range; + + factor = Math.Pow(factor, 1 / gamma); + + factor = Math.Min(Math.Max(factor, 0), 1); + + result[i] = (ushort)Math.Round(factor * ushort.MaxValue); + } + + return result; + } + + private void changeGamma() + { + CalculateLUT(1.0, 1.0, 1.0); + } + private static void OnWinEventHook(object sender, WinEventHookEventArgs e) { if (_applicationSettings.Count > 0) @@ -374,5 +438,31 @@ public void HandleDvcExit() else if (!_vibranceInfo.displayHandles.TrueForAll(handle => equalsDVCLevel(handle, _vibranceInfo.userVibranceSettingDefault))) _vibranceInfo.displayHandles.ForEach(handle => setDVCLevel(handle, _vibranceInfo.userVibranceSettingDefault)); } + int i = 0; + public void ReadColorSettings() + { + if(i%2 == 0) + { + SetDeviceGammaRampHelper.ApplyGammaRamp(555, 50, 1.0, 1.0, 0.5); + } + else + { + SetDeviceGammaRampHelper.ApplyGammaRamp(555, 50, 1.0, 0.5, 0.5); + } + i = i + 1; + + /*RAMP ramp = new RAMP(); + bool ret = GetDeviceGammaRamp(CreateDC("DISPLAY", _gameScreen.DeviceName, null, IntPtr.Zero), ref ramp); + Console.WriteLine("GetDeviceGammaRamp: " + ret); + string content = ""; + if(ret) + { + content += string.Join(",\n", ramp.Red); + content += string.Join(",\n", ramp.Green); + content += string.Join(",\n", ramp.Blue); + File.WriteAllText("data" + i +".txt", content); + i = i+1; + }*/ + } } } diff --git a/vibrance.GUI/Properties/AssemblyInfo.cs b/vibrance.GUI/Properties/AssemblyInfo.cs index 425abd0..747e8ab 100644 --- a/vibrance.GUI/Properties/AssemblyInfo.cs +++ b/vibrance.GUI/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.3.1.1")] -[assembly: AssemblyFileVersion("2.3.1.1")] +[assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyFileVersion("2.4.0.0")] diff --git a/vibrance.GUI/common/IVibranceProxy.cs b/vibrance.GUI/common/IVibranceProxy.cs index 53b6a49..0894281 100644 --- a/vibrance.GUI/common/IVibranceProxy.cs +++ b/vibrance.GUI/common/IVibranceProxy.cs @@ -15,5 +15,6 @@ public interface IVibranceProxy VibranceInfo GetVibranceInfo(); GraphicsAdapter GraphicsAdapter { get; } void SetNeverSwitchResolution(bool neverSwitchResolution); + void ReadColorSettings(); } } \ No newline at end of file diff --git a/vibrance.GUI/common/SetDeviceGammaRampHelper.cs b/vibrance.GUI/common/SetDeviceGammaRampHelper.cs new file mode 100644 index 0000000..85eccc7 --- /dev/null +++ b/vibrance.GUI/common/SetDeviceGammaRampHelper.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; + +namespace vibrance.GUI.common +{ + class SetDeviceGammaRampHelper + { + + static int monitorId; + static double level; + static double gamma; + static double brightness; + static double contrast; + static int monitorCounter; + + /// + /// Applies level, gamma, brightness, contrast to selected monitor. + /// + /// Id of selected monitor. If id is 0 then settings are applied to all monitors. + /// Level, default value 50 + /// Gamma, default value 10. + /// Brightness, default value 50 + /// Contrast, default value 50. + public static void ApplyGammaRamp(int monitorId, double level, double gamma, double brightness, double contrast) + { + SetDeviceGammaRampHelper.monitorId = monitorId; + SetDeviceGammaRampHelper.level = level; + SetDeviceGammaRampHelper.gamma = gamma; + SetDeviceGammaRampHelper.brightness = brightness; + SetDeviceGammaRampHelper.contrast = contrast; + monitorCounter = 0; + + var hdc = NativeAPI.GetDC(IntPtr.Zero); + if (hdc == IntPtr.Zero) + throw new InvalidOperationException(); + if (!NativeAPI.EnumDisplayMonitors(hdc, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero)) + throw new InvalidOperationException(); + if (NativeAPI.ReleaseDC(IntPtr.Zero, hdc) == 0) + throw new InvalidOperationException(); + } + + static NativeAPI.RAMP withoutRamp = new NativeAPI.RAMP(); + + // MonitorEnumProc callback + private static int MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeAPI.tagRECT lprcMonitor, IntPtr dwData) + { + //var info = new NativeAPI.MonitorInfo { cbSize = (uint)Marshal.SizeOf(typeof(NativeAPI.MonitorInfo)) }; + //if (!NativeAPI.GetMonitorInfoW(param0, ref info)) + // throw new InvalidOperationException(); + + + IntPtr thisHandle = NativeAPI.GetForegroundWindow(); + Screen screen = Screen.FromHandle(thisHandle); + screen.ToString(); + + monitorCounter++; + + //if (monitorId == 0 || monitorCounter == monitorId) + //{ + + + if(withoutRamp.Red == null) + { + bool ret = NativeAPI.GetDeviceGammaRamp(hdcMonitor, ref withoutRamp); + string content = ""; + if (ret) + { + content += string.Join(",\n", withoutRamp.Red); + content += string.Join(",\n", withoutRamp.Green); + content += string.Join(",\n", withoutRamp.Blue); + File.WriteAllText("data-ohne" + monitorCounter + ".txt", content); + } + } + else + { + NativeAPI.RAMP ramp = new NativeAPI.RAMP(); + bool ret = NativeAPI.GetDeviceGammaRamp(hdcMonitor, ref ramp); + string content = ""; + if (ret) + { + content += string.Join(",\n", ramp.Red); + content += string.Join(",\n", ramp.Green); + content += string.Join(",\n", ramp.Blue); + File.WriteAllText("data" + monitorCounter + "-" + DateTime.Now.ToFileTime().ToString() + ".txt", content); + } + + if(withoutRamp.Equals(ramp)) + { + Console.WriteLine("equal"); + } + } + + CalculateRamp(hdcMonitor, level, gamma, brightness, contrast); + //} + + return 1; + } + + private static ushort[] CalculateLUT(double brightness = 0.5, double contrast = 0.5, double gamma = 1) { + + const int dataPoints = 256; + + // Limit gamma in range [0.4-2.8] + gamma = Math.Min(Math.Max(gamma, 0.4), 2.8); + + // Normalize contrast in range [-1,1] + contrast = (Math.Min(Math.Max(contrast, 0), 1) - 0.5) * 2; + + // Normalize brightness in range [-1,1] + brightness = (Math.Min(Math.Max(brightness, 0), 1) - 0.5) * 2; + + // Calculate curve offset resulted from contrast + var offset = contrast > 0 ? contrast * -25.4 : contrast * -32; + + // Calculate the total range of curve + var range = (dataPoints - 1) + offset * 2; + + // Add brightness to the curve offset + offset += brightness* (range / 5); + + // Fill the gamma curve + var result = new ushort[dataPoints]; + for (var i = 0; i + /// Define red, blue and green arrays. + /// + /// Red array. + /// Green array. + /// Blue array. + public RAMP(UInt16[] r = null, UInt16[] g = null, UInt16[] b = null) + { + Red = r == null ? new UInt16[RAMP_SZ] : r; + Green = g == null ? new UInt16[RAMP_SZ] : g; + Blue = b == null ? new UInt16[RAMP_SZ] : b; + } + + public override bool Equals(object obj) + { + if(obj is RAMP) + { + return false; + } + RAMP other = (RAMP)obj; + return this.Red.Equals(other.Red) && this.Blue.Equals(other.Blue) && this.Green.Equals(other.Green); + } + + public override int GetHashCode() + { + int hashCode = -1058441243; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Red); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Green); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Blue); + return hashCode; + } + }; + + + // constant data + public const int RAMP_SZ = 256; + + + // extern methods + [DllImport("gdi32.dll")] + public static extern bool SetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp); + [DllImport("gdi32.dll")] + public static extern bool GetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate int MONITORENUMPROC(IntPtr param0, IntPtr param1, ref tagRECT param2, IntPtr param3); + + [StructLayout(LayoutKind.Sequential)] + public struct MonitorInfo + { + public uint cbSize; + public tagRECT rcMonitor; + public tagRECT rcWork; + public uint dwFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct tagRECT + { + public int left; + public int top; + public int right; + public int bottom; + } + } + + } +} diff --git a/vibrance.GUI/common/VibranceSettings.Designer.cs b/vibrance.GUI/common/VibranceSettings.Designer.cs index 5f5afa0..213f455 100644 --- a/vibrance.GUI/common/VibranceSettings.Designer.cs +++ b/vibrance.GUI/common/VibranceSettings.Designer.cs @@ -39,6 +39,7 @@ private void InitializeComponent() this.groupBox1 = new System.Windows.Forms.GroupBox(); this.labelResolution = new System.Windows.Forms.Label(); this.checkBoxResolution = new System.Windows.Forms.CheckBox(); + this.buttonReadColorSettings = new System.Windows.Forms.Button(); this.groupBox2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trackBarIngameLevel)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit(); @@ -144,11 +145,22 @@ private void InitializeComponent() this.checkBoxResolution.UseVisualStyleBackColor = true; this.checkBoxResolution.CheckedChanged += new System.EventHandler(this.checkBoxResolution_CheckedChanged); // + // buttonReadColorSettings + // + this.buttonReadColorSettings.Location = new System.Drawing.Point(166, 24); + this.buttonReadColorSettings.Name = "buttonReadColorSettings"; + this.buttonReadColorSettings.Size = new System.Drawing.Size(75, 23); + this.buttonReadColorSettings.TabIndex = 20; + this.buttonReadColorSettings.Text = "Read Brightness/Contrast/Gamma"; + this.buttonReadColorSettings.UseVisualStyleBackColor = true; + this.buttonReadColorSettings.Click += new System.EventHandler(this.buttonReadColorSettings_Click); + // // VibranceSettings // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(270, 266); + this.Controls.Add(this.buttonReadColorSettings); this.Controls.Add(this.groupBox1); this.Controls.Add(this.pictureBox); this.Controls.Add(this.labelTitle); @@ -183,5 +195,6 @@ private void InitializeComponent() private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.CheckBox checkBoxResolution; private System.Windows.Forms.Label labelResolution; + private System.Windows.Forms.Button buttonReadColorSettings; } } \ No newline at end of file diff --git a/vibrance.GUI/common/VibranceSettings.cs b/vibrance.GUI/common/VibranceSettings.cs index 819a44e..3f59a96 100644 --- a/vibrance.GUI/common/VibranceSettings.cs +++ b/vibrance.GUI/common/VibranceSettings.cs @@ -57,5 +57,10 @@ private void checkBoxResolution_CheckedChanged(object sender, EventArgs e) { this.cBoxResolution.Enabled = this.checkBoxResolution.Checked; } + + private void buttonReadColorSettings_Click(object sender, EventArgs e) + { + _v.ReadColorSettings(); + } } } diff --git a/vibrance.GUI/packages.config b/vibrance.GUI/packages.config index f3c5e08..202a3eb 100644 --- a/vibrance.GUI/packages.config +++ b/vibrance.GUI/packages.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/vibrance.GUI/vibrance.GUI.csproj b/vibrance.GUI/vibrance.GUI.csproj index 71e63a8..7999a87 100644 --- a/vibrance.GUI/vibrance.GUI.csproj +++ b/vibrance.GUI/vibrance.GUI.csproj @@ -1,5 +1,6 @@  + Debug @@ -27,7 +28,8 @@ 1.0.0.%2a false true - 535f6299 + + x86 @@ -76,6 +78,9 @@ MinimumRecommendedRules.ruleset + + ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll + ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll @@ -123,6 +128,7 @@ + @@ -208,13 +214,14 @@ - - Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Aktivieren Sie die Wiederherstellung von NuGet-Paketen, um die fehlende Datei herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". - + + +