From 285cd815090712bef883f806e31d324a3966fcd7 Mon Sep 17 00:00:00 2001 From: Serge <5920850+seerge@users.noreply.github.com> Date: Sat, 24 Feb 2024 11:41:41 +0100 Subject: [PATCH] Flicker-free dimming slider for OLED models (#2109) * Gamma Init * Cleanup * Show flicker-free dimming only on supported models * Gamma ramp tweaks * Gamma improvements * Stronger dimming * Added OLED model detection --- app/AppConfig.cs | 5 + app/Display/DisplayGammaRamp.cs | 112 ++++++++++++++++++++ app/Display/GammaRamp.cs | 56 ++++++++++ app/Display/ScreenControl.cs | 71 ++++++++++++- app/Display/ScreenNative.cs | 12 ++- app/Properties/Resources.Designer.cs | 10 ++ app/Properties/Resources.resx | 37 ++++--- app/Resources/icons8-brightness-32.png | Bin 0 -> 420 bytes app/Settings.Designer.cs | 137 +++++++++++++++++++++---- app/Settings.cs | 10 ++ 10 files changed, 408 insertions(+), 42 deletions(-) create mode 100644 app/Display/DisplayGammaRamp.cs create mode 100644 app/Display/GammaRamp.cs create mode 100644 app/Resources/icons8-brightness-32.png diff --git a/app/AppConfig.cs b/app/AppConfig.cs index 6835da91..509bc9e5 100644 --- a/app/AppConfig.cs +++ b/app/AppConfig.cs @@ -395,6 +395,11 @@ public static class AppConfig return ContainsModel("GA503") || IsSlash(); } + public static bool IsOLED() + { + return ContainsModel("OLED") || IsSlash() || ContainsModel("UX64") || ContainsModel("UX34") || ContainsModel("K360") || ContainsModel("X150"); + } + public static bool IsStrix() { return ContainsModel("Strix") || ContainsModel("Scar") || ContainsModel("G703G"); diff --git a/app/Display/DisplayGammaRamp.cs b/app/Display/DisplayGammaRamp.cs new file mode 100644 index 00000000..ca150f6f --- /dev/null +++ b/app/Display/DisplayGammaRamp.cs @@ -0,0 +1,112 @@ +namespace GHelper.Display +{ + + public class DisplayGammaRamp + { + + public DisplayGammaRamp(ushort[] red, ushort[] green, ushort[] blue) + { + if (red?.Length != GammaRamp.DataPoints) + { + throw new ArgumentOutOfRangeException(nameof(red)); + } + + if (green?.Length != GammaRamp.DataPoints) + { + throw new ArgumentOutOfRangeException(nameof(green)); + } + + if (blue?.Length != GammaRamp.DataPoints) + { + throw new ArgumentOutOfRangeException(nameof(blue)); + } + + Red = red; + Green = green; + Blue = blue; + } + + public DisplayGammaRamp(double brightness = 1, double contrast = 1, double gamma = 1) + : this( + CalculateLUT(brightness, contrast, gamma), + CalculateLUT(brightness, contrast, gamma), + CalculateLUT(brightness, contrast, gamma) + ) + { + } + + public DisplayGammaRamp( + double redBrightness, + double redContrast, + double redGamma, + double greenBrightness, + double greenContrast, + double greenGamma, + double blueBrightness, + double blueContrast, + double blueGamma + ) + : this( + CalculateLUT(redBrightness, redContrast, redGamma), + CalculateLUT(greenBrightness, greenContrast, greenGamma), + CalculateLUT(blueBrightness, blueContrast, blueGamma) + ) + { + } + + internal DisplayGammaRamp(GammaRamp ramp) : + this(ramp.Red, ramp.Green, ramp.Blue) + { + } + public ushort[] Blue { get; } + public ushort[] Green { get; } + public ushort[] Red { get; } + private static ushort[] CalculateLUT(double brightness, double contrast, double gamma) + { + brightness = 0.5 + brightness / 2; + var result = new ushort[GammaRamp.DataPoints]; + for (var i = 0; i < result.Length; i++) + { + result[i] = (ushort)(brightness * ushort.MaxValue * i / (float)(result.Length - 1)); + } + return result; + } + + public bool IsOriginal() + { + int MaxRed = Red[Red.Length - 1]; + int MaxGreen = Green[Green.Length - 1]; + int MaxBlue = Blue[Blue.Length - 1]; + return (Math.Abs((MaxRed + MaxGreen + MaxBlue) / 3 - ushort.MaxValue) < 256); + } + + private static ushort[] Brightness(ushort[] data, double brightness) + { + var result = new ushort[GammaRamp.DataPoints]; + for (var i = 0; i < result.Length; i++) + { + if (brightness < 0.5) + result[i] = (ushort)(0.5 * ushort.MaxValue * Math.Pow((float)i/(result.Length - 1), 2 - brightness*2)); + else + result[i] = (ushort)(data[i] * brightness); + } + return result; + } + + internal GammaRamp AsBrightnessRamp(double brightness) + { + return new GammaRamp( + Brightness(Red, brightness), + Brightness(Green, brightness), + Brightness(Blue, brightness) + ); + } + + internal GammaRamp AsRamp() + { + return new GammaRamp(Red, Green, Blue); + } + + + } +} \ No newline at end of file diff --git a/app/Display/GammaRamp.cs b/app/Display/GammaRamp.cs new file mode 100644 index 00000000..0b1321db --- /dev/null +++ b/app/Display/GammaRamp.cs @@ -0,0 +1,56 @@ +using System.Runtime.InteropServices; + +namespace GHelper.Display +{ + + [StructLayout(LayoutKind.Sequential)] + internal struct GammaRamp + { + public const int DataPoints = 256; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)] + public readonly ushort[] Red; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)] + public readonly ushort[] Green; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)] + public readonly ushort[] Blue; + + public GammaRamp(ushort[] red, ushort[] green, ushort[] blue) + { + if (red == null) + { + throw new ArgumentNullException(nameof(red)); + } + + if (green == null) + { + throw new ArgumentNullException(nameof(green)); + } + + if (blue == null) + { + throw new ArgumentNullException(nameof(blue)); + } + + if (red.Length != DataPoints) + { + throw new ArgumentOutOfRangeException(nameof(red)); + } + + if (green.Length != DataPoints) + { + throw new ArgumentOutOfRangeException(nameof(green)); + } + + if (blue.Length != DataPoints) + { + throw new ArgumentOutOfRangeException(nameof(blue)); + } + + Red = red; + Green = green; + Blue = blue; + } + } + +} \ No newline at end of file diff --git a/app/Display/ScreenControl.cs b/app/Display/ScreenControl.cs index 6331baaf..4220f4a4 100644 --- a/app/Display/ScreenControl.cs +++ b/app/Display/ScreenControl.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Runtime.InteropServices; namespace GHelper.Display { @@ -7,6 +7,8 @@ namespace GHelper.Display public const int MAX_REFRESH = 1000; + public static DisplayGammaRamp? gammaRamp; + public void AutoScreen(bool force = false) { if (force || AppConfig.Is("screen_auto")) @@ -22,6 +24,70 @@ namespace GHelper.Display } } + public void SaveGamma() + { + var screenName = ScreenNative.FindLaptopScreen(); + if (screenName is null) return; + + try + { + var handle = ScreenNative.CreateDC(screenName, screenName, null, IntPtr.Zero); + var gammaRamp = new GammaRamp(); + if (ScreenNative.GetDeviceGammaRamp(handle, ref gammaRamp)) + { + var gamma = new DisplayGammaRamp(gammaRamp); + Logger.WriteLine("Gamma R: " + string.Join("-", gamma.Red)); + Logger.WriteLine("Gamma G: " + string.Join("-", gamma.Green)); + Logger.WriteLine("Gamma B: " + string.Join("-", gamma.Blue)); + } + } + catch (Exception ex) + { + Logger.WriteLine(ex.ToString()); + } + } + + public void SetGamma(int brightness = 100) + { + var bright = Math.Round((float)brightness / 200 + 0.5, 2); + + var screenName = ScreenNative.FindLaptopScreen(); + if (screenName is null) return; + + try + { + var handle = ScreenNative.CreateDC(screenName, screenName, null, IntPtr.Zero); + if (gammaRamp is null) + { + var gammaDump = new GammaRamp(); + if (ScreenNative.GetDeviceGammaRamp(handle, ref gammaDump)) + { + gammaRamp = new DisplayGammaRamp(gammaDump); + Logger.WriteLine("Gamma R: " + string.Join("-", gammaRamp.Red)); + Logger.WriteLine("Gamma G: " + string.Join("-", gammaRamp.Green)); + Logger.WriteLine("Gamma B: " + string.Join("-", gammaRamp.Blue)); + } + } + + if (gammaRamp is null || !gammaRamp.IsOriginal()) + { + Logger.WriteLine("Default Gamma"); + gammaRamp = new DisplayGammaRamp(); + } + + var ramp = gammaRamp.AsBrightnessRamp(bright); + bool result = ScreenNative.SetDeviceGammaRamp(handle, ref ramp); + + Logger.WriteLine("Brightness " + bright.ToString() + ": " + result); + + } catch (Exception ex) + { + Logger.WriteLine(ex.ToString()); + } + + //ScreenBrightness.Set(60 + (int)(40 * bright)); + } + public void SetScreen(int frequency = -1, int overdrive = -1, int miniled = -1) { var laptopScreen = ScreenNative.FindLaptopScreen(true); @@ -71,7 +137,8 @@ namespace GHelper.Display if (miniled1 >= 0) { miniled = (miniled1 == 1) ? 0 : 1; - } else + } + else { switch (miniled2) { diff --git a/app/Display/ScreenNative.cs b/app/Display/ScreenNative.cs index 90cfdbf0..0b260fd3 100644 --- a/app/Display/ScreenNative.cs +++ b/app/Display/ScreenNative.cs @@ -32,6 +32,16 @@ namespace GHelper.Display } internal class ScreenNative { + + [DllImport("gdi32", CharSet = CharSet.Unicode)] + internal static extern IntPtr CreateDC(string driver, string device, string port, IntPtr deviceMode); + + [DllImport("gdi32")] + internal static extern bool SetDeviceGammaRamp(IntPtr dcHandle, ref GammaRamp ramp); + + [DllImport("gdi32")] + internal static extern bool GetDeviceGammaRamp(IntPtr dcHandle, ref GammaRamp ramp); + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DEVMODE { @@ -146,7 +156,7 @@ namespace GHelper.Display public const string defaultDevice = @"\\.\DISPLAY1"; - private static string? FindInternalName(bool log = false) + public static string? FindInternalName(bool log = false) { try { diff --git a/app/Properties/Resources.Designer.cs b/app/Properties/Resources.Designer.cs index 8ff78342..971b35c6 100644 --- a/app/Properties/Resources.Designer.cs +++ b/app/Properties/Resources.Designer.cs @@ -210,6 +210,16 @@ namespace GHelper.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap icons8_brightness_32 { + get { + object obj = ResourceManager.GetObject("icons8-brightness-32", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/app/Properties/Resources.resx b/app/Properties/Resources.resx index 7d0ca668..ea07c07c 100644 --- a/app/Properties/Resources.resx +++ b/app/Properties/Resources.resx @@ -136,6 +136,9 @@ ..\Resources\icons8-bicycle-48 (1).png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\standard.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\eco.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -163,9 +166,15 @@ ..\Resources\icons8-automation-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\icons8-settings-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\brightness-up.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\brightness-down.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\icons8-processor-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -187,15 +196,9 @@ ..\Resources\icons8-laptop-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\ally.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\Resources\icons8-remove-64.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\icons8-share-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\Resources\icons8-function-mac-96.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -229,14 +232,11 @@ ..\Resources\icons8-xbox-rt-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\icons8-controller-96.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\Resources\icons8-fan-48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\icons8-settings-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\icons8-controller-96.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\icons8-maus-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -250,8 +250,11 @@ ..\Resources\dot-ultimate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\brightness-down.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\icons8-heartbeat-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ally.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\backlight-up.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -319,8 +322,8 @@ ..\Resources\icons8-software-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\standard.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\icons8-share-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\icons8-soonvibes-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -331,7 +334,7 @@ ..\Resources\icons8-charging-battery-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\icons8-heartbeat-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\icons8-brightness-32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/app/Resources/icons8-brightness-32.png b/app/Resources/icons8-brightness-32.png new file mode 100644 index 0000000000000000000000000000000000000000..a1deb58f23c30a19b13c9d0de978aa0febe95a22 GIT binary patch literal 420 zcmV;V0bBlwP)pqYHGhrreI&v+R^?*w0u+rLxzy{bg@~^m+ zcl9?-tbAuPZ#*Z!5;(L7>)Zni>C+a=842)eA*}O7GvSS;hq&TARD38?>T94**=>k{ zO@O%K+E=_UD?8M{&ag$lQD{`Siy*F;ozkl`Doi69m^9-4(5nmvCJ_zXpE6K%kb;p# z%Fnh4>r=4Df82&KenfTO0fATq7