图像格式
首先Bitmap本质而言,是图片一种存储方式。
位图(Bitmap)格式其实并不能说是一种很常见的格式(从我们日常的使用频率上来讲,远不如 .jpg .png .gif 等),因为其数据没有经过压缩,其内部存储的色彩信息(灰度图,RGB 或 ARGB)直接以二进制的形式暴露在外,也十分方便借助计算机软件进行简单或深入的分析.
Bitmap的结构可以分三部分:位图文件头,位图信息数据头,原始位图数据。
-
位图文件头 Bitmap File Header (14 bytes)
-
位图信息数据头 DIB Header (54 bytes)
对于压缩方式,虽然 Bitmap 格式提供简单的压缩功能,但是绝大多数情况下,并没有采用任何压缩手段. -
原始位图数据 Raw Bitmap Data
拿最常见的 24BPP RGB,每一个像素24bit,分红绿蓝三个通道。在二进制文件中,通常情况下,RGB 按照蓝、绿、红的顺序依次表示图片中的像素点,而 RGBA 则按照蓝、绿、红、透明的顺序(从左下开始,横向逐行向上扫描)。
数据按照像素行进行包装,便于读取。但是这并不是全部,因为其中还可能会有补零(zero-padding)。这涉及到计算机的数据结构对齐(data structure alignment)的问题。
主流的 CPU 每次从内存中读取并处理数据块(chunk),且通常为 32 比特(4 字节)。因此,为了提升读取效率,位图每行的数据(字节)都需要是 4 的倍数。不可避免地,有些时候每行的结尾就会出现补零(其实补其他任意数字也是可以的,但常见都是补 0)。
对于每行补零的个数,是有计算方法的。公式如下(知乎的 LaTeX 公式不能很好地支持中文,因此无法进行翻译,抱歉):
即,每行的字节数等于:每像素比特数乘以图片宽度加 31 的和除以 32,并向下取整,最后乘以4。
得到了每行的字节数,进而就能够得到原始位图数据,或者说存储图片的所有像素的色彩信息的数据,的大小了:
即,原始位图数据大小等于:每行的字节数乘以图像高度(也就是总行数)
再加上之前说的文件头的数据大小(通常为 54 字节),就能够得到文件大小了。
在Android程序里Bitmap多大呢?
- 屏幕密度
我们在xml写layout的时候,都会写dp,而很少用px(物理像素点)。当然字体大小除外。这些详细原因在以前的博客写过,网上很多就不细说了。dp与px的关系就是因为屏幕密度(屏幕每英寸对应多少个点)。
density:The logical density of the display. This is a scaling factor for the Density Independent Pixel unit, where one DIP is one pixel on an approximately 160 dpi screen (for example a 240x320, 1.5”x2” screen), providing the baseline of the system’s display. Thus on a 160dpi screen this density value will be 1; on a 120 dpi screen it would be .75; etc.
This value does not exactly follow the real screen size (as given by xdpi and ydpi, but rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi. For example, a 240x320 screen will have a density of 1 even if its width is 1.8”, 1.3”, etc. However, if the screen resolution is increased to 320x480 but the screen size remained 1.5”x2” then the density would be increased (probably to 1.5).
densityDpi:The screen density expressed as dots-per-inch.
density | densityDpi |
---|---|
1 | 160 |
1.5 | 240 |
2 | 320 |
3 | 480 |
3.5 | 560 |
4 | 640 |
SkBitmap.cpp
size_t SkBitmap::ComputeRowBytes(Config c, int width) {
return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
}
SkImageInfo.h
static int SkColorTypeBytesPerPixel(SkColorType ct) {
static const uint8_t gSize[] = {
0, // Unknown
1, // Alpha_8
2, // RGB_565
2, // ARGB_4444
4, // RGBA_8888
4, // BGRA_8888
1, // kIndex_8
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
size_mismatch_with_SkColorType_enum);
SkASSERT((size_t)ct < SK_ARRAY_COUNT(gSize));
return gSize[ct];
}
static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) {
return width * SkColorTypeBytesPerPixel(ct);
}
ARGB_8888(也就是我们最常用的 Bitmap 的格式)的一个像素占用 4byte,那么 rowBytes 实际上就是 4width bytes。
那么结论出来了,一张 ARGB_8888 的 Bitmap 占用内存的计算公式
bitmapInRam = bitmapWidthbitmapHeight *4 bytes
Bitmap 与Drawable的关系
Bitmap是用来显示的实际数据,它在概念上隶属于Drawable,但是与Drawable之间不是继承关系。
可以简单地理解为 Bitmap 储存的是 像素信息,Drawable 储存的是 对 Canvas 的一系列操作。而 BitmapDrawable 储存的是「把 Bitmap 渲染到 Canvas 上」这个操作。
参考资料: