如何快速的合成各类样式的图片

在某Q和某信中都有咱们熟悉的公众号和结构化消息,例如html

又或者这样:linux

这些图片都是固定的,每一个用户看到的图片都是同一张。第一张,实在没有太多的点击欲望!第二张就还算凑合吧,不过哪来每天这么多福利图!c++

若是想让每一个用户看到的消息有所不一样,因人而异,咱们须要依赖终端作相应的开发。git

例如某某运动的消息:github

这种方式就比固定图片好不少了,用户的点击欲望明显增强。很是好!除了开发周期慢一点以外。web

呃,不妙,很快又要过阅兵节,而后是月饼节!老板想要作更炫酷的消息,怎么办?立刻打电话给终端同窗:“喂!喂?喂!3天能给我修改好模版吗?”。不过终端同窗不是神,作得出来也发布不了。。。算法

那么,咱们聪明的产品同窗就想到了“动态合成图片”的高招,立刻找到咱们先后台开发。咱们一碰面,又立刻敲定能够搞起,问题就是怎么搞起了。api

 

好啦,废话说完,该进入正题了。浏览器

 

消息的样式:服务器

设计同窗是绝不手软啊,真当这里是网页同样,各类设计各类字体各类效果各类进度条。

 

项目状况:

  • 100万活跃用户
  • 2小时内推送200万个消息

200万个消息也就是200万个图片,2小时内推送完成,也就是每秒大概300张图片。3ms一张图片?开什么玩笑?你觉得每一个服务器都有博尔特这么快吗!你要知道,这个图片并不小(630*350),要拼图,还要编码为JPG/PNG什么的。

挑战是存在的,需求是要作的,任务是要完成的。咱们开始研究合成图的各类现成的方案(那些从零开始实现各类尖端算法的思路就算了),包括:

  • C++图形库;
  • 浏览器截图;
  • Flash。

什么?浏览器截图?什么?Flash?服务器生成图片,跟浏览器和Flash什么事?

别急,咱们慢慢说明。

 

C++图形库:

后台同窗哪一个不是精通C++,因此咱们的后台同窗就开始研究各类C++方案。列出来一大堆:Boost.GIL、CImg、CxImage、FreeImage、Magick++(ImageMagick)、GDCM、ITK、OpenCV、VIGRA、VTK。各类高级术语,吓你一跳。

首先,尝试的是专业的Boost.GIL,但发现api羞涩难懂。后又转到街知巷闻的ImageMagick,不少重构同窗都是用这个库作图片压缩。因而,后台同窗浴血奋战,拼出了第一个效果:

,看来离目标效果不远了。虽然这给人感受win10和win95的感受,但要知道,win95升级四、5次就到win10了。

不过,悲剧的还不是这个丑,悲剧的是,合成一张jpg一共须要耗时300ms!

请容许我掐指一算,300ms一张,一秒3张,要达到一秒300张的目标,就须要100台机器。嗯,大老板这么有钱,应该不会介意的。

好吧,开个玩笑,后台同窗完全放弃了。

 

浏览器截图:

为何要想浏览器截图?其实之前在项目中用过,只不过当时并无这么高的速度要求。毕竟设计稿就很是适合用网页实现,若是浏览器截图的速度能达到要求,那么作这个动态图片的成本就很低了。

有不少linux命令行工具,能够对网页截图,原理是启动webkit渲染网页,而后截图。例如gnome-screenshot、wkhtmltoimage。

实际状况是让人沮丧的,截图随便须要1秒2秒的时间。

不过,这个也是能理解的,毕竟要启动webkit,网页要刷新,再截图,能不慢吗?

 

Flash:

笔者自己作Flash出身,因此对Flash生成图片情有独钟,既然如此,何不拿Flash测试一下呢?

通过测试,咱们发现Flash不单能轻松的完美复现设计的效果,并且截图效率很是高,最终也选择了这个方案。

不过,要让Flash运行在linux服务器上,却是要下一番功夫。

研究的内容包括:

  • Flash player or Air?
  • Flash和C++的通讯?
  • 高效压缩图片?

 

#Flash player or Air?

player和air只是swf运行的两种形式而已,对速度不会有影响。研究这个目的是尝试实现原来的通讯架构,由于Air模式才能在flash侧运行ServerSocket。若是Flash能运行ServerSocket,那么Flash就称为服务提供者,C++须要合成图的时候,只须要链接socket,传输参数,而后接收图片便可。

不过,Adobe于2011年宣布从air 2.7开始再也不支持linux版本,因此否决了Air,仍是继续使用Flash player。另外,要让flash正常运行起来,还须要安装xvfb服务。

 

#Flash和C++的通讯?

为了保证高效的通讯,避免每次截图都重启Flash player,咱们设计了这样的通讯机制:

C++控制Flash的生命周期,按期重启Flash。Flash启动后,立刻连接C++提供的socket服务。链接成功后,C++给Flash分配任务,传输相应的用户数据;Flash接收数据后,拉取用户头像、生成图片并压缩为JPG,再以二进制形式在socket中回传给C++。回传完毕后,Flash保持socket链接,等待新的任务。

 

#高效压缩图片?

图片动态拼接完成后,须要压缩为png或者jpg,又或者更多其余格式。固然,在当前的软件环境来看,jpg和png是惟二的选择了。

咱们作了不少测试,包括:

  • as3core压缩80%的jpg和无损png,也就是as3代码作编码运算
  • 改进版pngencoder:https://github.com/cameron314/PNGEncoder2
  • flascc(alchemy c++加速)压缩80%的jpg(as3_jpeg_wrapper)
  • png8和有损png24,使用的是blooddy(https://github.com/kenkozheng/blooddy),其中也有flascc加速。

大体的状况以下:

image

综合文件大小和压缩时间,咱们暂时选择了flascc压缩的jpg。

但清晰度方面略有欠缺,80%质量的jpg在呈现文字时,边缘会略有模糊。不过这只在大屏机器上有细微的感受。后续可能会考虑改成blooddy压缩的有损png24,虽然文件大小和耗时都比现有方案增长1倍,但图片要清晰一些。不得不赞赏一下blooddy的做者,俄罗斯人作软件要么就不作,要作就是很牛逼的。

有一个基础数据还没列出,就是Flash拼接生成画面的时间。这个却是快的惊人,不算加载图片的时间,只须要8ms左右。

那么最终,Flash生成一张图的时间大概就是40ms。

 

最后,请再容许我掐指一算。默默的打开计算器。。。

40ms*2000000/1000 = 80000s = 22.2小时

 

那么若是同时有10台机器,就大概能够在2小时内发送完成了。虽然100台机器搞不到,10台机器仍是有办法的

固然,实际状况还有图片传输的耗时(实际上这里更大,要传输到公众号平台),实际须要200ms一张图片,不过这些都是异步的,咱们单机启动25个Flash进程,整体运行平滑。

相关文章
相关标签/搜索