在显示器的校正中常常会涉及Gamma值、白点坐标、色温、三原色、荧光剂色度值等参数。不同的gamma值会使显示器的亮度和颜色有较大差别。gamma值较小时亮调的等级差比较大,对表现亮度的颜色有利,反之,gamma值较大时对暗调的等级差拉的很大,对表现暗色有利。人的视觉对RGB三色信号的感觉大致成对数变化而不是线性变化,gamma校正正是为了克服这种非线性。gamma值的选取应该使整个亮度级别变化均匀。
notebook的背光控制是通过EC完成的,EC可以通过调节电源电压来实现LCD亮度调节。应用层如果要实现这个功能是通过显卡的RAMP值来实现的。C#中可以调用gdi32.dll提供的函数SetDeviceGammaRamp 和 GetDeviceGammaRamp来实现。但在操作之前要使用GetHdc()得到显卡的句柄。
[DllImport("gdi32.dll")]
private unsafe static extern bool SetDeviceGammaRamp(Int32 hdc, void* ramp);
[DllImport("gdi32.dll")]
public static extern int GetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp);
private static bool initialized = false;
private static Int32 hdc;
private static void InitializeClass()
{
if (initialized) return;
hdc = Graphics.FromHwnd(IntPtr.Zero).GetHdc().ToInt32();
initialized = true;
}
public static unsafe bool SetBrightness(short brightness)
{
InitializeClass();
if (brightness > 255) brightness = 255;
if (brightness < 0) brightness = 0;
short* gArray = stackalloc short[3 * 256];
short* idx = gArray;
for (int j = 0; j < 3; j++)
{
for (int i = 0; i < 256; i++)
{
int arrayVal = i * (brightness + 128);
if (arrayVal > 65535) { arrayVal = 65535; }
*idx = (short)arrayVal;
idx++;
}
}
//For some reason, this always returns false?
bool retVal = SetDeviceGammaRamp(hdc, gArray);
//Memory allocated through stackalloc is automatically free'd by the CLR.
return retVal;
}
程序运行后确实可以调整背光,功能没问题。这里有两个问题:
一个是SetDeviceGammaRamp返回值总是false ,另一个是gArray的计算,为什么是arrayVal = i * (brightness + 128);?
另一个版本:
[DllImport("gdi32.dll")]
public static extern int GetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp);
[DllImport("gdi32.dll")]
public static extern int SetDeviceGammaRamp(IntPtr hDC, ref RAMP lpRamp);
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
private static RAMP s_ramp = new RAMP();
private static RAMP ramp = new RAMP();
[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;
}
public static unsafe void SetGamma(int gamma)
{
InitializeClass();
ramp.Red = new ushort[256];
ramp.Green = new ushort[256];
ramp.Blue = new ushort[256];
for (int i = 1; i < 256; i++)
{
// gamma 必须在3和44之间
ramp.Red[i] = ramp.Green[i] = ramp.Blue[i] = (ushort)(Math.Min(65535, Math.Max(0, Math.Pow((i + 1) / 256.0, gamma * 0.1) * 65535 + 0.5)));
}
SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref ramp);
}
public static unsafe int GetBrightness()
{
int gamma = 0;
s_ramp.Red = new ushort[256];
s_ramp.Green = new ushort[256];
s_ramp.Blue = new ushort[256];
GetDeviceGammaRamp(GetDC(IntPtr.Zero), ref s_ramp);
return gamma;
}
运行后会改变gamma的值而不是背光。两个版本看起来是ramp算法不同其他都一样。而ramp是一个3*256的矩阵 用来代表RGB的修正值。
使用using System.Management;空间提供的类和物件实现:
using System.Management;
static void SetBrightness(byte targetBrightness)
{
ManagementScope scope = new ManagementScope("root\\WMI");
SelectQuery query = new SelectQuery("WmiMonitorBrightnessMethods");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
using (ManagementObjectCollection objectCollection = searcher.Get())
{
foreach (ManagementObject mObj in objectCollection)
{
mObj.InvokeMethod("WmiSetBrightness",
new Object[] { UInt32.MaxValue, targetBrightness });
break;
}
}
}
}
编译时总是提示找不到 ManagementScope ,没办法执行。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets a monitor's brightness value. Increasing the brightness value makes the display on the monitor brighter, and decreasing it makes the display dimmer.
Vista下如何用软件控制屏幕高层的API可以方便地控制屏幕的亮度、色温、对比度、显示区等。
#include "PhysicalMonitorEnumerationAPI.h"
#include "HighLevelMonitorConfigurationAPI.h"
#pragma comment(lib,"dxva2.lib")
void FreePhysicalMonitor(DWORD npm, LPPHYSICAL_MONITOR ppm)
{
DestroyPhysicalMonitors(npm, ppm);
// Free the array.
free(ppm);
}
LPPHYSICAL_MONITOR GetPhysicalMonitor(DWORD *pnpm)
{
HMONITOR hMon = NULL;
hMon = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
LPPHYSICAL_MONITOR ppm = NULL;
DWORD npm = 0;
BOOL bRet = GetNumberOfPhysicalMonitorsFromHMONITOR(hMon, &npm);
if (bRet)
{ ppm = (LPPHYSICAL_MONITOR)malloc(npm * sizeof(PHYSICAL_MONITOR));
if (ppm)
{bRet = GetPhysicalMonitorsFromHMONITOR(hMon, npm, ppm);
if (!bRet) {
FreePhysicalMonitor(npm, ppm);
ppm = NULL;
npm = 0;
}
}
}
*pnpm = npm;
return ppm;
}
返回的是PHYSICAL_MONITOR数组,以下示例只是使用了第一个PHYSICAL_MONITOR元素。
1、调整屏幕前我们可以看看显示器支持什么功能,Vista提供的API是GetMonitorCapabilities(在有些显示器上虽然GetMonitorCapabilities调用失败,但仍然可以调整亮度等;在有些显示器上,从GetMonitorCapabilities返回的值看可以支持某些功能,但实际又不能。这些都另当别论)。
LPPHYSICAL_MONITOR ppm = 0;
ppm = GetPhysicalMonitor();
if (ppm) {
DWORD nmc = 0, nct = 0;
GetMonitorCapabilities(ppm->hPhysicalMonitor, &nmc, &nct);
CString str = _T("");
if (nmc & MC_CAPS_BRIGHTNESS) {
str += _T("Support brightness control\n");
}
if (nmc & MC_CAPS_COLOR_TEMPERATURE) {
str += _T("Support color temperature\n");
}
if (nmc & MC_CAPS_CONTRAST) {
str += _T("Support contrast\n");
}
.........
if (str == _T(""))
str = _T("Support None");
MessageBox(str);
FreePhysicalMonitor(npm, ppm);
}
2、如何调整亮度
LPPHYSICAL_MONITOR ppm = 0;
DWORD npm = 0;
ppm = GetPhysicalMonitor(&npm);
//这一步可以得到ppm ,并且szPhysicalMonitorDescription中有显示器的描述。但ppm->hPhysicalMonitor一直是NULL.
//有人知道原因吗?帮忙留言告知下
if (ppm) {
DWORD nMin = 0, nCur = 0, nMax = 0;
GetMonitorBrightness(ppm->hPhysicalMonitor, &nMin, &nCur, &nMax);
CString str;
str.Format(_T("Min:%d, Cur:%d, Max:%d"), nMin, nCur, nMax);
MessageBox(str);
SetMonitorBrightness(ppm->hPhysicalMonitor, nMin);
Sleep(1000);
SetMonitorBrightness(ppm->hPhysicalMonitor, nMax);
Sleep(1000);
SetMonitorBrightness(ppm->hPhysicalMonitor, nCur);
Sleep(1000);
FreePhysicalMonitor(npm, ppm);
}
3、调色温
LPPHYSICAL_MONITOR ppm = 0;
DWORD npm = 0;
ppm = GetPhysicalMonitor(&npm);
if (ppm) {
SetMonitorRedGreenOrBlueGain(ppm->hPhysicalMonitor, MC_RED_GAIN, 50);
Sleep(500);
SetMonitorRedGreenOrBlueGain(ppm->hPhysicalMonitor, MC_GREEN_GAIN, 49);
Sleep(500);
SetMonitorRedGreenOrBlueGain(ppm->hPhysicalMonitor, MC_BLUE_GAIN, 52);
MessageBox(_T("Set color temperature => Done"));
FreePhysicalMonitor(npm, ppm);
}
4、调对比度
LPPHYSICAL_MONITOR ppm = 0;
DWORD npm = 0;
ppm = GetPhysicalMonitor(&npm);
if (ppm) {
DWORD nMin, nCur, nMax;
GetMonitorContrast(ppm->hPhysicalMonitor, &nMin, &nCur, &nMax);
CString str;
str.Format(_T("Min:%d, Cur:%d, Max:%d"), nMin, nCur, nMax);
MessageBox(str);
SetMonitorContrast(ppm->hPhysicalMonitor, nMin);
Sleep(1000);
SetMonitorContrast(ppm->hPhysicalMonitor, nMax);
Sleep(1000);
SetMonitorContrast(ppm->hPhysicalMonitor, nCur);
Sleep(1000);
FreePhysicalMonitor(npm, ppm);
}
5、查看显示器类型
LPPHYSICAL_MONITOR ppm = 0;
DWORD npm = 0;
ppm = GetPhysicalMonitor(&npm);
if (ppm) {
TCHAR *descs[] = {
_T("Shadow-mask cathode ray tube (CRT)"),
_T("Aperture-grill CRT"),
_T("Thin-film transistor (TFT) display"),
_T("Liquid crystal on silicon (LCOS) display"),
_T("Plasma display"),
_T("Organic light emitting diode (LED) display"),
_T("Electroluminescent display"),
_T("Microelectromechanical display"),
_T("Field emission device (FED) display")
};
MC_DISPLAY_TECHNOLOGY_TYPE dtt;
GetMonitorTechnologyType(ppm->hPhysicalMonitor, &dtt);
CString str;
str.Format(_T("Technology type: %s"), descs[(int)dtt]);
MessageBox(str);
FreePhysicalMonitor(npm, ppm);
}
6、恢复出厂设置
LPPHYSICAL_MONITOR ppm = 0;
DWORD npm = 0;
ppm = GetPhysicalMonitor(&npm);
if (ppm) {
RestoreMonitorFactoryDefaults(ppm->hPhysicalMonitor);
FreePhysicalMonitor(npm, ppm);
}