本文来自腾讯前端开发工程师“ wendygogogo”的技术分享,作者自评:“在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦。”

1、GIF格式的历史

GIF ( Graphics Interchange Format )原义是“图像互换格式”,是 CompuServe 公司在1987年开发出的图像文件格式,可以说是互联网界的老古董了。

GIF 格式可以存储多幅彩色图像,如果将这些图像((https://www.qcloud.com/document/ ... w.59167.59167.59167)连续播放出来,就能够组成最简单的动画。所以常被用来存储“动态图片”,通常时间短,体积小,内容简单,成像相对清晰,适于在早起的慢速互联网上传播。

本来,随着网络带宽的拓展和视频技术的进步,这种图像已经渐渐失去了市场。可是,近年来流行的表情包文化,让老古董 GIF 图有了新的用武之地。

 
 

表情包通常来源于手绘图像,或是视频截取,目前有很多方便制作表情包的小工具。

这类图片通常具有文件体积小,内容简单,兼容性好(无需解码工具即可在各类平台上查看),对画质要求不高的特点,刚好符合 GIF 图的特性。

所以,老古董 GIF 图有了新的应用场景。

学习交流:

- 即时通讯开发交流3群:185926912[推荐]

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

(本文同步发布于:http://www.52im.net/thread-2032-1-1.html

2、相关文章

腾讯技术分享:社交网络图片的带宽压缩技术演进之路

QQ音乐团队分享:Android中的图片压缩技术详解(上篇)

QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

全面掌握移动端主流图片格式的特点、性能、调优等

基于社交网络的Yelp是如何实现海量用户图片的无损压缩的?

3、技术需求场景

新的应用场景带来新的需求,本文所要探究的技术和要解决的问题来源于某个真实的业务场景下——即为用户批量推送GIF表情包的功能需求。

一批图像大约有200-500张,以缩略图列表的形式展示在客户端。

根据我们使用测试数据进行的统计 GIF 图表情包的尺寸大部分在200k-500k之间,批量推送的一个重要问题就是数据量太大,因此,我们希望能够在列表里展示体积较小的缩略图,用户点击后,再单独拉取原图。

传统的 GIF 缩略图是静态的,通常是提取第一帧,但在表情包的情形下,这种方式不足以表达出图片中信息。

比如下面的例子:

 
 

(左为原始GIF动态图,右为GIF的第一帧)

第一帧完全看不出重点啊!

所以,我们希望缩略图也是动态的,并尽可能和原图相似。

对于传统图片来说,文件大小一般和图片分辨率(尺寸)正相关,所以,生成缩略图最直观的思路就是缩小尺寸,resize大法。

但是在 GIF 图的场合,这个方式不再高效,因为 GIF 图的文件大小还受到一个重要的因素制约——帧数

以这张柴犬表情为例,原图宽度200,尺寸1.44M,等比缩放到150之后,尺寸还是1.37M,等比缩放到100,相当于尺寸变为原来的四分之一,体积还是749K。

 
 
 

可见,resize大法的压缩率并不理想,收效甚微。

而且,我们所得到的大部分表情图素材,分辨率已经很小了,为了保证客户端展示效果,不能够过度减少尺寸,不然图片会变得模糊。

所以,想要对GIF图进行压缩,只能从别的方向入手。

4、GIF技术详解:拆解GIF格式

4.1 基本

想要压缩一个文件,首先要了解它是如何存储的。毕竟,编程的事,万变不离其宗嘛。

 

作为一种古老的格式,GIF的存储规则也相对简单,容易理解。

一个GIF文件主要由以下几部分组成:

1)文件头;

2)图像帧信息;

3)注释。

下面我们来分别探究每个部分。

4.2 文件头

GIF格式文件头和一般文件头差别不大,也包含有:

1)格式声明;

2)逻辑屏幕描述块;

3)全局调色盘;

格式声明:

 

Signature 为“GIF”3 个字符;Version 为“87a”或“89a”3 个字符。

逻辑屏幕描述块:

 

前两字节为像素单位的宽、高,用以标识图片的视觉尺寸。

Packet里是调色盘信息,分别来看:

1)Global Color Table Flag为全局颜色表标志,即为1时表明全局颜色表有定义;

2)Color Resolution 代表颜色表中每种基色位长(需要+1),为111时,每个颜色用8bit表示,即我们熟悉的RGB表示法,一个颜色三字节;

3)Sort Flag 表示是否对颜色表里的颜色进行优先度排序,把常用的排在前面,这个主要是为了适应一些颜色解析度低的早期渲染器,现在已经很少使用了;

4)Global Color Table 表示颜色表的长度,计算规则是值+1作为2的幂,得到的数字就是颜色表的项数,取最大值111时,项数=256,也就是说GIF格式最多支持256色的位图,再乘以Color Resolution算出的字节数,就是调色盘的总长度。

这四个字段一起定义了调色盘的信息。

Background color Index 定义了图像透明区域的背景色在调色盘里的索引。

Pixel Aspect Ratio 定义了像素宽高比,一般为0。

什么是调色盘?我们先考虑最直观的图像存储方式,一张分辨率M×N的图像,本质是一张点阵,如果采用Web最常见的RGB三色方式存储,每个颜色用8bit表示,那么一个点就可以由三个字节(3BYTE = 24bit)表达,比如0xFFFFFF可以表示一个白色像素点,0x000000表示一个黑色像素点。

如果我们采用最原始的存储方式,把每个点的颜色值写进文件,那么我们的图像信息就要占据就是3×M×N字节,这是静态图的情况,如果一张GIF图里有K帧,点阵信息就是3×M×N×K。

下面这张兔子snowball的表情有18帧,分辨率是200×196,如果用上述方式计算,文件尺寸至少要689K。

 

但实际文件尺寸只有192K,它一定经历过什么……

我们可以使用命令行图片处理工具gifsicle来看看它的信息:

gifsicle -I snowball.gif > snowball.txt

我们得到下面的文本:

5.gif 19 images

logical screen 200x196

global color table (128)

background 93

loop forever

extensions 1

+ image #0 200x196 transparent 93

disposal asis delay 0.04s

+ image #1 200x188 transparent 93

disposal asis delay 0.04s

........

可以看到,global color table 128就是它的调色盘,长度128。

为了确认,我们再用二进制查看器查看一下它的文件头:

 

可以看到Packet里的字段的确符合我们的描述。

在实际情况中,GIF图具有下面的特征:

1)一张图像最多只会包含256个RGB值;

2)在一张连续动态GIF里,每一帧之间信息差异不大,颜色是被大量重复使用的。

在存储时,我们用一个公共的索引表,把图片中用到的颜色提取出来,组成一个调色盘,这样,在存储真正的图片点阵时,只需要存储每个点在调色盘里的索引值。

如果调色盘放在文件头,作为所有帧公用的信息,就是公共(全局)调色盘,如果放在每一帧的帧信息中,就是局部调色盘。GIF格式允许两种调色盘同时存在,在没有局部调色盘的情况下,使用公共调色盘来渲染。

 

这样,我们可以用调色盘里的索引来代表实际的颜色值。

一个256色的调色盘,24bit的颜色只需要用9bit就可以表达了。

调色盘还可以进一步减少,128色,64色,etc,相应的压缩率就会越来越大……

还是以兔子为例,我们还可以尝试指定它的调色盘大小,对它进行重压缩:

gifsicle --colors=64 5.gif > 5-64.gif

gifsicle --colors=32 5.gif > 5-32.gif

gifsicle --colors=16 5.gif > 5-16.gif

gifsicle --colors=2 5.gif > 5-2.gif

......

依然使用gifsicle工具,colors参数就是调色盘的长度,得到的结果:

 
 
 
 

注意到了2的时候,图像已经变成了黑白二值图。

居然还能看出是个兔子……

所以我们得出结论——如果可以接受牺牲图像的部分视觉效果,就可以通过减色来对图像做进一步压缩。

文件头所包含的对我们有用的信息就是这些了,我们继续往后看。

4.3 帧信息描述

帧信息描述就是每一帧的图像信息和相关标志位,在逐项了解它之前,我们首先探究一下帧的存储方式。

我们已经知道调色盘相关的定义,除了全局调色盘,每一帧可以拥有自己的局部调色盘,渲染顺序更优先,它的定义方式和全局调色盘一致,只是作用范围不同。

直观地说,帧信息应该由一系列的点阵数据组成,点阵中存储着一系列的颜色值。点阵数据本身的存储也是可以进行压缩的,GIF图所采用的是LZW压缩算法。

这样的压缩和图像本身性质无关,是字节层面的,文本信息也可以采用(比如常见的gzip,就是LZW和哈夫曼树的一个实现)。

基于表查询的无损压缩是如何进行的?基本思路是,对于原始数据,将每个第一次出现的串放在一个串表中,用索引来表示串,后续遇到同样的串,简化为索引来存储(串表压缩法)。

举一个简单的例子来说明LZW算法的核心思路。

有原始数据:ABCCAABCDDAACCDB

可以看出,原始数据里只包括4个字符A,B,C,D,四个字符可以用2bit的索引来表示,0-A,1-B,2-C,3-D。

原始字符串存在重复字符,比如AB,CC,都重复出现过。用4代表AB,5代表CC,上面的字符串可以替代表示为45A4CDDAA5DB

这样就完成了压缩,串长度从16缩减到12。对原始信息来说,LZW压缩是无损的。

除了采用LZW之外,帧信息存储过程中还采取了一些和图像相关的优化手段,以减小文件的体积,直观表述就是——公共区域排除、透明区域叠加

这是ImageMagick官方范例里的一张GIF图:

 

根据直观感受,这张图片的每一帧应该是这样的:

 

但实际上,进行过压缩优化的图片,每一帧是这样的:

 

首先,对于各帧之间没有变化的区域进行了排除,避免存储重复的信息。

其次,对于需要存储的区域做了透明化处理,只存储有变化的像素,没变化的像素只存储一个透明值。

这样的优化在表情包中也是很常见的,举个栗子:

 

上面这个表情的文件大小是278KB,帧数是14

我们试着用工具将它逐帧拆开,这里使用另一个命令行图像处理工具ImageMagick:

gm convert source.gif target_%d.gif

 

可以看出,除了第一帧之外,后面的帧都做了不同程度的处理,文件体积也比第一帧小。

这样的压缩处理也是无损的,带来的压缩比和原始图像的具体情况有关,重复区域越多,压缩效果越好,但相应地,也需要存储一些额外的信息,来告诉引擎如何渲染。

具体包括:

帧数据长宽分辨率,相对整图的偏移位置;

透明彩色索引——填充透明点所用的颜色;

Disposal Method——定义该帧对于上一帧的叠加方式;

Delay Time——定义该帧播放时的停留时间。

其中值得额外说明的是Disposal Method,它定义的是帧之间的叠加关系,给定一个帧序列,我们用怎样的方式把它们渲染成起来。

详细参数定义,可以参考该网站的范例:http://www.theimage.com/animation/pages/disposal.html

Disposal Method和透明颜色一起,定义了帧之间的叠加关系。在实际使用中,我们通常把第一帧当做基帧(background),其余帧向前一帧对齐的方式来渲染,这里不再赘述。

理解了上面的内容,我们再来看帧信息的具体定义,主要包括:

1)帧分隔符;

2)帧数据说明;

3)点阵数据(它存储的不是颜色值,而是颜色索引);

4)帧数据扩展(只有89a标准支持)。

1和3比较直观,第二部分和第四部分则是一系列的标志位,定义了对于“帧”需要说明的内容。

帧数据说明:

 

除了上面说过的字段之外,还多了一个Interlace Flag,表示帧点阵的存储方式,有两种,顺序和隔行交错,为 1 时表示图像数据是以隔行方式存放的。最初 GIF 标准设置此标志的目的是考虑到通信设备间传输速度不理想情况下,用这种方式存放和显示图像,就可以在图像显示完成之前看到这幅图像的概貌,慢慢的变清晰,而不觉得显示时间过长。

帧数据扩展是89a标准增加的,主要包括四个部分。

1)程序扩展结构(Application Extension):主要定义了生成该gif的程序相关信息

 

2)注释扩展结构(Comment Extension):一般用来储存图片作者的签名信息

 

3)图形控制扩展结构(Graphic Control Extension):这部分对图片的渲染比较重要

 

除了前面说过的Dispose Method、Delay、Background Color之外,User Input用来定义是否接受用户输入后再播放下一帧,需要图像解码器对应api的配合,可以用来实现一些特殊的交互效果。

4)平滑文本扩展结构(Plain Text Control Extension):

 

89a标准允许我们将图片上的文字信息额外储存在扩展区域里,但实际渲染时依赖解码器的字体环境,所以实际情况中很少使用。

以上扩展块都是可选的,只有Label置位的情况下,解码器才会去渲染。

5、将技术理论付诸应用——给表情包减负

说完了基本原理,用刚才了解到的技术细节来分析一下我们的实际问题。

给大量表情包生成缩略图,在不损耗原画质的前提下,尽可能减少图片体积,节省用户流量。

之前说过,单纯依靠resize大法不能满足我们的要求,没办法,只能损耗画质了。

主要有两个思路:减少颜色和减少帧数:

1)减少颜色——图片情况各异,标准难以控制,而且会造成缩略图和原图视觉差异比较明显。

2)减少帧数——通过提取一些间隔帧,比如对于一张10帧的动画,提取其中的提取1,3,5,7,9帧。来减少图片的整体体积,似乎更可行。

先看一个成果,就拿文章开头的图做栗子吧:

 
 

看上去连贯性不如以前,但是差别不大,作为缩略图的视觉效果可以接受,由于帧数减小,体积也可以得到明显的优化。体积从428K缩到了140K。

但是,在开发初期,我们尝试暴力间隔提取帧,把帧重新连接压成新的GIF图,这时,会得到这样的图片:

 

主要有两个问题:

1)帧数过快;

2)能看到明显的残留噪点。

分析我们上面的原理,不难找到原因,正是因为大部分GIF存储时采用了公共区域排除和透明区域叠加的优化,如果我们直接间隔抽帧,再拼起来,就破坏了原来的叠加规则,不该露出来的帧露出来了,所以才会产生噪点。

所以,我们首先要把原始信息恢复出来。

两个命令行工具,gifsicle和ImageMagick都提供这样的命令:

gm convert -coalesce source.gif target_%d.gif

gifsicle --unoptimize source.gif > target.gif

 

还原之后抽帧,重建新的GIF,就可以解决问题2了。

注意重建的时候,可以应用工具再进行对透明度和公共区域的优化压缩。

至于问题1,也是因为我们没有对帧延迟参数Delay Time做处理,直接取原帧的参数,帧数减少了,速度一定会加快。

所以,我们需要把抽去的连续帧的总延时加起来,作为新的延迟数据,这样可以保持缩略图和原图频率一致,看起来不会太过鬼畜,也不会太过迟缓。

提取出每一帧的delay信息,也可以通过工具提供的命令来提取:

gm identify -verbose source.gif

gifsicle -I source.gif

在实际应用中,抽帧的间隔gap是根据总帧数frame求出的:

frame<8 gap=1

frame>40 gap=5

delay值的计算还做了归一化处理,如果新生成缩略图的帧间隔平均值大于200ms,则统一加速到均值200ms,同时保持原有节奏,这样可以避免极端情况下,缩略图过于迟缓。

6、具体的代码实践

本文介绍的算法已经应用于手Q热图功能的后台管理系统等,使用Nodejs编写。ImageMagick是一个较为常用的图像处理工具,除了gif还可以处理各类图像文件,有node封装的版本可以使用。gifsicle只有可执行版本,在服务器上重新编译源码后,采用spawn调起子进程的方式实现。

ImageMagick对于图片信息的解析较为方便,可以直接得到结构化信息。gifsicle支持命令管道级联,处理图片速度较快。实际生产过程中,同时采用了两个工具。

const {spawn} = require('child_process');

const image = gm("src2/"+file)

image.identify((err, val) => {

if(!val.Scene){

console.log(file+" has err:"+err)

return

}

let frames_count = val.Scene[0].replace(/\d* of /, '') * 1

let gap = countGap(frames_count)

let delayList = [];

let totaldelay = 0

if(val.Delay!=undefined){

let iii

for(iii = 0; iii < val.Delay.length; iii ++) {

delayList[iii] = val.Delay[iii].replace(/x\d*/, '') * 1

totaldelay+=delayList[iii]

}

for(; iii < val.Scene.length; iii ++) {

delayList[iii] = 8

totaldelay+=delayList[iii]

}

}else{

for(let iii = 0; iii < val.Scene.length; iii ++) {

delayList[iii] = 8

totaldelay+=delayList[iii]

}

}

let totalFrame = parseInt(frames_count/gap)

//判断是否速度过慢,需要进行归一加速处理

if(totaldelay/totalFrame>20){

let scale =(totalFrame*1.0*20)/totaldelay

for(let iii = 0; iii < delayList.length; iii ++) {

delayList[iii] = parseInt(delayList[iii] * scale)

}

}

let params=[]

params.push("--colors=255")

params.push("--unoptimize")

params.push("src2/"+file)

let tempdelay = delayList[0]

for(let iii = 1; iii < frames_count; iii ++) {

if(i%gap==0){

params.push("-d"+tempdelay)

params.push("#"+(iii-gap))

tempdelay=0

}

tempdelay += delayList[iii]

}

params.push("--optimize=3")

params.push("-o")

params.push("src2/"+file+"gap-keepdelay.gif")

spawn("gifsicle", params, { stdio: 'inherit'})

})

测试时,采用该算法随机选择50张gif图进行压缩,原尺寸15.5M被压缩到6.0M,压缩比38%,不过由于该算法的压缩比率和具体图片质量、帧数、图像特征有关,测试数据仅供参考。

本文到这里就结束了,原来看似简单的表情包,也有不少文章可做。

谢谢观看,希望文中介绍的知识和研究方法对你有所启发。

附录:来自即时通讯大厂的分享

[1] QQ、微信团队原创技术文章:

微信朋友圈千亿访问量背后的技术挑战和实践总结

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

微信团队分享:微信移动端的全文检索多音字问题解决方案

腾讯技术分享:Android版手机QQ的缓存监控与优化实践

微信团队分享:iOS版微信的高性能通用key-value组件技术实践

微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

腾讯技术分享:Android手Q的线程死锁监控系统技术实践

微信团队原创分享:iOS版微信的内存监控系统技术实践

让互联网更快:新一代QUIC协议在腾讯的技术实践分享

iOS后台唤醒实战:微信收款到账语音提醒技术总结

腾讯技术分享:社交网络图片的带宽压缩技术演进之路

微信团队分享:视频图像的超分辨率技术原理和应用场景

微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

QQ音乐团队分享:Android中的图片压缩技术详解(上篇)

QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

腾讯团队分享:手机QQ中的人脸识别酷炫动画效果实现详解

腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享

微信团队分享:微信Android版小视频编码填过的那些坑

微信手机端的本地数据全文检索优化之路

企业微信客户端中组织架构数据的同步更新方案优化实战

微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

QQ 18年:解密8亿月活的QQ后台服务接口隔离技术

月活8.89亿的超级IM微信是如何进行Android端兼容测试的

以手机QQ为例探讨移动端IM中的“轻应用”

一篇文章get微信开源移动端数据库组件WCDB的一切!

微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化

微信后台基于时间序的海量数据冷热分级架构设计实践

微信团队原创分享:Android版微信的臃肿之困与模块化实践之路

微信后台团队:微信后台异步消息队列的优化升级实践分享

微信团队原创分享:微信客户端SQLite数据库损坏修复实践

腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率

腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)

腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(上篇)

微信Mars:微信内部正在使用的网络层封装库,即将开源

如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源

开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]

微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解

微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)

微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)

Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]

微信团队原创分享:Android版微信从300KB到30MB的技术演进

微信技术总监谈架构:微信之道——大道至简(演讲全文)

微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]

如何解读《微信技术总监谈架构:微信之道——大道至简》

微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]

微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案

微信朋友圈海量技术之道PPT [附件下载]

微信对网络影响的技术试验及分析(论文全文)

一份微信后台技术架构的总结性笔记

架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]

快速裂变:见证微信强大后台架构从0到1的演进历程(一)

快速裂变:见证微信强大后台架构从0到1的演进历程(二)

微信团队原创分享:Android内存泄漏监控和优化技巧总结

全面总结iOS版微信升级iOS9遇到的各种“坑”

微信团队原创资源混淆工具:让你的APK立减1M

微信团队原创Android资源混淆工具:AndResGuard [有源码]

Android版微信安装包“减肥”实战记录

iOS版微信安装包“减肥”实战记录

移动端IM实践:iOS版微信界面卡顿监测方案

微信“红包照片”背后的技术难题

移动端IM实践:iOS版微信小视频功能技术方案实录

移动端IM实践:Android版微信如何大幅提升交互性能(一)

移动端IM实践:Android版微信如何大幅提升交互性能(二)

移动端IM实践:实现Android版微信的智能心跳机制

移动端IM实践:WhatsApp、Line、微信的心跳策略分析

移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)

移动端IM实践:iOS版微信的多设备字体适配方案探讨

信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑

腾讯信鸽技术分享:百亿级实时消息推送的实战经验

IPv6技术详解:基本概念、应用现状、技术实践(上篇)

IPv6技术详解:基本概念、应用现状、技术实践(下篇)

腾讯TEG团队原创:基于MySQL的分布式数据库TDSQL十年锻造经验分享

微信多媒体团队访谈:音视频开发的学习、微信的音视频技术和挑战等

了解iOS消息推送一文就够:史上最全iOS Push技术详解

腾讯技术分享:微信小程序音视频技术背后的故事

腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面

微信多媒体团队梁俊斌访谈:聊一聊我所了解的音视频技术

腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天

腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)

腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践

>> 更多同类文章 ……

[2] 有关QQ、微信的技术故事:

技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail

QQ和微信凶猛成长的背后:腾讯网络基础架构的这些年

闲话即时通讯:腾讯的成长史本质就是一部QQ成长史

2017微信数据报告:日活跃用户达9亿、日发消息380亿条

腾讯开发微信花了多少钱?技术难度真这么大?难在哪?

技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码

技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史

技术往事:“QQ群”和“微信红包”是怎么来的?

开发往事:深度讲述2010到2015,微信一路风雨的背后

开发往事:微信千年不变的那张闪屏图片的由来

开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)

一个微信实习生自述:我眼中的微信开发团队

首次揭秘:QQ实时视频聊天背后的神秘组织

为什么说即时通讯社交APP创业就是一个坑?

微信七年回顾:历经多少质疑和差评,才配拥有今天的强大

前创始团队成员分享:盘点微信的前世今生——微信成功的必然和偶然

即时通讯创业必读:解密微信的产品定位、创新思维、设计法则等

QQ的成功,远没有你想象的那么顺利和轻松

QQ现状深度剖析:你还认为QQ已经被微信打败了吗?

[技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-2032-1-1.html

腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践的更多相关文章

  1. 【转载】NeurIPS 2018 | 腾讯AI Lab详解3大热点:模型压缩、机器学习及最优化算法

    原文:NeurIPS 2018 | 腾讯AI Lab详解3大热点:模型压缩.机器学习及最优化算法 导读 AI领域顶会NeurIPS正在加拿大蒙特利尔举办.本文针对实验室关注的几个研究热点,模型压缩.自 ...

  2. 【Big Data - Hadoop - MapReduce】通过腾讯shuffle部署对shuffle过程进行详解

    摘要: 通过腾讯shuffle部署对shuffle过程进行详解 摘要:腾讯分布式数据仓库基于开源软件Hadoop和Hive进行构建,TDW计算引擎包括两部分:MapReduce和Spark,两者内部都 ...

  3. Typora+图床详解(小白都能学得会)

    Typora+图床详解(小白都能学得会) 1 了解工作 博客中用的笔记软件--Typora(Markdown语法) 博客中用的图床--阿里云对象存储(Object Storage Service,简称 ...

  4. python编写微信公众号首图思路详解

    前言 之前一直在美图秀秀调整自己的微信公众号首图,效果也不尽如人意,老是调来调去,最后发出来的图片被裁剪了一大部分,丢失部分关键信息,十分恼火,于是想着用python写一个程序,把微信公众号首图的模式 ...

  5. MySQL系列详解八:MySQL多线程复制演示-技术流ken

    前言 Mysql 采用多线程进行复制是从 Mysql 5.6 开始支持的内容,但是 5.6 版本下有缺陷,虽然支持多线程,但是每个数据库只能一个线程,也就是说如果我们只有一个数据库,则主从复制时也只有 ...

  6. 基础篇:4.1)规范化:3d工程图纸出图步骤详解

    本章目的:按照工程图出图步骤,更方便出具规范的工程图. 1.工程出图步骤 这是作者个人归纳的步骤,供同行业工程师参考完善. 以solidworks为例,工程出图步骤如下:1.1)打开绘制的3d零件图, ...

  7. ps切图抠图详解-web前端(转)

    网页设计在技术层面上,第一步是美工做出网页效果图,第二步就是网页前端进行网页切图.网页切图工具常用的有fireworks.PS,这里使用PS进行网页切图. 我们通过设计稿,得到我们想要的产出物(如.p ...

  8. 【好程序员笔记分享】——UIView与CALayer详解

    -iOS培训,iOS学习-------型技术博客.期待与您交流!------------ UIView与CALayer详解 研究Core Animation已经有段时间了,关于Core Animati ...

  9. 详解ROMA Connect API 流控实现技术

    摘要:本文将详细描述API Gateway流控实现,揭开高性能秒级流控的技术细节. 1.概述 ROMA平台的核心系统ROMA Connect源自华为流程IT的集成平台,在华为内部有超过15年的企业业务 ...

随机推荐

  1. tiny4412--linux驱动学习(1)

    1,概述 linux设备驱动分为三种:字符驱动设备.块驱动设备.网络设备 架构: 1,字符设备驱动 是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符 ...

  2. PEP8 规范

    Python PEP8 编码规范中文版   原文链接:http://legacy.python.org/dev/peps/pep-0008/ item detail PEP 8 Title Style ...

  3. CentOS7 修改静态IP地址

    Ip配置文件在/etc/sysconfig/network-scripts文件夹下,查找该文件的方法为: [root@localhost ~]# ll /etc/sysconfig/network-s ...

  4. MySQL联接查询算法(NLJ、BNL、BKA、HashJoin)

    一.联接过程介绍 为了后面一些测试案例,我们事先创建了两张表,表数据如下:   1 2 3 4 CREATE TABLE t1 (m1 int, n1 char(1)); CREATE TABLE t ...

  5. 解决Dynamic Web Module 3.1 requires Java 1.7 or newer

    需要检查和确认Eclipse项目属性的java版本和本地jdk的版本一致,以1.7版本为例: 1. 在 Java Build Path的libraries中修改对应的JDK版本为1.7: 2.在Jav ...

  6. Markdown使用小总结[不定时更新]

    title: Markdown使用小总结 date: 2019-03-27 10:09:19 tags: Markdown --- 鸽了这么久,Markdown使用下降,因此写一篇博客来总结一下至今( ...

  7. 面试简单整理之Redis

    179.redis 是什么?都有哪些使用场景? Redis是一个key-value存储系统. 缓存,消息队列,排行榜/计数器,分布式架构,做session共享 180.redis 有哪些功能? 181 ...

  8. 常用Linux VPS/服务器SSH连接工具 - Xshell下载与使用

    我们很多网友可能初次接触Linux VPS.服务器,所以在购买完毕VPS主机不知道如何登录.有些网友甚至直接类似WIN系统一样直接在桌面远程连接工具连接,可想而知肯定是无法连接的.因为如果我们购买的是 ...

  9. idea实现热部署并且开启自动编译

    [注]本文转自https://blog.csdn.net/z15732621582/article/details/79439359博文,如有冒犯,请联系博主: 问题描述: 最近在调试代码并进行本地测 ...

  10. 4-1 requests库的安装

    1.  一切安装从简就行了直接打开cmd命令窗口输入 pip install requests  把复杂的问题简单化,我这个已经安装好了.会提示已经安装. 2.打开cmd 输入python 这个会提示 ...