背景

在一些应用场景下,客户端软件会添加护眼模式,以提升产品的人性化体验,本文以C#实现对护眼模式的开发。

实现思路

我们生活中经常遇到带有护眼模式的软件,例如:iphone护眼模式,PC端360护眼模式,那么我们软件中应该如何实现呢,这一点似乎网上并没有现成的实现方法和工具。

我结合平时对护眼功能的宣传,定位到以下几个关键词:暖屏防蓝光。我们无论做前端开发还是客户端开发应该都知道,代码中设置颜色可以通过RGB设置,其中的B(blue)就是蓝色。由此我产生联想:

  • 在展示的过程中,将B值调低,是否可以减少蓝光的输出,达到暖屏的效果?

  • WPF是否存在hook,可以在渲染界面的过程中,实现界面RGB的调整?

  • 显示器都可以调整亮度对比度,windows平台是否存在api,可以修改这些配置,达到暖屏的效果?

经过一系列的调研,我没有找到第二点的可实现方案,但是找到了windows平台下通过gdi32.dll对显示器进行设置,然后顺蔓摸瓜一顿尝试,果然被我试成功了!

实现代码

    /// <summary>
    /// 护眼模式实现类
    /// </summary>
    public static class ProtectEyeUtil
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [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);

        [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;

        }

        static IntPtr defaultHdc;
        static RAMP defaultlpRamp;
        public static bool isProtextEye = false;

        public static int InitDefaultBrightness()
        {
            int gamma = 1;
            try
            {
                defaultHdc = GetDC(IntPtr.Zero);
                GetDeviceGammaRamp(defaultHdc, ref defaultlpRamp);
                for (int i = 255; i > 2; i--)
                {
                    if (defaultlpRamp.Green[i] != defaultlpRamp.Green[i - 1])
                    {
                        gamma = defaultlpRamp.Green[i] / i - 128;
                        return gamma;
                    }
                }
                gamma = gamma > 130 ? 130 : gamma;
            }
            catch(Exception e)
            {
                LogHelper.WriteLog(e);
            }
            return gamma;
        }

        public static void RestoreDefaultBrightness()
        {
            try
            {
                SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref defaultlpRamp);
            }
            catch(Exception e)
            {
                LogHelper.WriteLog(e);
            }
        }

        public static void UpdateBrightness()
        {
            try
            {
                RAMP ramp = new RAMP();
                int gamma = 1;
                GetDeviceGammaRamp(defaultHdc, ref ramp);

                for (int i = 255; i > 2; i--)
                {
                    if (defaultlpRamp.Green[i] != defaultlpRamp.Green[i - 1])
                    {
                        gamma = defaultlpRamp.Green[i] / i - 128;
                        break;
                    }
                }
                gamma = gamma > 130 ? 130 : gamma;
                SetBrightness(gamma);
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(e);
            }
        }

        public static void SetBrightness(int gamma)
        {
            try
            {
                if (gamma <= 256 && gamma >= 1)
                {
                    RAMP ramp = new RAMP();
                    ramp.Red = new ushort[256];
                    ramp.Green = new ushort[256];
                    ramp.Blue = new ushort[256];

                    for (int i = 1; i < 256; i++)
                    {
                        int iArrayValue = i * (gamma + 128);
                        if (iArrayValue > 65535)
                            iArrayValue = 65535;
                        ramp.Red[i] = ramp.Green[i] = (ushort)iArrayValue;
                        int bArrayValue = i * (gamma + (isProtextEye ? 88 : 128));
                        if (bArrayValue > 65535)
                            bArrayValue = 65535;
                        ramp.Blue[i] = (ushort)bArrayValue;
                    }
                    SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref ramp);
                }
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(e);
            }
        }

    }

应用步骤

  1. 调用InitDefaultBrightness获取屏幕初始亮度;

  2. 调用SetProtectEye打开护眼模式;

  3. 调用SetBrightness传入0-100参数,调整护眼模式屏幕亮度;

  4. 调用SetProtectEye关闭护眼模式;

  5. 调用RestoreDefaultBrightness恢复屏幕初始亮度;

其他说明

  • 遵守以上调用步骤,不然可能导致屏幕亮度异常;

  • 在测试过程中发现,部分虚拟机瘦客户机无法实现护眼模式,同时360的护眼模式也无法正常使用,考虑可能客户机部分系统dll不完善导致。