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
This commit is contained in:
Serge
2024-02-24 11:41:41 +01:00
committed by GitHub
parent 034b46e557
commit 285cd81509
10 changed files with 408 additions and 42 deletions

View File

@@ -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);
}
}
}

56
app/Display/GammaRamp.cs Normal file
View File

@@ -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;
}
}
}

View File

@@ -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)
{

View File

@@ -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
{