在上一篇博文中,讲解了如何利用java组件实现了一个简易的画图板,那么本篇博文将会为大家介绍如何打开已有的BMP文件,和以BMP格式保存自己绘制的图片。
二、BMP是什么
(一)BMP介绍
BMP 是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此, BMP 文件所占用的空间很大。 BMP 文件的图像深度可选 lbit 、 4bit 、 8bit 及 24bit 。 BMP 文件存储数据时,图像的扫描方式是按从左到右、 从下到上的顺序。由于 BMP 文件格式是Windows 环境中交换与图有关的数据的一种标准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。
(二)BMP文件结构
典型的 BMP 图像文件由四部分组成:
1 . 构位图文件 头数据结构 ,它包含 BMP 图像文件的类型、显示内容等信息;
2 .构位图信息数据结构 ,它包含有 BMP 图像的宽、高、压缩方法,以及定义颜色等信息;
3. 板调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图 (24 位的 BMP )就不需要调色板。本文就是讲解如何保存和读取一个24位的BMP文件;
4. 据位图数据 ,这部分的内容根据 BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB ,而其他的小于 24 位的使用调色板中颜色索引值。
(三)BMP各部分数据含义
1.BMP头文件(14字节)
int bfType; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 ) int bfSize; // 位图文件的大小,以字节为单位 (2-5 字节 ) int usignedshort bfReserved1; // 位图文件保留字,必须为 0(6-7 字节 ) int usignedshort bfReserved2; // 位图文件保留字,必须为 0(8-9 字节 ) int bfOffBits; // 位图数据的起始位置,以相对于位图 (10-13 字节 ) int bfOffBits;// 文件头的偏移量表示,以字节为单位
2.位图信息头(40 节 字节)
int Size; // 本结构所占用字节数 (14-17 字节 ) int image_width; // 位图的宽度,以像素为单位 (18-21 字节 ) int image_heigh; // 位图的高度,以像素为单位 (22-25 字节 ) int Planes; // 目标设备的级别,必须为 1(26-27 字节 ) int biBitCount;// 每个像素所需的位数,必须是 1(双色),(28-29 字节) 4(16 色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一 int biCompression; // 位图压缩类型,必须是 0( 不压缩 ),(30-33 字节 ) 1(BI_RLE8 压缩类型 ) 或// 2(BI_RLE4 压缩类型 ) 之一 int SizeImage; // 位图的大小,以字节为单位 (34-37 字节 ) int biXPelsPerMeter; // 位图水平分辨率,每米像素数 (38-41 字节 ) int biYPelsPerMeter; // 位图垂直分辨率,每米像素数 (42-45 字节 ) int biClrUsed;// 位图实际使用的颜色表中的颜色数 (46-49 字节 ) int biClrImportant;// 位图显示过程中重要的颜色数 (50-53 字节 )
3.颜色表
class RGBQUAD { byte rgbBlue;// 蓝色的亮度 ( 值范围为 0-255) byte rgbGreen; // 绿色的亮度 ( 值范围为 0-255) byte rgbRed; // 红色的亮度 ( 值范围为 0-255) byte rgbReserved;// 保留,必须为 0 }
4.位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右, 扫描行之间是从下到上。位图的一个像素值所占的字节数:
当 biBitCount=1 时, 8 个像素占 1 个字节 ;
当 biBitCount=4 时, 2 个像素占 1 个字节 ;
当 biBitCount=8 时, 1 个像素占 1 个字节 ;
当 biBitCount=24 时 ,1 个像素占 3 个字节 ;
Windows 规定一个扫描行所占的字节数必须是 4 的倍数 ( 即以 long 为单位 ), 不足的以 0填充,具体数据举例:如某 BMP 文件开头:
424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 0000 9000 0000 0100*1000 0300 0000
0090 0000 A00F 0000 A00F 0000 0000 0000 0000 0000*00F8 E007 1F00 0000*02F1 84F1
04F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 .... ....
BMP 文件可分为四个部分:位图文件头、位图信息头、彩色板、图像数据阵列,在上图中
已用 * 分隔。
了解了BMP的基础知识后,我们可以进一步完善我们的画图板了,注意,我们这里只针对24位的BMP图片,不做256色和16位BMP的讲解。
三、实现BMP文件的读取
了解了BMP的基本知识后,我们可以知道,读取一个BMP文件实际上只要获取到文件头的宽度,高度和它的位图数据,而其他部分都是可以跳过的。可以利用之前已经做好的画图板来实现。
1.创建一个BMPReader.java类,用来封装读取BMP图片的一些列方法。
/** * BMP文件的读取类 * * @author Bill56 * */ public class BMPReader { /** * 读取BMP文件的方法 * * @param path * 文件路径 * @param panel * 显示的画板 * @throws IOException * 流异常 */ public void readBMP(String path, JPanel panel) throws IOException { FileInputStream fis = new FileInputStream(path); fis.skip(18); // 跳过不需要的,读取宽度和高度 int width = changeInt(fis); int height = changeInt(fis); fis.skip(28); // 跳过,直接读取位图数据。 DrawListener.array = new int[height][width]; int t = 0; if (width * 3 % 4 > 0) { t = 4 - width * 3 % 4; } for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { // 调用自定义方法,得到图片的像素点并保存到int数组中 int c = readColor(fis); DrawListener.array[i][j] = c; } fis.skip(t); } fis.close(); // 刷新,重绘面板,打开系统保存的图片。 panel.setPreferredSize(new Dimension(width, height)); } /** * 由于读取的是字节,把读取到的4个byte转化成1个int * * @param ips * 文件输入流对象 * @return 转换后的int值 * @throws IOException * 流异常 */ public int changeInt(FileInputStream ips) throws IOException { int t1 = ips.read() & 0xff; int t2 = ips.read() & 0xff; int t3 = ips.read() & 0xff; int t4 = ips.read() & 0xff; int num = (t4 << 24) + (t3 << 16) + (t2 << 8) + t1; return num; } /** * 24位的图片是1个像素3个字节,所以要将其颜色值合成一个具体的颜色值 * * @param ips * 文件输入流对象 * @return 颜色值的合成值 * @throws IOException * 流异常 */ public int readColor(FileInputStream ips) throws IOException { int b = ips.read() & 0xff; int g = ips.read() & 0xff; int r = ips.read() & 0xff; int c = (r << 16) + (g << 8) + b; return c; } }
2.在画图板代码的MenuListener中重写的actionPerformed方法中对应“打开”菜单的功能部分内写入打开一个BMP文件的代码。
else if (command.equals("打开")) { int t = chooser.showOpenDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { BMPReader reader = new BMPReader(); reader.readBMP(path, panel); // 刷新界面 panel.repaint(); } catch (Exception ef) { JOptionPane.showMessageDialog(null, "文件打开失败!!"); ef.printStackTrace(); } } }
至此,我们可以用计算机自带的画图工具画一个图片,然后保存为24位的BMP(一定要是24位的),再用我们的自己画图板打开,查看效果。
四、实现BMP文件的保存
保存和读取比较起来,保存稍复杂,因为保存的时候需要写入很多必要的信息,而读取的时候可以跳过这些信息。具体如下:
1.创建一个BMPWriter.java类,用来封装保存BMP图片的一些列方法。
/** * BMP文件的保存写入类 * * @author Bill56 * */ public class BMPWriter { /** * 保存BMP图片头部信息的方法 * * @param ops * 输出流 * @throws Exception * 异常对象 */ public void savebmpTop(OutputStream ops) throws Exception { ops.write('B'); ops.write('M'); int height = DrawListener.array.length; int width = DrawListener.array[0].length; int size = 14 + 40 + height * width * 3 + (4 - width * 3 % 4) * height; // 位图文件的大小 size = 14 + 40 + height * width * 3 + (4 - width * 3 % 4) * 255; writeInt(ops, size); writeShort(ops, (short) 0); writeShort(ops, (short) 0); writeInt(ops, 54); } /** * 保存BMP图片位图信息头部分的方法 * * @param ops * 输出流 * @throws Exception * 异常 */ public void savebmpInfo(OutputStream ops) throws Exception { int height = DrawListener.array.length; int width = DrawListener.array[0].length; writeInt(ops, 40); writeInt(ops, width); writeInt(ops, height); writeShort(ops, (short) 1); writeShort(ops, (short) 24); writeInt(ops, 0); writeInt(ops, height * width * 3 + (4 - width * 3 % 4) * height); writeInt(ops, 0); writeInt(ops, 0); writeInt(ops, 0); writeInt(ops, 0); } /** * 保存BMP图片位图数据部分的方法 * * @param ops * 输出流 * @throws Exception * 异常 */ public void savebmpDate(OutputStream ops) throws Exception { int height = DrawListener.array.length; int width = DrawListener.array[0].length; int m = 0; if (width * 3 % 4 > 0) { m = 4 - width * 3 % 4; } for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { int t = DrawListener.array[i][j]; writeColor(ops, t); } for (int k = 0; k < m; k++) { ops.write(0); } } } /** * 由于写入的是字节,所以要将整型进行转换 * * @param ops * 输出流 * @param t * 整型值 * @throws Exception * 异常 */ public void writeInt(OutputStream ops, int t) throws Exception { int a = (t >> 24) & 0xff; int b = (t >> 16) & 0xff; int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); ops.write(b); ops.write(a); } /** * 由于写入的是字节,所以要将颜色值进行转换 * * @param ops * 输出流 * @param t * 整型的颜色值 * @throws Exception * 异常 */ public void writeColor(OutputStream ops, int t) throws Exception { int b = (t >> 16) & 0xff; int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); ops.write(b); } /** * 由于写入的是字节,所以要将短整型进行转换 * * @param ops * 输出流 * @param t * 短整形值 * @throws Exception * 异常 */ public void writeShort(OutputStream ops, short t) throws Exception { int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); } }
2.在画图板代码的MenuListener中重写的actionPerformed方法中对应“保存”菜单的功能部分内写入保存成BMP文件的代码。
if (command.equals("保存")) { int t = chooser.showSaveDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { FileOutputStream fos = new FileOutputStream(path + ".bmp"); BMPWriter writer = new BMPWriter(); writer.savebmpTop(fos); writer.savebmpInfo(fos); writer.savebmpDate(fos); fos.flush(); fos.close(); } catch (Exception ef) { JOptionPane.showMessageDialog(null, "文件保存失败!!"); ef.printStackTrace(); } } }
至此,我们已经完成了画图板的全部工作了。大家可以自己画一幅图片,然后保存后,用计算机自带的图片浏览器打开,查看完成情况。
谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~
相关推荐
在本篇博文中,我们将探讨如何使用Java编程语言来实现一个简单的画图板应用程序,并学习如何保存和读取BMP(Bitmap)格式的图片。BMP是一种无损图像格式,通常用于存储像素数据,便于程序处理。以下是实现这一功能所...
BMP格式图片包含图像的元数据,如宽度(Width,W)、高度(Height,H)、颜色深度等,以及实际像素数据。RAW格式则更接近于相机传感器的数据,通常包含每个像素的红、绿、蓝分量,没有预处理或色彩空间转换。 要...
Java编写的绘图板小程序是一款基于Java编程语言开发的简易画板应用,它提供了一系列丰富的绘图功能,如撤销、恢复、调色板选色、保存图像为BMP格式以及打开BMP图像等。这款程序的亮点在于采用了双缓冲技术,有效解决...
- 提供基本的文件操作功能,如保存和打开jpg或bmp格式的图像文件。 #### 二、技术栈与开发环境 - **主要技术**:Java语言,Swing框架 - **开发工具**:推荐使用Eclipse或IntelliJ IDEA - **运行环境**:JDK 8及...
5. **文件保存与读取**: 考虑到绘图应用通常需要保存和加载作品,"Java小画板"可能实现了将图像数据序列化为文件(如.png或.bmp格式),并提供相应的读取功能。这涉及到了文件I/O操作,如`FileOutputStream`和`...
5. **文件操作**:读取和保存用户创建的图像文件,通常会支持常见的图像格式,如BMP、JPEG、PNG等。 6. **编程语言和框架**:根据实际实现,可能使用了如Python的tkinter、Pygame,C#的WPF,JavaScript的EaselJS,...
在Java中,可以使用`javax.imageio.ImageIO`类读取和显示这种类型的图片。 接着,有多个`.class`文件,如`PainterPanel.class`和`PainterDemo.class`等,这些都是Java编译后的字节码文件,表明项目包含至少两个类:...
5. 读取/保存文件:可能有用于存储用户画作的`.bmp`, `.png` 或者其他格式的图像文件。 在这个项目中,我们可以学到以下几个方面的知识点: 1. 图形用户界面(GUI)编程:如何使用库(如Tkinter, PyQt, wxPython等...
5. 保存/加载功能:用户可以将作品保存为图片文件,如BMP或PNG格式,也可以加载已保存的作品继续编辑。 代码实现上,我们可以按照以下步骤进行: 1. 创建主窗口类,继承自JFrame。在这个类中,我们将添加所有必要...
C#提供了丰富的文件操作API,可以将图像数据序列化为文件,如BMP、PNG或JPEG格式。在加载时,程序读取文件内容并重新绘制到Canvas上。 **异常处理:** 在开发过程中,良好的异常处理是必不可少的。在涂鸦游戏中,...