今天早上收到了一加推送给OnePlus 6的Oxygen OS Open Beta 23,更新日志里提到本次OTA新增DC调光功能。之前一直以为DC调光需要硬件支持,可能会是屏幕需要能接受可变电流来增减亮度,或者是GPU能够控制屏幕电流之类的,但是了解一下以后发现很多手机厂商其实都是用软件实现的DC调光。所以这一篇文章是我边了解边写的,粗略的介绍一下屏幕调光的分类和各种实现原理,以及一加6的内核代码示例。

背景: 图像如何到人眼

屏幕材质 (LCD | OLED)

按照目前的手机市场来看,屏幕材质主要分为两种: LCD和AMOLED。

AMOLED

AMOLED的全称是Active Matrix Organic Light Emitting Diode。Organic指有机,即在正负极之间使用了有机薄膜材料,这一层薄膜是发光的主要来源。Active Matrix主动矩阵是指屏幕LED像素的驱动方式,对应的是Passive Matrix被动矩阵。PM被动矩阵使用交叉的阳极和阴极矩阵,点亮某个像素的时候需要两次线性扫描分别控制两个矩阵,而AM主动矩阵则可以单独控制到每一个像素点,故而PMOLED在显示尺寸巨大或者像素数量极高的情况下效率偏低,目前几乎所有的OLED屏幕都是主动矩阵AMOLED。AM/PM示意图参考如下

OLED主动 / 被动矩阵,图源[1]

AMOLED的主要优点就是每个像素可控,从而能获得更高的对比度和鲜艳度,从肉眼看上去颜色更加讨喜。目前少数软件有适配“A屏黑”,原理就是使用纯黑的配色(#000000)使主控直接关闭黑色区域的OLED像素点,做到省电和黑得纯粹。另外就是OLED整块面板的厚度极薄并且半透明,能开拓出另一片使用场景,比如超广可视角度、透明屏幕、柔性/折叠屏幕等等。

LCD

LCD的全称是Liquid Crystal Display,屏幕发光的主要方式是依靠背光打亮像素点。一般来说LCD屏幕背面有一层背光层,甚至有一些大型的LCD屏幕(比如电视、广告屏等)会使用多个光源用于提供各个区域的背光。

通常为了保证色彩的高还原度,绝大多数LCD面板需要使用白色背光,所以首先需要获得纯净白色光源。学过物理的都知道,自然界没有白色光对应的波长,光谱上是从红色最长慢慢到紫色最短,人眼看到的白色不过是多个波长的光按照某种比例混合到了一起。一般LCD的背光策略是使用有色背光+荧光粉产生白光。

得到白色光之后如何再通过液晶分离成想要的颜色呢?液晶分子在不同电压下会有不同的扭转角度,从而对白光的极化程度不同,在RGB滤波片的过滤下产生的光色就不同。在白光到达液晶分子之前,通常会有一层偏振层,玩过摄影的可能会知道偏振镜可以操作光线的成像,比如消除眼睛和玻璃上的反光,而LCD偏振层就是相同的原理。液晶层前面的偏振层负责对白光振动方向作归一化,而液晶层后放置的与之前偏振层垂直的另一个偏振层可以协同控制光强。

在对液晶电压的控制上,思路和AMOLED/PMOLED的差异是相同的,LCD也有AM/PM的方式控制液晶矩阵,而大多数目前的LCD面板采用AM主动矩阵进行像素驱动。

LCD vs AMOLED

截止到今天(2019年),绝大多数旗舰手机都已经转入了AMOLED阵营,只有少数高端手机仍然在使用LCD面板。就iPhone用户来说,最新一代的产品线上,只有iPhone XR使用的是一块色彩表现异常出色的LCD屏幕,其他所有iPhone都已近使用上了AMOLED。

有不少人仍然觉得“LCD永不为奴!”其实也不是没有道理,目前大多数显示器都仍然是LCD的,并且鲜艳度不输OLED。LCD的高刷新率目前是AMOLED达不到的,游戏玩家可能更在乎的是144hz的刷新率,而不是游戏画面中的人看起来是否讨喜。这也是为什么苹果的iPad仍然采用LCD面板来实现ProMotion的120hz刷新率。

AMOLED除了目前极难实现高刷新率外,另外一个显著缺点就是寿命短,AMOLED中的有机(O)薄膜材料并不是永久的,常亮情况下单个像素寿命通常只有1-2年。虽然LCD的液晶分子和背光元件理论上也不是永久的,但是寿命非常长,长到一般用不坏。

最后,最重要的一个区别,也是本文主要话题,就是AMOLED通常被认为在低亮度下由于PWM调光的低频闪会造成人眼的酸胀以及其他的眼部健康问题。

屏幕亮度控制

上面讨论了OLED和LCD如何发光显示一张图像,而通常所有的屏幕都有亮度调节,允许用户在显示同样或者接近的颜色的情况下调节图像的亮度。理想情况下,显示同一张图像的时候,不同亮度下的表现不应该有太大差别。

LCD面板对亮度的控制就相当直观简单,可以通过调整发光元件两侧的电压强度直接实现亮度控制,但是缺点就是黑色不黑,因为背光始终是常开的,部分光会透过颜色层,导致黑色变成灰色。

LCD可以通过改变背光层电压控制亮度,很多人认为AMOLED其实也可以,把所有像素的电压降低不就暗了。但是其实以目前OLED的工艺和质量,OLED像素点在低电压低亮度下的色彩还原非常差先不说,屏幕的果冻效应会非常严重。所以目前OLED几乎无一例外都是采用PWM调光。

PWM调光原理

OLED屏幕按照调光策略其实可以分成全称PWM调光,和PWM、DC混合调光。先谈谈全称PWM调光的,这种屏幕对于系统来说可控的只有单个像素亮还是不亮的选项。也就是说所有像素全开的情况下,是亮度100%的白色,所有红色像素全开就是亮度100%的红色。

所以这种屏幕实现亮度控制其实就是靠的就是屏幕明暗交替。如果我想要一个60%的白色,那么系统就需要确保单位时间内每个像素都有60%的时间是亮着的,剩余时间是关掉的。而这个明暗交替又不能太慢,比如10秒时间内前6秒亮屏后4秒息屏就不会有人认为那是60%的亮度。

因为人眼的时间分辨率其实是很低的,超过100hz的几乎无法分辨,所以只要在1/100秒以内完成60%时间的亮屏和40%时间的息屏就可以使人眼认为亮度是60%。而事实上绝大多数厂商采用的PWM明暗交替都是类似线性扫描,相比整屏的明暗交替,这种方式更不易被人眼察觉。在相机高速快门的拍摄下屏幕的瞬间显示如下图

来源黑鲨手机知乎专栏, [2]

这种情况在屏幕维持高亮度下并不会很明显,再加上很多屏幕高亮度下可以使用DC调光控制亮度,很多人觉得OLED的PWM调光使眼睛不舒适的情况都发生在屏幕亮度极低的时候。像上图拍出来的一样,只不过在亮度极低的情况下屏幕有更高的概率处于像素关闭的状态。假设系统可以以1/10秒的时间精度控制单个像素点,那么一秒内在80%亮度的情况下,息屏时间在1/10s-1/5s之间;如果这个精度上升至1/200秒,那么息屏时间就会处于1/200s – 1/5s之间。这个完全取决于系统如何分配20%的息屏时间。

正常AMOLED屏幕在极低亮度下的屏闪也不会低于150hz,也就是说人眼绝对不可能识别出不连续的情况,(人眼即使快速眨眼的情况下也不可能看到上图相机高速快门拍出的条纹),绝大多数人适应这个屏闪也不会有任何问题。但是会有一小部分人在这种相对比较低频率的闪烁下眼睛会有酸痛的情况,再加上低亮度通常意味着这个人晚上关了灯躺在床上玩手机,AMOLED的PWM调光才会被许多人说成是“AMOLED瞎眼屏”。具体可以查看国际照明协会发布的关于屏闪的健康影响 [3]。

DC调光原理

正是因为PWM的低频闪烁加上夜间屏幕和背景对比度实在太大,才会有很多厂商想让自己的屏幕在低亮度的时候使用DC调光,甚至全程DC调光。

但是正如之前说的,OLED像素点在低功率情况下颜色还原不精准以及屏幕果冻效应严重到丧心病狂,所以目前所有手机厂商吹得DC调光都不是真正的DC调光。这一点也是我收到一加的DC调光软件更新之后研究一番才知道的。

DC调光,先不论其能实现与否,目的主要是消除手机在低亮度情况下的PWM调光引发的屏闪问题。所以在OLED技术取得下一次突破前,手机厂商想尽了办法解决这个问题,其中绝大多数厂商采用的方案都是在PWM调光下强制限制屏闪降低亮度的伪DC调光。

高占空比PWM调光

在PWM调光的AMOLED屏幕上,我们知道在100%的亮度下屏幕不会出现任何屏闪,因为像素都是常亮的。那么如何在确保屏幕不闪烁的情况下降低亮度既是解决PWM屏闪的出路。

目前所有我去了解的厂商的DC调光方案都是直接加一层半透明滤镜,拿纯白色举例,屏幕显示的始终是100%亮度的纯白,也就是PWM调光的闪烁并不会介入。而系统在白色面前加一层半透明灰色滤镜,可以看做类似于相机的减光镜(ND Filter)低配版。最纯粹的白色颜色代码是 #FFFFFF,那么50%的亮度情况下系统在叠加透明滤镜前显示的仍然是 #FFFFFF, 透明滤镜应用过后最终的颜色代码将会是#808080,也就是CMYK的K或者HSL的L降低50%。

也就是在同样使用PWM调光的情况下,与其用PWM调光显示50%亮度的#FFFFFF纯白 (这种情况会有屏闪),现在系统将会计算好最终的伪颜色代码并将其以100%亮度显示,也就是变为显示亮度100%的#808080,解决了屏闪问题。

PWM、DC 混合调光

大多数手机厂商做了高空占比PWM调光就完了,比如一加6在开启DC调光的情况下屏幕亮度全程都是100%,亮度控制完全靠那个伪ND镜。也有少数手机厂商在DC调光模式下会采用DC/PWM混合调光,因为加了半透明滤镜会使原先饱和度和对比度较高的画面变得不那么鲜艳,直接中和掉了AMOLED相较于LCD的一大颜色优势。前面说过,DC调光不可行只是因为OLED像素在低功率下显示不稳定,而大多数手机在75%亮度以上完全可以使用PWM调光确保颜色鲜艳,在75%亮度一下可以使用PWM调光降低部分亮度,再加上DC调光加一层不那么明显致命的半透明滤镜,来实现低亮度控制。

这种混合调光方案不仅能确保低亮度下颜色饱和度足够还原原图像,对屏闪的控制也会比纯PWM调光要好上许多。

软件DC调光实现

之前说到高占空比PWM调光需要确保在OLED的PWM调光不介入的情况下加一层类似于减光镜的半透明灰色滤镜,那么这个灰色滤镜有几种实现方案。第一个是使用高通平台提供的PCC+Dither方案,第二个是HWC模块中更改Alpha通道(透明度),区别可以参考小米知乎专栏写的技术文章 [4]。

一加6的实现方案

文件在内核代码的/drivers/gpu/drm/msm/dsi-staging/dsi-panel.c

extern int op_dimlayer_bl_alpha;
extern int op_dimlayer_bl_enabled;
extern int op_dimlayer_bl_enable_real;
static int dsi_panel_update_backlight(struct dsi_panel *panel,
	u32 bl_lvl)
{
	int rc = 0;
	u32 count;
	struct mipi_dsi_device *dsi;
	struct dsi_display_mode *mode;

	if (!panel || (bl_lvl > 0xffff) || !panel->cur_mode) {
		pr_err("invalid params\n");
		return -EINVAL;
	}

	dsi = &panel->mipi_device;
	mode = panel->cur_mode;

	if (panel->is_hbm_enabled) {
		hbm_finger_print = true;
		pr_err("HBM is enabled\n");
		return 0;
	}
	if (op_dimlayer_bl_enabled != op_dimlayer_bl_enable_real) {
		op_dimlayer_bl_enable_real = op_dimlayer_bl_enabled;
		if (op_dimlayer_bl_enable_real) {
		bl_lvl = op_dimlayer_bl_alpha;
			pr_err("dc light enable\n");
		} else {
			pr_err("dc light disenable\n");
		}
	}
	if (op_dimlayer_bl_enable_real) {
		bl_lvl = op_dimlayer_bl_alpha;
    }

	if (panel->bl_config.bl_high2bit) {
		if (HBM_flag == true)
			return 0;

		if (cur_backlight == bl_lvl && (mode_fps != cur_fps ||
				 cur_h != panel->cur_mode->timing.h_active) && !hbm_finger_print) {
			cur_fps = mode_fps;
			cur_h = panel->cur_mode->timing.h_active;
			return 0;
		}

		if (hbm_brightness_flag == 1) {
			count = mode->priv_info->cmd_sets[DSI_CMD_SET_HBM_BRIGHTNESS_OFF].count;
			if (!count) {
				pr_err("This panel does not support HBM brightness off mode.\n");
				goto error;
			}
			else {
				rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_HBM_BRIGHTNESS_OFF);
				pr_err("Send DSI_CMD_SET_HBM_BRIGHTNESS_OFF cmds.\n");
				hbm_brightness_flag = 0;
			}
		}

		rc = mipi_dsi_dcs_set_display_brightness_samsung(dsi, bl_lvl);
		pr_err("backlight = %d\n", bl_lvl);
		cur_backlight = bl_lvl;
		cur_fps = mode_fps;
		cur_h = panel->cur_mode->timing.h_active;
		hbm_finger_print = false;
	}
	else
		rc = mipi_dsi_dcs_set_display_brightness(dsi,
			bl_lvl);
	if (rc < 0)
		pr_err("failed to update dcs backlight:%d\n", bl_lvl);

error:
	return rc;
}

手动给一加的烂代码加几个注释….

  • HBM相关变量指显示屏的High Brightness Mode 高亮度模式,
  • op_dimlayer_bl_enabled控制是否为DC调光,
  • bl_lvl是个32位unsigned整型,表示Backlight Level,
  • op_dimlayer_bl_alpha表示该半透明灰色滤镜的alpha (透明度)
  • fp/fingerprint相关的代码主要是确保在DC调光下屏下指纹识别区域在需要识别的时候仍然以HBM模式显示。

伪DC调光的效果对比

原图参考
此图像的alt属性为空;文件名为DSC00189-pwm-683x1024.png
图像自然增减亮度,类似PWM
此图像的alt属性为空;文件名为image-1.png
灰色半透明蒙版,类似伪DC

以上是我用Photoshop对一张图片模拟的PWM调光控制亮度,以及未经处理的模拟DC调光效果。可以看到如果单纯更改Alpha通道/GrayScale值模拟亮度控制,那么图像的鲜艳程度必然会受到影响。

对于这个问题,各个厂商解决方法基本就是在调用高通的PCC之前先把色彩鲜艳度拉到爆或者至少做一些补偿,随后应用灰度蒙版把整个图像变暗的同时压缩掉了部分色彩数据,然后再一次用dither抖动进行色彩补偿,使得在低亮度下色彩还原也不会太差。在写这篇文章的时候,黑鲨手机声称使用了纯硬件DC调光,原理就是用第三方Pixelworks的硬件芯片和高通的PCC共同实现灰度压缩,色彩还原应该会好上一点。不过话说回来其实所有方案在做的事情就是相当于压缩图片之后再尝试把它还原,没颜色损失几乎是不可能的。

总之

目前市面上声称DC调光的都是PWM+灰色滤镜实现的伪DC/类DC,在低亮度下的确可以大幅度减少屏闪,但是缺点就是颜色还原非常不精准。希望之后有屏幕厂商的面板里内置伪DC的算法,这样传入的颜色就不会有损失,伪DC配合原图像可以更好地在低亮度下还原色彩。

References

[1]: https://www.mspace.tech/tech/714/
[2]: https://zhuanlan.zhihu.com/p/62725255
[3]: http://files.cie.co.at/883_CIE_TN_006-2016.pdf
[4]: https://zhuanlan.zhihu.com/p/67606601

Leave a comment