通过分析bmp图片的格式,可以完成BMP图片的打开和保存
?
一、bmp格式:
典型的BMP 图像文件由四部分组成:
1 .位图文件头数据结构,它包含BMP 图像文件的类型、显示内容等信息;
2 .位图信息数据结构,它包含有BMP 图像的宽、高、压缩方法,以及定义颜色等信息;
????? 3.调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24 位的 BMP )就不需要调色板;
4.位图数据,这部分的内容根据BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB ,而其他的小于 24 位的使用调色板中颜色索引值。
?
① BMP文件头 (14 字节 )
?
BMP文件头数据结构含有 BMP 文件的类型、文件大小和位图起始位置等信息。其结构定义如下:
?
intbfType; // 位图文件的类型,必须为 ‘ B ‘‘ M ‘两个字母 (0-1字节 ) intbfSize; // 位图文件的大小,以字节为单位 (2-5 字节 ) intbfReserved1; // 位图文件保留字,必须为 0(6-7 字节 ) intbfReserved2; // 位图文件保留字,必须为 0(8-9 字节 ) intbfOffBits; // 位图数据的起始位置,以相对于位图 (10-13 字节 ) |
?
?
?
② 位图信息头(40 字节 )
?
BMP 位图信息头数据用于说明位图的尺寸等信息。
?
intSize; // 本结构所占用字节数 (14-17 字节 ) intimage_width; // 位图的宽度,以像素为单位 (18-21 字节 ) intimage_heigh; // 位图的高度,以像素为单位 (22-25 字节 ) intPlanes; // 目标设备的级别,必须为 1(26-27 字节 ) intbiBitCount;// 每个像素所需的位数,必须是或1,4,8 24(// 真彩色 ) 之一 (28-29 字节) intbiCompression; // 位图压缩类型,必须是 0( 不压缩 ), 1(BI_RLE8 压缩类型 ) 或// 2(BI_RLE4 压缩类型 ) 之一 (30-33 字节 ) intSizeImage; // 位图数据的大小,以字节为单位 (34-37 字节 ) intbiXPelsPerMeter; // 位图水平分辨率,每米像素数 (38-41 字节 ) intbiYPelsPerMeter; // 位图垂直分辨率,每米像素数 (42-45 字节 ) intbiClrUsed;// 位图实际使用的颜色表中的颜色数 (46-49 字节 ) intbiClrImportant;// 位图显示过程中重要的颜色数 (50-53 字节 ) |
?
?
?
?
③ 颜色表
?
颜色表中的个数有 biBitCount 来确定。当biBitCount=1,4,8 时,分别有 2,16,256 个颜色 ; 当biBitCount=24 时,没有颜色表项。
?
在windows中每个颜色是 b g r a 四个字节保存,a代表透明度,如果是1位位图,那么颜色表一共站八个字节,如果是4位位图,颜色表站84个字节,如果是8位位图,需要表示256中颜色,每种颜色站四个字节,所以颜色表一共站1024个字节。
?
256色的时候,windows中,位图数据的大小就是0-255,代表着调色板中的数据,每次读八个为就是一个像素信息,其他位图类似。
?
④ 位图数据
?
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右, 扫描行之间是从下到上。位图的一个像素值所占的字节数:
?
当biBitCount=1 时, 8 个像素占 1 个字节 ;
?
当biBitCount=4 时, 2 个像素占 1 个字节 ;
?
当biBitCount=8 时, 1 个像素占 1 个字节 ;
?
当biBitCount=24 时, 1 个像素占 3 个字节 ;
?
Windows规定一个扫描行所占的字节数必须是4 的倍数 ( 即以 long 为单位 ), 不足的以 0 填充,具体数据举例:
?
也就是说,写入图片一行的像素信息的时候,每一行的字节数都必须是4的倍数,不足的在后面补0,然后才又开始写入下一行的像素信息。
?
位图数据,其实就是在描述图片上每一个点的颜色,在windos中,是先写入该图片的最有一排像素点,从下往上,从左往右,没写完一排,如果写入的字节数不是4的倍数,就会补0,比如写入了3个字节,就不要再补一个字节的0,然后开始写下一排的数据。
?
对于单色位图,只需要一个位就可以表示其所有的颜色可能,黑白,对于16色位图,也就是有16中颜色的可能,那么需要四个位就可以表示16中可能,对于256色位图,那么需要8个位才能表示256中情况,对于真彩色,也就是表示所有的颜色,那么需要三个字节才可以表示所有情况(透明度不算),对于单色,16色,256色位图来说,位图数据读到的是调色板上的索引,通过这个索引在调试版中找到对应的颜色,然后显示在屏幕上。所以对于单色位图,他的调色板只有黑白两种颜色,16色位图,他的调色板有16种颜色,256色的调色板有256中颜色,如果是24位位图,他的位图信息,就是保存的真实颜色,每读三个字节就表示了一个像素点的颜色,不需要再进行调色板查找对应的颜色。
?
?
?
前言:windows中存入基本数据,是先存入了低位,再存入高位。
?
******************************************************************************************
?
首先完成打开一张bmp24位位图图片:
?
1、利用系统画图板画一张图片,保存的时候选择24位位图,由于24为位图没有调试板,位图数据区保存的是每个像素点的颜色,不需要我们进行调试板的转换,所有有利于初学者对于bmp格式的理解,完成了24位的再来看其他位图的。
?
2、利用swing写一个图形界面,中间留一个panel区域用于显示图片。将图片的每一个颜色保存到二维数组中,然后画在面板上,就打开了一张图片。
?
3、读取到图片的主要信息:
?
File file = new File("d:\\8bit.bmp"); FileInputStream fis = new FileInputStream(file); // 首先得到宽度和高度 fis.skip(18); int width = myReadInt(fis); int height = myReadInt(fis); //由于24位位图没有调试板数据,所以现在已经已经读取了26个字节,再跳过28个字节就到了位图 //数据区 fis.skip(28); //再开始读取就是位图数据了,现在是24位位图,每读取三个字节就代表一个像素点的颜色,注意 //windows在保存的时候是先保存的最后一排的数据,从下往上从左往右保存的,所以我们取出数 //据只有需要保存的数组正确的位置。每次读取三个字节我们就转成颜色保存起来 // 现在读取位图数据 // 判断是否补零 Color[][] colors = new Color[height][width]; int sk = 0; if (width % 4 > 0) { sk = 4 - width % 4; } for (int h = image_heigh - 1; h >= 0; h--) { for (int w = 0; w < image_width; w++) { // dis.skipBytes(bfOffBits-60); // 读入三原色 int blue = dis.read(); int green = dis.read(); int red = dis.read(); Color color = new Color(red,green,blue); colors[h][w]=color; } // 跳过补0项,每次读取第一行就判断需要跳过的字节 dis.skipBytes(sk); }
?
保存下来所有像素的颜色,就可以画在面板上了,面板的大小应该是图片的大小。
?
如果理解了文件格式含义,那么就可以尝试完成256色位图的打开和保存。
?
保存的时候,我们需要严格按照格式规定,写入正确的字节,如果是256色,需要进行颜色的转换:
?
下面是256色位图的打开:
?
说明:我先根据系统画图板的图片,保存一张256色图片,读取出了调调色板的数据,放在一个文件中
?
便于我在进行打开256色图片的时候,把位图数据读到的信息转成调色板对应的颜色,所以要完成下面的操作,请先自己
?
读取256色的调色板数据。把由于系统画图板,位图信息是0,代表的是第一个颜色,如果是255代表最后一个颜色,所以
?
我们可以把调色板数据放在一维数组中,利用下标来进行查找。
?
?
// 打开256色,也就是8位表示一个像素点的颜色 File file = new File("d:\\8bit.bmp"); FileInputStream fis = new FileInputStream(file); // 首先得到宽度和高度 fis.skip(18); int width = myReadInt(fis); int height = myReadInt(fis); // 现在流的位置,26 // 再跳过12个字节,到达需要测试的数据区 fis.skip(12); int biXPelsPerMeter = myReadInt(fis); // 位图水平分辨率,每米像素数 (38-41 字节 // ) int biYPelsPerMeter = myReadInt(fis); // 位图垂直分辨率,每米像素数 (42-45 字节 // ) int biClrUsed = myReadInt(fis);// 位图实际使用的颜色表中的颜色数 (46-49 字节 ) int biClrImportant = myReadInt(fis);// 位图显示过程中重要的颜色数 (50-53 字节 ) System.out.println("" + biXPelsPerMeter + " " + biYPelsPerMeter + " " + biClrUsed + " " + biClrImportant); // 由于是256色,所以颜色表是[256][4]大小的数组,每个分量占1个字节 // 定义一个256的数组,保存下颜色表的数据 Color[] colors256 = new Color[256]; for (int i = 0; i < 256; i++) { colors256[i] = myReadColor(fis); System.out.println("颜色表" + colors256[i]); } // 现在读取位图数据 // 判断是否补零 int sk = 0; if (width % 4 > 0) { sk = 4 - width % 4; } DrawUI.array = new int[height][width]; System.out.println("宽度" + width + "高度" + height); for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { int index = fis.read(); DrawUI.array[i][j] = colors256[index].getRGB(); System.out.println("保存的值"+DrawUI.array[i][j]); } fis.skip(sk); } // 设置画布大小 panel.setPreferredSize(new Dimension(width, height)); // 重新绘制panel,如果是repaint,只会重新绘制原来panel中的内容,不会将panel更新大小 SwingUtilities.updateComponentTreeUI(panel);
?
?
下面的代码,保存256色位图的bmp图片,注意需要自己写入调色板,调色板的信息是按照b g r a的顺序写入,调色板字节
?
先要准备好。
原文:http://376798041.iteye.com/blog/2167106