Linux实时放大镜原理

还是离不开编码的生活

今年国庆节前后出差太多,导致一个多月没有摸键盘了,甚是想念写代码的感觉。准备找一个小的产品练练手。

心目中的完美取色器

做产品对UI的时候,经常需要对着设计图取色,Linux传统的 gcolor2 一直都不是很方便,先要打开 gcolor2 窗口,然后还要点击取色图标,取色图标非常小,每次用取色图标取色都取不准确,取完颜色以后还要手动复制到系统剪切板。


深度录屏_Desktop_20171102165517.gif

所以一直一来都想做一个交互极简的取色器,最好鼠标移动到目标位置,快捷键直接启动取色器,点击就马上取色并自动复制到系统剪切板。
经过三天的研究终于做出了我理想中的取色器,世界上最简单的屏幕取色器:

深度录屏_Desktop_20171102165716.gif

1、启动取色器以后立即对屏幕进行实时预览
2、点击左键取色后自动把颜色数值复制到剪切板
3、点击右键弹出不同的颜色类型,方便不同类型的开发者

关键技术原理

为了做到极致的性能和操作流畅度,屏幕取色器最关键的技术就是 “实时放大镜”。
第一版demo做的时候,采用了深度截图同样的技术:

  • 先把屏幕的截图截取成 Pixmap 保存到内存中,以备取色使用
  • 光标移动的时候,根据光标坐标截取光标下的色块进行放大,形成 ”放大镜图层“
  • 重新绘制 “屏幕截图图层” 后,再绘制 ”放大镜图层“,通过两个图层的合成绘制,给用户一种实时放大屏幕的视觉错觉


    深度截图_选择区域_20171102175731.png

像深度截图这种传统的图层绘制方式有一个巨大的缺点,就是每次光标移动的时候,都会触发 “屏幕截图图层” 重新绘制,即使光标下的区域只更新了非常小的区域,这样就会造成潜在的性能问题。

3 (1).png

比如当用户使用的是双屏或者三屏,同时这些屏幕的分辨率都是2k以上的话,每次光标移动都会导致 6000x2000 像素的图片进行重新绘制,如果用户这时候快速移动光标,电脑瞬间就会卡顿,因为在 30ms 传统的流畅帧循环中,已经无法在一个循环中完成绘制超大图片所需的计算时间。

第二天的时候,就针对怎么实现 “实时放大镜” 的技术进行冥思苦想,到第二天晚上快放弃这个产品研发的时候,突然发现截图的时候是无法截取屏幕的光标的,然后又想到Linux系统中的光标是由窗口管理器根据光标主题的图标实时进行合成的,任何应用程序都无法截取这个由窗口管理器单独管理和绘制的光标图层。

突然灵机一动,如果我截取光标小的色块以后,直接把光标的图形设置成截图色块,这样我就不用每次移动光标的时候,手动去重新绘制屏幕截图的整个图层,因为根本就不需要绘制 “屏幕截图图层”, 每个图层的数据都会被窗口管理器保存,实时改变光标的图形后,窗口管理器自动会把当前的屏幕和光标进行实时合成来实现放大镜的效果。

这样做的好处就是,每次光标移动的时候,实际的计算量就只有截取光标处几十像素色块,不论屏幕多大,永远都只消耗常量的计算量,而且窗口管理器本身就会使用显卡进行图层混合,所以实时改变光标的技术的性能要比传统的截图自行混合图层的技术好百倍以上,而且随着屏幕的增大,性能优势非常明显。

2.png

真是古语所言 “山穷水尽疑无路,柳暗花明又一村”, 所以很多时候技术上遇到瓶颈,千万不要放弃,先暂时放一下,灵感会随着你长时间的深入思考突然蹦出来的。 ;)

随之而来的第二个问题就是,虽然我可以把屏幕取色色块设置成光标的样式,但是Qt本身只能设置应用自身的光标,无法设置整个系统的光标样式,除非用 X11 的技术。当最难的问题都解决时,剩下的问题就更容易攻破: 如果我启动一个窗口和整个屏幕一样大,而且窗口本身透明,这样屏幕所能看到的位置其实都是取色器应用窗口的位置,这样就可以通过 “设置一个全屏程序的光标” 来解决改变整个系统的光标的目的。

剩下的事情很简单,利用 Linux全局事件监听技术所介绍的技术来实现整个屏幕的鼠标移动和点击操作。
把 实时屏幕放大→设置光标内容→监听全局事件 这三种技术一串联,整个产品逻辑流程就非常清晰了。

关键源码讲解

完整的源代码在:deepin-picker github, 下面是关键源代码的讲解:

// 设置窗口属性
// X11BypassWindowManagerHint表示不受窗口管理器控制, 好把取色透明窗口铺满全屏
// WindowStaysOnTopHint 表示窗口永远置顶
// FramelessWindowHint 表示窗口不显示窗口边框和标题栏
// Tool 利用Qt::Tool的特有属性实现不在任务栏显示图标
setWindowFlags(Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool);
// 设置窗口背景透明
setAttribute(Qt::WA_TranslucentBackground, true);

...

// 得到光标位置
cursorX = QCursor::pos().x();
cursorY = QCursor::pos().y();

// 获取屏幕光标处的截图,并放大一定倍数实现放大镜的视觉
screenshotPixmap = QApplication::primaryScreen()->grabWindow(
            0,
            cursorX - size / 2,
            cursorY - size / 2,
            size,
            size).scaled(width, height);

// 创建一个空的 cursorPixmap 用于填充光标色块图形
QPainter painter(&cursorPixmap);

// 打开绘制反锯齿,使得放大镜的圆形边框是无锯齿的
painter.setRenderHint(QPainter::Antialiasing, true);

// 把光标处的色块画成圆形的样子
painter.save();
QPainterPath circlePath;
circlePath.addEllipse(2 + offsetX, 2 + offsetY, width - 4, height - 4);
painter.setClipPath(circlePath);
painter.drawPixmap(1 + offsetX, 1 + offsetY, screenshotPixmap);
painter.restore();

...

// 设置光标为放大镜的图形
QApplication::setOverrideCursor(QCursor(cursorPixmap));

最后

做一个好的产品就像手工打造一把军刀,每一个菱角,每一个刀锋都经过精心思考,每一个产品的操作都是在充分研究用户的心理后,顺着用户的思绪对产品的功能进行自然的延伸和操作,用户遇到的每一个逻辑的转角,都完全符合用户的下一步心理预期,所有操作都一气呵成,操作完以后给用户一种好似泉水一般的清爽,有触感而无形,不需要用户过多思考即可自然完成用户期望的操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容