[Image_Codec]常见图片格式的封装及编解码-Android平台(一)BMP
[TOC]
概述
图片格式是计算机存储图片的格式,是图片在计算机中存放的样式。常见的存储的格式有很多,有不同的编码格式和封装方式,实用与各个平台和各种需求。
bmp jpg png tiff gif pcx tga exif fpx svg psd cdr pcd dxf ufo eps ai raw WMF ...
我们这里只介绍里面最常见的几种:
bmp jpg png gif
我们采用4x2的一张图片进行介绍。这里的放大版本,4x2的图片很小,看不见,这个是放大很多倍的样图。另外,介绍时,我们默认都是采用32bit的,也就是默认的ARGB32。
文章图片
图片样例 这些图片格式网络上有很多介绍的,大家也可以参考,下面我们分别来看看,我们的样例代码都是基于Android平台,但是很多都是以native的方式提供。
BMP图片格式 BMP也就是位图Bitmap,Windows标准格式图形文件,是一种与硬件设备无关的非压缩的图像文件格式,也就是采用位映射存储格式,不进行压缩,仅可以选择图像深度。所以,BMP文件所占用的空间很大,不适合网络等传输。BMP文件的图像深度可选lbit、4bit、8bit及24bit。
Bitmap文件结构 下面是Wiki上Bitmap文件的的相关信息:
属性 | 属性值 |
---|---|
Filename extension | .bmp, .dib |
Internet media type | image/bmp, image/x-bmp |
Type code | 'BMP', 'BMPf', 'BMPp' |
Uniform Type Identifier (UTI) | com.microsoft.bmp |
Developed by | Microsoft Corporation |
Type of format | Raster graphics |
Open format? | OSP for WMF |
文章图片
Bitmap文件格式 其中,
Bitmap file header
,DIB header
和Pixel array
三部分是必须的。我们以一个4x2的32bit的Bitmap加以说明:[图片上传失败...(image-f47dcf-1524794608583)]
图片非常小,前面的样图的放大后的版本,放大后的如下:
文章图片
图片样例 我们用二进制将图片打开
vim -b bitmap_4x2_32bit.bmp
用
:%!xxd
转换为16进制00000000: 424d 5600 0000 0000 0000 3600 0000 2800BMV.......6...(.
00000010: 0000 0400 0000 0200 0000 0100 2000 0000............ ...
00000020: 0000 2000 0000 0000 0000 0000 0000 0000.. .............
00000030: 0000 0000 0000 ffff ff7f 00ff 007f 0000................
00000040: ff7f ff00 007f 0000 0000 0000 ff00 ff00................
00000050: 0000 00ff 0000......
这是一张4x2 32位的bmp图片,包括Alpha通道,像素格式为ARGB32。32Bit,那么一个像素点就是32位,4字节;4x2大小,就4x2x4就是32字节。我们具体来看里面包含的信息。
- 第一部分 BMP的头(BMP Header)
00000000: 424d 5600 0000 0000 0000 3600 0000
偏移量Offset | 大小Size(字节) | 十六进制值Hex Value | 值Value | 描述Description |
---|---|---|---|---|
0h | 2 | 424d |
BM | BMP的ID(42,4d) |
2h | 4 | 5600 0000 |
0x56字节(头54+像素32=86字节) | BMP文件的大小 |
6h | 2 | 0000 |
没有用到 | 应用指定的 |
8h | 2 | 0000 |
没有用到 | 应用指定的 |
Ah | 4 | 3600 0000 |
0x36字节(14+40) | Bitmap数据的起始位置 |
- 第二部分 DIB Header
00000000: ---- ---- ---- ---- ---- ---- ---- 2800BMV.......6...(.
00000010: 0000 0400 0000 0200 0000 0100 2000 0000............ ...
00000020: 0000 2000 0000 0000 0000 0000 0000 0000.. .............
00000030: 0000 0000 0000
偏移量Offset | 大小Size(字节) | 十六进制值Hex Value | 值Value | 描述Description |
---|---|---|---|---|
Eh | 4 | 2800 0000 |
0x28字节 | DIB Header的大小 |
12h | 4 | 0400 0000 |
4 pixels | Bitmap的宽,宽高是用是像素表的 |
16h | 4 | 0200 0000 |
2 pixels | Bitmap的高 |
1Ah | 2 | 0100 |
1 plane | 会用到是颜色板数量 |
1Ch | 2 | 2000 |
32 bits | 每个像素点占的bit位 |
1Eh | 4 | 0000 0000 |
0 | BI_RGB, 没有像素被压缩 |
22h | 4 | 2000 0000 |
32字节 | Bitmap raw数据的大小,包括对齐的部分 |
26h | 4 | 0000 0000 |
0 pixels/metre horizontal | 打印信息没有指定 |
2Ah | 4 | 0000 0000 |
0 pixels/metre vertical | 打印信息没有指定 |
2Eh | 4 | 0000 0000 |
0 colors | 调色板数量 |
32h | 4 | 0000 0000 |
0 important colors | 0表示所有color都重要 |
- 第三部分 像素点数据 Pixels
00000030: ---- ---- ---- ffff ff7f 00ff 007f 0000................
00000040: ff7f ff00 007f 0000 0000 0000 ff00 ff00................
00000050: 0000 00ff 0000......
偏移量Offset | 大小Size(字节) | 十六进制值Hex Value | 值Value | 描述Description |
---|---|---|---|---|
36h | 4 | ffff ff7f |
255 255 255 127 | 白色Alpha为7f,ARGB32,采用小端模式 |
3Ah | 4 | 00ff 007f |
0 255 0 127 | 绿色Alpha为7f,Bitmap扫描采用从做到右,下到上 |
3Eh | 4 | 0000 ff7f |
0 0 255 127 | 红色Alpha为7f,从左到右BGRA |
42h | 4 | ff00 007f |
255 0 0 127 | 蓝色Alpha为7f |
46h | 4 | 0000 0000 |
0 0 0 0 | 黑色Alpha为0 |
4Ah | 4 | 0000 ff00 |
0 0 255 0 | 红色Alpha为0 |
4Eh | 4 | ff00 0000 |
255 0 0 0 | 蓝色Alpha为0 |
52h | 4 | 00ff 0000 |
0 255 0 0 | 绿色Alpha为0 |
试想一下,如果是一张2x2 24bit的BMP图片会是什么样的呢?参照前面的描述。需要变的有一下几个地方:
每像素的位数:24, 0x18;3字节
尺寸:2x2 0x02
Pixel的大小:2x2x3 12字节,0x0c
文件的大小:54 + 12 66字,0x42
Pixels的值,就黑白,白黑的矩阵吧:
ffff ff 0000 00
000 00 ffff ff
【[Image_Codec]常见图片格式的封装及编解码-Android平台(一)BMP】修改文件吧~
00000000: 424d 4200 0000 0000 0000 3600 0000 2800BMV.......6...(.
00000010: 0000 0200 0000 0200 0000 0100 1800 0000............ ...
00000020: 0000 0c00 0000 0000 0000 0000 0000 0000.. .............
00000030: 0000 0000 0000 ffff ff00 0000 0000 00ff................
00000040: ffff................
用
:%!xxd -r
转成二进制,:wq
保存退出。打开我们编辑后的图片,看看是什么样的?打不开吧?Why?
这是因为,宽需要做一个8字节的对齐,我们来看对齐后,应该是什么样的。
原来是
2x3
,6字节,对齐后应该是8字节。pixel的大小是不变为16字节了,0x10;文件大小为0x46,像素矩阵为:ffff ff00 0000 0000
000 00ff ffff 0000
后面的
0000
的补齐的,不是bitmap的数据,修改文件吧~再打开bitmap文件试试,可以打开吧!只是2x2的图片实在太小了,很难看见,下面是放大后的效果:
文章图片
2x2 RGB24的BMP图片 Bitmap文件 解析 Bitmap中的像素点数据Pixels是没有经过压缩的,我们读出来后就是图片的数据值,所以没有解压一说。但是需要我们从一个Bitmap文件中,将需要的信息获取出来。我们勉强也把这个过程称为解压吧!
样例代码请参考github Codec-BitmapCodec
我们的样例的代码是用Java实现的,很简单,只包括我们前面介绍的Bitmap文件必现的信息。
public class BitmapFile {
String fileName;
byte type[];
byte data[];
byte pixels[];
byte outPixels[];
BitmapFileHeader fileHeader;
BitmapDibHeader dibHeader;
定义BitmapFile类来描述一个Bitmap文件,type是类型,Bitmap的type为
42 4d
;data Bitmap读出来的所有数据。文件头,用类BitmapFileHeader进行描述;Dib头用BitmapDibHeader进行描述。pixels 从Bitmap文件中读出来的像素点,outPixels是准备写到Bitmap文件中的像素点。BitmapFileHeader定义如下:
public class BitmapFileHeader {
intfileSize;
shortreserved1;
shortreserved2;
intpixelsOffset;
}
BitmapDibHeader定义如下:
public class BitmapDibHeader {
intdibSize;
intwidth;
intheight;
shortplanes;
shortbitPerPixel;
intcompressed;
intpixelSize;
intxPixelsPerMeter;
intyPixelsPerMeter;
intclrUsed;
intclrImportant;
intbytePerPixel;
}
bytePerPixel是加的,每个像素占多少个字节。
解析Bitmap文件的时候,直接读取文件中的数据,将文件中的数据按照封装读出来。
public boolean readBitmap() throws Exception {
DataReader reader = new DataReader();
reader.readFromFile(fileName);
type = reader.readType();
fileHeader.fileSize = reader.readInt();
fileHeader.reserved1 = reader.readShort();
fileHeader.reserved2 = reader.readShort();
fileHeader.pixelsOffset = reader.readInt();
dibHeader.dibSize = reader.readInt();
dibHeader.width = reader.readInt();
dibHeader.height = reader.readInt();
dibHeader.planes = reader.readShort();
dibHeader.bitPerPixel = reader.readShort();
dibHeader.compressed = reader.readInt();
dibHeader.pixelSize = reader.readInt();
dibHeader.xPixelsPerMeter = reader.readInt();
dibHeader.yPixelsPerMeter = reader.readInt();
dibHeader.clrUsed = reader.readInt();
dibHeader.clrImportant = reader.readInt();
data = https://www.it610.com/article/reader.readBytesFrom(0);
pixels = reader.readBytesFrom(fileHeader.pixelsOffset);
return true;
}
将我们前面自己编辑的bitmap_2x2_24bit.bmp文件读出来看看:
Bitmap Info
type:424d
fileSize:0x46
reserved1:0x0
reserved2:0x0
pixelsOffset:0x36
dibSize:0x28
width:0x2
height:0x2
planes:0x1
bitPerPixel:0x18
compressed:0x0
pixelSize:0x10
xPixelsPerMeter:0x0
yPixelsPerMeter:0x0
clrUsed:0x0
clrImportant:0x0
pixels:ffffff0000000000000000ffffff0000
我们解析出来的信息是对的。
Bitmap文件封装 我们将前面的bitmap_2x2_24bit.bmp扩展为32bit,也就是加上Alpha通道;放大100倍(w,h都放大10倍)后,再保存为Bitmap文件。
private void buildBitmapAndSave() {
BitmapFile bmpFile= new BitmapFile();
BitmapDibHeader dibHeader = new BitmapDibHeader();
dibHeader.dibSize = 0x28;
dibHeader.width = 20;
dibHeader.height = 20;
dibHeader.planes = 1;
dibHeader.bitPerPixel = 32;
dibHeader.bytePerPixel = 4;
dibHeader.pixelSize = dibHeader.width * dibHeader.height * dibHeader.bytePerPixel;
bmpFile.setDibHeader(dibHeader);
BitmapFileHeader fileHeader = new BitmapFileHeader();
fileHeader.fileSize = 0x36 + dibHeader.pixelSize;
fileHeader.pixelsOffset = 0x36;
fileHeader.reserved1 = Utils.char2short(new char[]{'T', 'S'});
fileHeader.reserved2 = Utils.char2short(new char[]{'C', 'J'});
bmpFile.setFileHeader(fileHeader);
// raw data 20x20x4,already 8bit align.
byte[] pixels = new byte[]{
... ...
};
bmpFile.setOutPixels(pixels);
try {
String fileName = "/sdcard/bitmap_20_20_32bit.bmp";
int size = bmpFile.saveBitmap(fileName);
MyLog.d("buildBitmapAndSave " + size + " bytes to " + fileName);
} catch (Exception e) {
MyLog.e("buildBitmapAndSave fail " + e.toString());
}
}
一个像素点4个byte,pixels总共20x20x4,1600个byte。量比较大,我们就不列出来了。红色我们用的
0000 ff00
,ff是Alpha值,绿色就是00ff 0000
,不透明。直接给个图吧 bitmap_20x20_32bit.bmp文章图片
20x20 ARGB32的Bitmap图片(bmp上传不了,转为了jpg) Bitmap接介绍到这里,实例代码可以高github里面去下载哦~
Codec-BitmapCodec
推荐阅读
- 第6.2章(设置属性)
- 唱歌教学(导致嗓音损坏的几个常见的错误唱歌方法!)
- Hive常见问题汇总
- JS常见数组操作补充
- 孕期妈妈们常见的几个误区
- jvm常见分析工具
- 关于Core|关于Core Image
- unity探究UGUI的Image中sprite和overrideSprite的区别
- ImageLoaders 加载图像
- redis|redis 常见问题一