在我们展开去讨论Bitmap之前,我们可以看看以下的几个问题。
1、什么是Bitmap,它是怎么存储的?
2、什么是PNG,JPG,JPEG,GIF,WEBP,他们本质是什么文件?
3、一般地GIF为什么没有高清大图?
4、图片的压缩怎么做,分哪些方式?
5、图片在内存中所占的大小怎么算?
6、客户端怎么把图片做到极致?
啥是Bitmap?
位图(Bitmap),又称栅格图(英语:Raster graphics)或点阵图,是使用像素阵列(Pixel-array/Dot-matrix点阵)来表示的图像。 ---百度百科
Bitmap,即位图。它本质上就是一张图片的内容在内存中的表达形式。那么,Bitmap是通过什么方式表示一张图片的内容呢?
Bitmap原理:从纯数学的角度,任何一个面都由无数个点组成。但是对于图片而言,我们没必要用无数个点来表示这个图片,毕竟单独一个微小的点人类肉眼是看不清的。换句话说,由于人类肉眼的能力有限,我们只需要将一张图片表示为 有限但足够多的点即可。点的数量不能无限,因为无限的点信息量太大无法存储;但是点的数量也必须足够多,否则视觉上无法形成连贯性。这里的点就是像素。比如说,某个658X800的图片,这里的像素总数即为1262X600个。
图片、屏幕的分辨率与像素之间的关系介绍
将图片内容表示为有限但足够多的像素的集合,这个“无限→有限”的思想极其迷人。所以,我们只需要将每个像素的信息存储起来,就意味着将整个图片的内容进行了表达。
像素信息:每个像素的信息,无非就是ARGB四个通道的值。其中,A代表透明度,RGB代表红绿蓝三种颜色通道值。每个通道的值范围在0~255之间,即有256个值,刚好可以通过一个字节(8bit)进行表示。所以,每个通道值由一个字节表示,四个字节表示一个像素信息,这似乎是最好的像素信息表示方案。
但是这里忽略了两个现实的需求问题:
1、在实际需求中,我们真的需要这么多数量的颜色吗?上述方案是256X256X256种。有的时候,我们并不需要这么丰富的颜色数量,所以可以适当减少表示每个颜色通道的bit位数。这么做的好处是节省空间。也就是说,每个颜色通道都采用8bit来表示是代表全部颜色值的集合;而我们可以采用少于8bit的表示方式,尽管这会缺失一部分颜色值,但是只要颜色够用即可,并且这还可以节省内存空间。
2、我们真的需要透明度值吗?如果我们需要某个图片作为背景或者图标,这个图片透明度A通道值是必要的。但是如果我们只是普通的图片展示,比如拍摄的照片,透明度值毫无意义。细想一下,你希望你手机自拍的照片透明或者半透明吗?hell no! 因此,透明度这个通道值是否有必要表示也是根据需求自由变化的。
具体每个像素点存储ARGB值的方案介绍,后面会详细介绍。
总结:Bitmap对象本质是一张图片的内容在内存中的表达形式。它将图片的内容看做是由存储数据的有限个像素点组成;每个像素点存储该像素点位置的ARGB值。每个像素点的ARGB值确定下来,这张图片的内容就相应地确定下来了。
详细介绍
1、Bitmap.Config
Config是Bitmap的一个枚举内部类,它表示的就是每个像素点对ARGB通道值的存储方案。取值有以下四种:
ARGB_8888:这种方案就是上面所说的每个通道值采8bit来表示,每个像素点需要4字节的内存空间来存储数据。该方案图片质量是最高的,但是占用的内存也是最大的 也是说他是32位通道或者说它的色深为32-bit。
色深: 指的是每一个像素点用多少bit来存储ARGB值,属于图片自身的一种属性。色深可以用来衡量一张图片的色彩处理能力(即色彩丰富程度)。
典型的色深是8-bit、16-bit、24-bit和32-bit等。
上述的Bitmap.Config参数的值指的就是色深。比如ARGB_8888方式的色深为32位,RGB_565方式的色深是16位。
ARGB_4444:这种方案每个通道都是4位,每个像素占用2个字节,图片的失真比较严重。一般不用这种方案。
RGB_565:这种方案RGB通道值分别占5、6、5位,但是没有存储A通道值,所以不支持透明度。每个像素点占用2字节,是ARGB_8888方案的一半 也是说他是16位通道或者说它的色深为16-bit。
ALPHA_8:这种方案不支持颜色值,只存储透明度A通道值,使用场景特殊,比如设置遮盖效果等。
2、位深与色深
我们知道了图片在内存中和在磁盘上的两种不同的表示形式:前者为Bitmap,后者为各种压缩格式。这里在介绍一下位深的概念:
位深 指的是在对Bitmap进行压缩存储时存储每个像素所用的bit数,主要用于存储。由于是“压缩”存储,所以位深一般小于或等于色深 。
举个例子:某张图片100像素*100像素 色深32位(ARGB_8888),保存时位深度为24位,那么:
size = width*height*bitCount
该图片在内存中所占大小为:100 * 100 * (32 / 8) Byte
在文件中所占大小为 100 * 100 * ( 24/ 8 ) * 压缩率 Byte
注意:这里的100*100 值的是像素值,不是图片的宽度,要是图片的宽度是100dp 那还要转换成px 像素值 在Android里假设当前屏幕的密度为240dip根据换算的公式px = dp * (dpi / 160)
得出100dp的像素值为150px
比较分析:一般我们在ARGB_8888方式和RGB_565方式中进行选取:不需要设置透明度时,比如拍摄的照片等,RGB_565是个节省内存空间的不错的选择;既要设置透明度,对图片质量要求又高,就用ARGB_8888。
3、Bitmap的压缩存储
Bitmap是图片内容在内存中的表示形式,那么如果想要将Bitmap对象进行持久化存储为一张本地图片,需要对Bitmap对象表示的内容进行压缩存储。根据不同的压缩算法可以得到不同的图片压缩格式(简称为图片格式),比如GIF、JPEG、BMP、PNG和WebP等。这些图片的(压缩)格式可以通过图片文件的后缀名看出。
换句话说:Bitmap是图片在内存中的表示,GIF、JPEG、BMP、PNG和WebP等格式图片是持久化存储后的图片。内存中的Bitmap到磁盘上的GIF、JPEG、BMP、PNG和WebP等格式图片经过了”压缩”过程,磁盘上的GIF、JPEG、BMP、PNG和WebP等格式图片到内存中的Bitmap经过了“解压缩”的过程。
那么,为什么不直接将Bitmap对象进行持久化存储而是要对Bitmap对象进行压缩存储呢?这么做依据的思想是:当图片持久化保存在磁盘上时,我们应该尽可能以最小的体积来保存同一张图片的内容,这样有利于节省磁盘空间;而当图片加载到内存中以显示的时候,应该将磁盘上压缩存储的图片内容完整地展开。前者即为压缩过程,目的是节省磁盘空间;后者即为解压缩过程,目的是在内存中展示图片的完整内容。
3.1、有损压缩和无损压缩
Bitmap压缩存储时的算法有很多种,但是整体可分为两类:有损压缩和无损压缩。
1、有损压缩
有损压缩的基本依据是:人的眼睛对光线的敏感度远高于对颜色的敏感度,光线对景物的作用比颜色的作用更为重要。有损压缩的原理是:保持颜色的逐渐变化,删除图像中颜色的突然变化。生物学中的大量实验证明,人类大脑会自发地利用与附近最接近的颜色来填补所丢失的颜色。有损压缩的具体实现方法就是删除图像中景物边缘的某些颜色部分。当在屏幕上看这幅图时,大脑会利用在景物上看到的颜色填补所丢失的颜色部分。利用有损压缩技术,某些数据被有意地删除了,并且在图片重新加载至内存中时这些数据也不会还原,因此被称为是“有损”的。有损压缩技术可以灵活地设置压缩率。
无可否认,利用有损压缩技术可以在位图持久化存储的过程中大大地压缩图片的存储大小,但是会影响图像质量,这一点在压缩率很高时尤其明显。所以需要选择恰当的压缩率。
工具推荐:
1.ImageAlpha
ImageAlpha与ImageOptim是同一个作者,不过ImageAlpha属于有损压缩,因此图片质量会受到影响。所以使用ImageAlpha对PNG图片进行压缩后,必须让设计师检视一下优化后的PNG图片,以免影响APP的视觉效果。但是ImageAlpha的优点是可以极大减少PNG图片的体积大小。
ImageAlpha工具的网址为:https://pngmini.com
2.TinyPNG
前面两个工具是应用程序,TinyPNG是一个Web站点。你可以上传原PNG图片,它对PNG图片压缩后你就可以下载优化后的结果了。因为TinyPNG也是有损压缩,所以优缺点同②
TinyPNG的网址为:https://tinypng.com
2、无损压缩
无损压缩的基本原理是:相同的颜色信息只需保存一次。具体过程是:首先会确定图像中哪些区域是相同的,哪些是不同的。包括了重复数据的区域就可以被压缩,只需要记录该区域的起始点即可。
从本质上看,无损压缩的方法通过删除一些重复数据,也能在位图持久化存储的过程中减少要在磁盘上保存的图片大小。但是,如果将该图片重新读取到内存中,重复数据会被还原。因此,无损压缩的方法并不能减少图片的内存占用量,如果要减少图片占用内存的容量,就必须使用有损压缩方法。
无损压缩方法的优点是能够比较好地保存图像的质量,但是相对来说这种方法的压缩率比较低。
对比分析:有损压缩压缩率高而且可以灵活设置压缩率,并且删除的数据不可还原,因此可以减少图片的内存占用,但是对图片质量会有一定程度的影响;无损压缩可以很好地保存图片质量,也能保证一定的压缩率虽然没有有损压缩那么高,并且无损压缩删除的数据在重新加载至内存时会被还原,因此不可以减少图片的内存占用。
工具推荐:
ImageOptim
ImageOptim是一种无损压缩工具,所以你不用担心利用该工具对PNG图片进行压缩后图片质量会受影响。它的压缩原理是:优化PNG压缩参数,移除冗余元数据以及非必需的颜色配置文件等,在不牺牲图片质量的前提下,既减少了PNG图片的大小,又提高了其加载的速度。
ImageOptim工具的网址为:https://imageoptim.com
3.2常见的压缩格式
Bitmap的压缩格式就是最终持久化存储得到的图片格式,一般由后缀名即可看出该图片采用了何种压缩方式。不同的压缩方式的压缩算法不一样。常见的主要有:
1、Gif
Gif是一种基于LZW算法的无损压缩格式,其压缩率一般在50%左右。Gif可插入多帧,从而实现动画效果。因此Gif图片分为静态GIF和动画GIF两种GIF格式。
由于Gif以8位颜色压缩存储单个位图,所以它最多只能用256种颜色来表现物体,对于色彩复杂的物体它就力不从心了。因此Gif不适合用于色彩非常丰富的图片的压缩存储,比如拍摄的真彩图片等。
2、BMP
BMP是标准图形格式,它是包括Windows在内多种操作系统图像展现的终极形式。其本质就是Bitmap对象直接持久化保存的位图文件格式,由于没有进行压缩存储,因此体积非常大,故而不适合在网络上传输。同时也是因为这种格式是对Bitmap对象的直接存储而没有进行压缩,因此我们在讨论压缩格式时往往忽略这一种。
3、PNG
PNG格式本身的设计目的是替代GIF格式,所以它与GIF 有更多相似的地方。PNG格式也属于无损压缩,其位深为32位,也就是说它支持所有的颜色类型。
同样是无损压缩,PNG的压缩率高于Gif格式,而且PNG支持的颜色数量也远高于Gif,因此:如果是对静态图片进行无损压缩,优先使用PNG取代Gif,因为PNG压缩率高、色彩好;但是PNG不支持动画效果。所以Gif仍然有用武之地。
PNG缺点是:由于是无损压缩,因此PNG文件的体积往往比较大。如果在项目中多处使用PNG图片文件,那么在APP瘦身时需要对PNG文件进行优化以减少APP体积大小。具体做法后面会详细介绍。
4、JPEG
JPEG是一种有损压缩格式,JPEG图片以24位颜色压缩存储单个位图。也就是说,JPEG不支持透明通道。JPEG也不支持多帧动画。
因为是有损压缩,所以需要注意控制压缩率以免图片质量太差。
JPG和JPEG没有区别,全名、正式扩展名是JPEG。但因DOS、Windows95等早期系统采用的8.3命名规则只支持最长3字符的扩展名,为了兼容采用了.jpg。也因历史习惯和兼容性的考虑,.jpg目前更流行。
JPEG2000作为JPEG的升级版,其压缩率比JPEG高约30%左右,同时支持有损和无损压缩。JPEG2000格式有一个极其重要的特征在于它能实现渐进传输,即先传输图像的轮廓,然后逐步传输数据,不断提高图像质量,让图像由朦胧到清晰显示。此外,JPEG2000还支持所谓的“感兴趣区域”特性,也就是可以任意指定影像上感兴趣区域的压缩质量;另外,JPEG2000还可以选择指定的部分先解压缩来加载到内存中。JPEG2000和JPEG相比优势明显,且向下兼容,因此可取代传统的JPEG格式。
5、WebP
WebP 是 Google 在 2010 年发布的图片格式,希望以更高的压缩率替代 JPEG。它用 VP8 视频帧内编码作为其算法基础,取得了不错的压缩效果。WebP支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,并且没有版权问题,是一种非常理想的图片格式。WebP支持动图,基本取代gif。
WebP不仅集成了PNG、JPEG和Gif的所有功能,而且相同质量的无损压缩WebP图片体积比PNG小大约26%;如果是有损压缩,相同质量的WebP图片体积比JPEG小25%-34%。
很多人会认为,既然WebP功能完善、压缩率更高,那直接用WebP取代上述所有的图片压缩格式不就行了吗?其实不然,WebP也有其缺点:我们知道JPEG是有损压缩而PNG是无损压缩,所以JPEG的压缩率高于PNG;但是有损压缩的算法决定了其压缩时间一定是高于无损压缩的,也就是说JPEG的压缩时间高于PNG。而WebP无论是无损还是有损压缩,压缩率都分别高于PNG和JPEG;与其相对应的是其压缩时间也比它们长的多。经测试,WebP图片的编码时间比JPEG长8倍。可以看出,时间和空间是一对矛盾;如果想要节省更多的空间,必然要付出额外的时间;如果想要节省时间,那么必然要付出空间的代价。这取决于我们在实际中对于时空不同的需求程度来做出选择。
不管怎么说,WebP还是一种强大的、理想的图片压缩格式,并且借由 Google 在网络世界的影响力,WebP 在几年的时间内已经得到了广泛的应用。看看你手机里的 App:微博、微信、QQ、淘宝等等,每个 App 里都有 WebP 的身影。
另外,WebP是Android4.0才引入的一种图片压缩格式,如果想要在Android4.0以前的版本支持WebP格式的图片,那么需要借助于第三方库来支持WebP格式图片,例如:webp-android-backport函数库,该开源项目在GitHub地址为:https://github.com/alexey-pelykh/webp-android-backport 当然考虑到一般的Android开发中只需要向下兼容到Android4.0即可,所以也可以忽略这个问题。
目前来说,以上所述的五种压缩格式,Android操作系统都提供了原生支持;但是在上层能直接调用的编码方式只有 JPEG、PNG、WebP 这三种。具体的,可以查看Bitmap类的枚举内部类CompressFormat类的枚举值来获取上层能调用的图片编码方式。你会发现枚举值也是JPEG、PNG和WEBP三种。
如果我们想要在应用层使用Gif格式图片,需要自行引入第三方函数库来提供对Gif格式图片的支持。不过一般我们用WebP取代Gif。
因此,我们只需要比较分析PNG、JPEG、WebP这三种压缩格式即可。
3.3、比较分析:
1、对于摄影类等真彩图片:因为我们对这类色彩丰富的图片的透明度没有要求(一般默认为不透明),可以采用JPEG有损压缩格式,因为JPEG本身就不支持透明度,而且因为是有损压缩,所以尽管会牺牲一丢丢照片的质量但是可以大大减少体积。如果非要采用PNG格式,那么首先因为PNG支持透明度通道,所以明明不必要的透明度值却会被存储;其次因为是无损压缩,所以压缩率不会很高从而导致保存的图片非常大!综上比较,建议采用JPEG格式,不要用PNG格式。
JPEG格式可以与Bitmap.Config参数值为RGB_565搭配使用,这是一个理想的设置。
2、对于logo图标、背景图等图片:这类图片的特点是往往是有大块的颜色相同的区域,这与无损压缩的思路不谋而合(即删除重复数据)。而且这类图片对透明度是有要求的,因此可以采用PNG无损压缩格式;尽管使用PNG格式会让图片有点大,但是可以在后续进行PNG图片优化以对APP体积进行瘦身。如果非要采用JPEG格式,那么由于有损压缩的原理(利用人脑的自动补全机制),可能会随机地丢失一些线条导致最终的图片完全不是想要的效果。综上比较,建议使用PNG格式,不要用JPEG格式。
PNG格式可以与Bitmap.Config参数值为ARGB_8888搭配使用,这是一个理想的设置。
当然,以上两种情况,我们都可以使用WebP取代PNG或JPEG,如果我们想要这么做的话。如果你的项目中对空间的需求程度更高,你完全有理由这么做。但是如果你对空间需求程度还OK,你也可以选择分情况使用PNG或JPEG格式。
图片优化
图片优化属于Android性能优化的一种,这里主要是针对PNG图片的大小进行优化,毕竟PNG这种无损压缩格式往往会导致图片都比较大。除非你的项目已经全面支持了WebP格式,否则对PNG格式图片的优化都会是你必须考虑的一点,这有利于减少APP的体积大小。
对PNG图片进行优化的思想是:减少PNG图片的体积,常用方式有:
1、巧用压缩工具或者压缩算法
不同的算法压缩图片(有的是有损有的是无损),我们需要在图片质量和图片大小这对矛盾中根据实际情况进行选择。
2、PNG/JPEG转换为WebP
如果不想对PNG图片进行二次压缩,可以考虑直接将其替换为WebP格式的图片。另外,我们对JPEG格式的图片也可以这么替换。毕竟WebP无论是与PNG还是与JPEG格式想比,压缩后体积大小都小很多。WebP转换工具有:
智图,这是一个图片优化平台,地址为:https://zhitu.isux.us
iSparta,这是一个针对PNG图片的二次压缩和格式转换工具,地址为:https://isparta.github.io
3、使用NinePatch格式的PNG图(Android特有)
.9.png图片格式简称为NinaPatch图,本质上仍然是PNG格式图片。不过它的优点是体积小、拉伸不变形,能够很好地适配Android各种机型。我们可以利用Android Studio提供的功能,右键一张PNG图片点击“create 9=Patch File”即可完成转换。
4、使用SVG
SVG是一种用XML定义的语言,用来描述二维矢量及矢量/栅格图形,它提供了目前网络流行的PNG和JPEG格式无法具备的优势:可以任意放大图形显示,但绝不会以牺牲图像质量为代价;可在SVG图像中保留可编辑和可搜寻的状态;平均来讲,SVG文件比JPEG和PNG格式的文件要小很多,因而下载也很快。可以相信,SVG的开发将会为Web提供新的图像标准。
5、使用切图服务
切图服务是客户端,UED,运营人员,图片服务后端同事共同协作去制定一些规则,保证我们客户端在图片加载上做到按尺寸,按比例,按分辨率等不同纬度去进行图片的处理优化 ,切图服务能为每部手机每张图片得到量身定制的大小尺寸,不浪费一点空间(理论上),在加上端自己平台的优化,可以将图片做到很好!
总结:无论是二次压缩还是格式转换,无论是有损二次压缩还是无损二次压缩,我们都需要根据实际需求进行方案和工具的选择。
BMP文件 在内存中的表示
BMP文件由4部分组成:
位图文件头(bitmap-file header)
位图信息头(bitmap-informationheader)
颜色表(color table)
颜色点阵数据(bits data)
用UltraEdit打开bmp-1.bmp,可以看到这个文件的全部数据如下图所示:
1、位图文件头(BITMAPFILEHEADER)
注意,BMP的数据是倒着念的,这是PC电脑的特色。如果一段数据为50 1A 25 3C,倒着念就是3C 25 1A50,即0x3C251A50。因此,如果bfSize的数据为50 00 00 00,实际上就成了0x00000050,也就是0x50。
名称 | 占用空间 | 内容 | 实际数据 |
---|---|---|---|
bfType | 2字节 | 标识,就是“BM”二字 | BM |
bfSize | 4字节 | 整个BMP文件的大小 | 0x50(80)【与右键查看图片属性里面的大小值一样】 |
bfReserved1/2 | 4字节 | 保留字,没用 | 0 |
bfOffBits | 4字节 | 偏移数,即 位图文件头+位图信息头+调色板 的大小 | 0x36(54) |
2、位图信息头(BITMAPINFOHEADER )
位图信息头共40字节:
名称 | 占用空间 | 内容 | 实际数据 |
---|---|---|---|
biSize | 4字节 | 位图信息头的大小,为40 | 0x28(40) |
biWidth | 4字节 | 位图的宽度,单位是像素 | 0x02 |
biHeight/2 | 4字节 | 位图的高度,单位是像素 | 0x03 |
biPlanes | 2字节 | 固定值1 | 1 |
biBitCount | 2字节 | 每个像素的位数1-黑白图,4-16色,8-256色,24-真彩色 | 0x18(24) |
biCompression | 2字节 | 压缩方式,BI_RGB(0)为不压缩 | 0 |
biSizeImage | 4字节 | 位图全部像素占用的字节数,BI_RGB时可设为0 | 1A |
biXPelsPerMeter | 4字节 | 水平分辨率(像素/米) | - |
biYPelsPerMeter | 4字节 | 垂直分辨率(像素/米) | - |
biClrUsed | 4字节 | 位图使用的颜色数如果为0,则颜色数为2的biBitCount次方 | - |