[Image_Codec]常见图片格式的封装及编解码-Android平台(一)BMP

概述

图片格式是计算机存储图片的格式,是图片在计算机中存放的样式。常见的存储的格式有很多,有不同的编码格式和封装方式,实用与各个平台和各种需求。

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。

![图片样例](https://img-blog.csdn.net/20180326204027628?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

这些图片格式网络上有很多介绍的,大家也可以参考,下面我们分别来看看,我们的样例代码都是基于Android平台,但是很多都是以native的方式提供。

BMP图片格式

BMP也就是位图Bitmap,Windows标准格式图形文件,是一种与硬件设备无关的非压缩的图像文件格式,也就是采用位映射存储格式,不进行压缩,仅可以选择图像深度。所以,BMP文件所占用的空间很大,不适合网络等传输。BMP文件的图像深度可选lbit、4bit、8bit及24bit。

Bitmap文件结构

下面是Wiki上Bitmap文件的的相关信息:

属性属性值
Filename extension.bmp, .dib
Internet media typeimage/bmp, image/x-bmp
Type code‘BMP’, ‘BMPf’, ‘BMPp’
Uniform Type Identifier (UTI)com.microsoft.bmp
Developed byMicrosoft Corporation
Type of formatRaster graphics
Open format?OSP for WMF

BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。典型的BMP图像文件由三部分组成:位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息;位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;像素点阵,也就是图片的具体数据。Bitap的文件结构可以参考Wiki百科的BMPfileFormat,文件格式描述如下图(此图来自wikipedia):

[外链图片转存失败(img-6PjnSsXQ-1564126857429)(https://upload.wikimedia.org/wikipedia/commons/c/c4/BMPfileFormat.png)]
其中,`Bitmap file header`,`DIB header`和`Pixel array`三部分是必须的。我们以一个4x2的32bit的Bitmap加以说明:
![4x2的ARGB32的BMP图片](https://img-blog.csdn.net/20180326205413874?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
图片非常小,前面的样图的放大后的版本,放大后的如下:
![图片样例](https://img-blog.csdn.net/20180326204027628?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

我们用二进制将图片打开

vim -b bitmap_4x2_32bit.bmp

:%!xxd转换为16进制

00000000: 424d 5600 0000 0000 0000 3600 0000 2800  BMV.......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
0h2424dBMBMP的ID(42,4d)
2h45600 00000x56字节(头54+像素32=86字节)BMP文件的大小
6h20000没有用到应用指定的
8h20000没有用到应用指定的
Ah43600 00000x36字节(14+40)Bitmap数据的起始位置

BMP的头大小为14字节。而下一部分要说道的DIB Header为40字节,所以像素的位置为0x36字节(14+40)。

  • 第二部分 DIB Header
00000000: ---- ---- ---- ---- ---- ---- ---- 2800  BMV.......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
Eh42800 00000x28字节DIB Header的大小
12h40400 00004 pixelsBitmap的宽,宽高是用是像素表的
16h40200 00002 pixelsBitmap的高
1Ah201001 plane会用到是颜色板数量
1Ch2200032 bits每个像素点占的bit位
1Eh40000 00000BI_RGB, 没有像素被压缩
22h42000 000032字节Bitmap raw数据的大小,包括对齐的部分
26h40000 00000 pixels/metre horizontal打印信息没有指定
2Ah40000 00000 pixels/metre vertical打印信息没有指定
2Eh40000 00000 colors调色板数量
32h40000 00000 important colors0表示所有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
36h4ffff ff7f255 255 255 127白色Alpha为7f,ARGB32,采用小端模式
3Ah400ff 007f0 255 0 127绿色Alpha为7f,Bitmap扫描采用从做到右,下到上
3Eh40000 ff7f0 0 255 127红色Alpha为7f,从左到右BGRA
42h4ff00 007f255 0 0 127蓝色Alpha为7f
46h40000 00000 0 0 0黑色Alpha为0
4Ah40000 ff000 0 255 0红色Alpha为0
4Eh4ff00 0000255 0 0 0蓝色Alpha为0
52h400ff 00000 255 0 0绿色Alpha为0

以上就是BMP的文件封装的简单介绍。

试想一下,如果是一张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

修改文件吧~

00000000: 424d 4200 0000 0000 0000 3600 0000 2800  BMV.......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图片](https://img-blog.csdn.net/20180326210352688?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

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 {
    int     fileSize;
    short   reserved1;
    short   reserved2;
    int     pixelsOffset;
}

BitmapDibHeader定义如下:

public class BitmapDibHeader {
    int     dibSize;
    int     width;
    int     height;
    short   planes;
    short   bitPerPixel;
    int     compressed;
    int     pixelSize;
    int     xPixelsPerMeter;
    int     yPixelsPerMeter;
    int     clrUsed;
    int     clrImportant;
    int     bytePerPixel;
}

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 = 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 ffff,ff是Alpha值,绿色就是00ff 00ff,不透明。直接给个图吧 bitmap_20x20_32bit.bmp

![20x20 ARGB32的Bitmap图片](https://img-blog.csdn.net/20180403172159978?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

Bitmap接介绍到这里,实例代码可以高github里面去下载哦~

Codec-BitmapCodec

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夕月风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值