博主声明:
转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。
本文首发于此 博主:威威喵 | 博客主页:https://blog.csdn.net/smile_running
Android UI 中最离不开的就是图片了,图片可以由很多种格式构成,比如 png、jpeg、webp 格式等等。我们有时候会发现同一张的图片却有不同的大小,即使在图片的宽、高一致的情况下,也会有所差别。这个就是图片内部储存结构导致的,今天我们通过 Bitmap.Config 下的属性类型让学习一下位图是如何存储的,确切的说它的内部是按怎样的一种规格储存。然后,你就会明白为何有这样的差异了。
我们最常见的创建一张位图的操作如下,但是一般不会去设置带有 Config 的类型。
//创建一个Bitmap时,在它的参数里有一个Config Bitmap.createBitmap(...,Config);
图片格式介绍
首先,我们来看看Bitmap几个参数类型的源码:
这里的源码是设置它的压缩类型,有3种可以选择,也是计算机中图片格式里最常见的。
// 设置压缩格式类型
public static enum CompressFormat {
JPEG,
PNG,
WEBP;
private CompressFormat() {
}
}
PNG格式
全称:便携式网络图形(Portable Network Graphics,PNG),它是一种无损压缩的位图图形格式,支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性。最高支持1600万色,支持全透明/半透明,也包括复杂的图片透明。它的最大特点应该就是无损压缩,这也使得它拥有丰富的颜色显示效果,即使在压缩情况下也能做到不降低图像质量。
JPEG格式
全称:(Joint Photographic Experts Group,JPEG)联合图像专家组的英文缩写,这个我们不用管它。我们在计算机中通常看到的是.JPG的后缀名文件,其实就是它的缩写格式。这是因为在很久以前计算机有这样一个规定,只用3位保存后缀名格式。JPEG是一个压缩标准,又可分为标准JPEG、渐进式JPEG及JPEG2000三种,那么这三种又有很大的区别:
WEBP格式
WebP格式,Google在2010年推出的新一代图片格式,也是Google开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器宽带资源和数据空间。WebP既支持有损压缩也支持无损压缩。相较编码JPEG文件,编码同样质量的WebP文件需要占用更多的计算资源。
谷歌浏览器已经支持webp格式,Opera在版本号Opera11.10后也增加了支持,然而火狐和ie暂时还不支持webp格式。当然,Android系统也已经支持了,IOS还尚未支持。
前面我们介绍了Android的三种位图存储的格式,下面我们来通过源码解读位图的配置属性。当然,英文好的你看这些注释就可以直观的知道它们的一些区别和作用,但是我还是简单的介绍一下以及举例说明一下。
这是我们Bitmap.Config的属性,它是由一个enum(枚举)类型构成的,其中拥有6中枚举可以选择。我们选择其中最常用的4个来着重说明一下。下面是Bitmap.Config的源码:
/**
* Possible bitmap configurations. A bitmap configuration describes
* how pixels are stored. This affects the quality (color depth) as
* well as the ability to display transparent/translucent colors.
*/
public enum Config {
// these native values must match up with the enum in SkBitmap.h
/**
* Each pixel is stored as a single translucency (alpha) channel.
* This is very useful to efficiently store masks for instance.
* No color information is stored.
* With this configuration, each pixel requires 1 byte of memory.
*/
ALPHA_8 (1),
/**
* Each pixel is stored on 2 bytes and only the RGB channels are
* encoded: red is stored with 5 bits of precision (32 possible
* values), green is stored with 6 bits of precision (64 possible
* values) and blue is stored with 5 bits of precision.
*
* This configuration can produce slight visual artifacts depending
* on the configuration of the source. For instance, without
* dithering, the result might show a greenish tint. To get better
* results dithering should be applied.
*
* This configuration may be useful when using opaque bitmaps
* that do not require high color fidelity.
*/
RGB_565 (3),
/**
* Each pixel is stored on 2 bytes. The three RGB color channels
* and the alpha channel (translucency) are stored with a 4 bits
* precision (16 possible values.)
*
* This configuration is mostly useful if the application needs
* to store translucency information but also needs to save
* memory.
*
* It is recommended to use {@link #ARGB_8888} instead of this
* configuration.
*
* Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},
* any bitmap created with this configuration will be created
* using {@link #ARGB_8888} instead.
*
* @deprecated Because of the poor quality of this configuration,
* it is advised to use {@link #ARGB_8888} instead.
*/
@Deprecated
ARGB_4444 (4),
/**
* Each pixel is stored on 4 bytes. Each channel (RGB and alpha
* for translucency) is stored with 8 bits of precision (256
* possible values.)
*
* This configuration is very flexible and offers the best
* quality. It should be used whenever possible.
*/
ARGB_8888 (5),
/**
* Each pixels is stored on 8 bytes. Each channel (RGB and alpha
* for translucency) is stored as a
* {@link android.util.Half half-precision floating point value}.
*
* This configuration is particularly suited for wide-gamut and
* HDR content.
*/
RGBA_F16 (6),
/**
* Special configuration, when bitmap is stored only in graphic memory.
* Bitmaps in this configuration are always immutable.
*
* It is optimal for cases, when the only operation with the bitmap is to draw it on a
* screen.
*/
HARDWARE (7);
final int nativeInt;
private static Config sConfigs[] = {
null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
};
Config(int ni) {
this.nativeInt = ni;
}
static Config nativeToConfig(int ni) {
return sConfigs[ni];
}
}
public enum Config {
ALPHA_8 (1),
RGB_565 (3),
RGBA_F16 (6),
ARGB_8888 (5),
}
以上4种是我们最常用的属性设置,ARGB_4444头上有个被遗弃的注解,所以我们就不具体去解释它的属性了。我相信只要你知道了ARGB_8888,那么ARGB_4444也就是同一个道理。
首先,我们来看一下第一个,也就是ALPHA_8,这种枚举类型是可以充当swith语句中的case条件的,所以枚举用起来也特别方便。优点是在于它是线程安全的单利模式,也支持序列化。缺点就是大量使用会导致性能降低。
ALPHA_8
根据上面的源码注释,我简单的概括它的属性信息。首先,它是每个像素储存一个透明度值,每个像素是占1个字节(8位),也就是后面8的由来。其次,它不储存任何颜色信息,只单纯做透明度的处理。
根据变量名称,我们可以初步的知道它要表达的信息,以下也是如此。
RGB_565
RGB_565,如变量名所述。R代表Red,G代表Green,B代表Blue。那么565就是储存它们的方式,它的每个像素占用2个字节。红色占5位,绿色占6位,蓝色占5位(5位保存的值2的5次方也就是32种的可能性,比如00001,00002...。同理6位则有64种值)。虽然它能提供这么多种颜色值,但是唯独不可以透明。因为它没有提供保存透明度的通道,所以通常它用于一些不透明的图片的设置。
ARGB_8888
我已经说明了两个例子,相信这个你自己也可以看懂的。也如其名,它提供了颜色和透明度的通道,综合了以上两种方式。它每个像素占用4个字节(显然图片的大小也增加),并且它每个都用8位来保存其颜色和透明度值。我们知道8位将有256种可能值,这意味着它的颜色值的可能性越多,图片的质量、显示效果将大大提高。所以,这也是被推荐的一种属性设置。
RGBA_F16
这个与上面变量名又有点区别,从F16就可以看出。它每个像素占用8个字节,但是它是以半浮点数存储的,那前面的F就说的通了。Google的注释里还指明这个属性非常适合用于广色域宽屏和HDR(高动态范围的图片)。所以以此来看,它所占用的内存是最高的,因此显示的效果也非常好。
举个小例子:
比如拿ARGB_8888来说,它所拥有的颜色种类就是红色:2^8,绿色:2^8,蓝色:2^8,也就是大约1600多万种颜色。这么多种颜色组成的一张图片,比如1920*1080像素的一张图,以ARGB_8888的方式来存储颜色值的话。那它的大小就是1920*1080*4(字节) = 8294400(bytes) = 7.91兆(M)大约值。
当然,如果用RGBA_F16的方式储存颜色值,那将在16M左右的一张图,这对手机来说已经是一张非常高清的图片了。