本人参考了一些人的关于BMP的代码,最后终于搞懂了,针对网上的很多代码对于新手来说有的看其实还是比较费力的,我就给我的代码尽量做了详细的解释和注解和提出两个问题,希望这能帮助你理解以下代码能。
新手为什么对那些代码看起来比较费力,我想应该是一下几个知识点不清楚。这是我作为一个新手的体会,如果你把下面这两个知识点搞懂了,我想BMP的解析对你来说应该是可以轻轻松松的搞下来。
1.BMP在Windows里面的储存格式是什么样子的?
BMP是Windows操作系统中的标准图像文件格式,Windows规定一个扫描行所占的字节数必须是4的倍数,不足的以0补充。关键就是要搞清楚Windows里面数据是怎么存的?Windows储存数据是从地位到高位来储存的,我们一般都是从高位到地位进行保存。所以就需要我们进行位运算将数据来转化成Windows储存数据的方式。
2.对于位运算你懂了多少?
我们上面也说了要将数据转化之后来储存,所以要搞清楚位运算。我就拿代码中的位运算来说一下位运算。
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);
要理解我下面的说的,那你要懂二进制的位运算,在这里至少你要知道&(与)的位运算(关于这个知识我就不说,这需要你自己去百度,不是特别难,如果你不知道,也不去百度继续看下去,那懂不懂就看你的造化了)
上面的t是一个int类型的,0xff是16进制它的二进制是
1111 1111 t>>24表示t的二进制左移24位与1111 1111做位运算,得到t的二进制的前8位,同理t>>16 左移16得到t的次8位,这里右移16,还剩下16位怎么会是得到t的次8位呢,注意啊0xff的二进制是1111 1111 只有8位,在计算的b的时候它会把0xff前面用0补成16位就是0000 0000 1111 1111,前面8个0在进行与位运算的时候不管怎么样都是0,所以是得到t的次8位的数据,就这样将一个int值的t按照8位8位的一次取出数据。
那么肯定会有人这样想,你一个int你拆成4个int值,那不就将数据扩大了吗?当然是不会了,再看我们下面是每次写一个字节,a,b,c,d虽然都是int值,但是我们write()是写一个字节,一个字节是8位,那么只会将这个int值的后面8位数据写进去。
如果你把我上面说的都理解了,好了关于BMP你已经掌握了一半了。因为下面代码里面方法里面涉及的位运算,我想你肯定也可以自己看懂了。
写到这里突然感觉BMP的解析和哈夫曼的压缩和解压道理差不多,知道原理了,再一一写进去就可以了,读取的时候就是你读什么就写什么就可以了
好我们继续说BMP,BMP的解析,其实它的原理很简单:按照格式去写进去,按照格式来读就可以了。其实大多数人不懂都是那个关于二进制的位运算。下面我给出BMP前面54个字节的格式。
(一下格式参考湖南大学周圣韬的,自己只是做了一点点的修改) ① BMP文件头(14)字节 byte bfType1;//位图文件的类型,必须位’B’’1个字母(’B ‘ 1个字节) byte bfType2;//位图文件的类型,必须位’B’1个个字母(’M ‘ 1个字节) int bfSize;//位图文件的大小,以字节位单位(4个字节) short usignedshort bfReserved1;//位图文件保留字,必须为0(2个字节) short usignedshort bfReserved2;//位图文件保留字,必须为0(2个字节) int bfOffBits;//位图数据的起始位置,以相对于位图(文件头的偏移量表示,以字节为单位)(4个字节) ① BMP位图信息头(40)字节 BMP位图信息头数据用于说明位图的尺寸等信息 int Size;//本结构所占用字节数(4个字节) int image_width;//位图的宽度,以像素为单位(4个字节) int image_heigh;//位图的高度,以像素位单位(4个字节) short Planes;//目标设备的级别,必须位1(2个字节) short biBitCount;//每个像素所需的位数,必须是1(双色),4(16色), 8(256色)或24(//真彩色)之一(2个字节) int biCompression;//位图压缩类型,必须是0(不压缩),1(BI_RLE8 压缩类型)或2(BI_RLE4)之一(4个字节) int SizeImage;//位图的大小,以字节位单位(4个字节) int biXPelsPerMeter;//位图水平分辨率,每米像素数(4个字节) int biYPelsPerMeter;//位图垂直分辨率,每米像素数(4个字节) int biClrUsed;//位图实际使用的颜色表中的颜色数(4个字节) int biClrImportant;//位图显示过程中重要的颜色数(4个字节)
我们就先从保存说起。按照上面的格式写完之后,然后就开始写文件的数据了。
下面我给出代码,这里我需呀说明一下,我的这个BMP的代码是由何旭同学提供的,我只是拿出来给大家讲解。这个代码是属于画图板的一部分的,画图板的所有代码我以附件给出。
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.swing.JFileChooser; import javax.swing.JOptionPane; /** * 菜单处理监听器 * * @author kowloon * */ public class MenuListener implements ActionListener { private MyPanel panel; public MenuListener(MyPanel panel) { this.panel = panel; } // bmp文件头 public void savebmpTop(OutputStream ops) throws Exception { ops.write('B'); ops.write('M'); int height = DrawListener.array.length; int width = DrawListener.array[0].length; //注意这里的宽和高是像素的宽和高,24位图的话是一个像素3个//字节,所以一行的字节数应该是width*3 int size = 54+ height * width * 3 + (4 - width * 3 % 4) * height; writeInt(ops, size); // 保留字节,必须为零 writeShort(ops, (short) 0); writeShort(ops, (short) 0); // 位图偏移量 writeInt(ops, 54); } // 位图信息头 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); // 位图位面数总是为1 writeShort(ops, (short) 1); // 位图24位像素 writeShort(ops, (short) 24); // 位图是否被压缩,0为不压缩 writeInt(ops, 0); // 字节数代表位图大小 writeInt(ops, height * width * 3 + (4 - width * 3 % 4) * height); // 水平分辨率 writeInt(ops, 0); // 垂直分辨率 writeInt(ops, 0); // 颜色索引,0为所有调色板 writeInt(ops, 0); // 对图象显示有重要影响的颜色索引的数目。如果是0表示都重要 writeInt(ops, 0); } // 图像数据阵列 public void savebmpDate(OutputStream ops) throws Exception { int height = DrawListener.array.length; int width = DrawListener.array[0].length; int m = 0; // 进行补0 if (width * 3 % 4 > 0) { m = 4 - width * 3 % 4; } //其实每一个i代表一行数据,j=0说明是从左往右, //正好符合window的储存数据的格式 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); } } } public void writeInt(OutputStream ops, int t) throws Exception { int a = (t >> 24) & 0xff; //window里面储存数据是从高位到地位进行储存的,因为每次写一//个字节,所以是8位 //这里的0xff是可以变化的如果是一个字节那么它就是1111 1111 //如果是一个int那么它就是一个00000000 00000000 00000000 //11111111 int b = (t >> 16) & 0xff; int c = (t >> 8) & 0xff; int d = t & 0xff; //a是取前8位,一次每次取得t的8位,d是最后8位,然后反着//储存数据。 ops.write(d); ops.write(c); ops.write(b); ops.write(a); } 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); } public void writeShort(OutputStream ops, short t) throws Exception { int c = (t >> 8) & 0xff; int d = t & 0xff; ops.write(d); ops.write(c); } // 由于读取的是字节,把读取到的4个byte转化成1个int public int changeInt(InputStream 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; System.out.println(num); return num; } // 24位的图片是1个像素3个字节。 public int readColor(InputStream 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; //这里是读取,第一读取做位运算得到的这个数据的最低8位, //在读取得到原来数据的次8位,最后一次 //读取得到原来数据的最高8位,通过位运算把原来数据还原。 } public void actionPerformed(ActionEvent e) { // 获得被点击的组件的动作命令 String command = e.getActionCommand(); JFileChooser chooser = new JFileChooser(); if (command.equals("保存")) { int t = chooser.showSaveDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { FileOutputStream fos = new FileOutputStream(path); DataOutputStream dos = new DataOutputStream(fos); savebmpTop(dos); savebmpInfo(dos); savebmpDate(dos); fos.flush(); fos.close(); } catch (Exception ef) { JOptionPane.showMessageDialog(null, "文件保存失败!!"); ef.printStackTrace(); } } } else if (command.equals("打开")) { int t = chooser.showOpenDialog(null); if (t == JFileChooser.APPROVE_OPTION) { String path = chooser.getSelectedFile().getAbsolutePath(); try { FileInputStream fis = new FileInputStream(path); DataInputStream dis = new DataInputStream(fis); dis.skip(18); //skip(n)从输入流中跳过并丢弃 n个字节的数据 int width = changeInt(dis); // 跳过不需要的,读取宽度和高度 int height = changeInt(dis); dis.skip(28); // 跳过,直接读取位图数据。 DrawListener.array = new int[height][width]; int w = 0; //注意上面的width是源图的宽,是补零之前的宽度 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(dis); DrawListener.array[i][j] = c; } dis.skip(w); } fis.close(); // 刷新界面 panel.repaint(); } catch (Exception ef) { JOptionPane.showMessageDialog(null, "文件打开失败!!"); ef.printStackTrace(); } } } } }
看完上面的代码可能你还有一些不懂的,我说一下我之前的问题。
DrawListener.array中的array是一个二维数组,array数组的长度表示源图数据的高和宽,array中的每一个值表示的是一个表示颜色的RGB值,array是其他代码中得到的,这里直接用了,所以感觉还是不太懂的,可以下载全部代码,看一看array是怎么来的。
1.
说一下文件大小的计算,上面的采用24位图的,也就是一个像素占3个字节,54+height*width*3,54是前面BMP格式所占用的54个字节,height*width*3总共有height*width个像素,一个像素占用3个字节所以源文件的总共有height*width*3个字节,注意这是源文件的字节数,并不是我们得到BMP图片的大小,因为Windows规定一个扫描行所占的字节数必须是4的倍数,不足的以0补充,所以4 - width * 3 % 4这是每一行需要补0的字节,(4 - width * 3 % 4) * height这是总共需要补零的字节个数。这里应该理解很多地方是*3%4了吧,这样子就得出写出后BMP文件的大小是int size = 54+ height * width * 3 + (4 - width * 3 % 4) * height;
2.写完BMP头文件和位图信息头就开始写位图数据,再写的过程中进行了补零操作。
3.同理在读取数据的时候就按照你写什么就读什么就可以了,要注意的是我们存的时候是把int拆开写进去的,但是我们每次读取只是读取一个字节,并且顺序和源数据顺序相反,这就需要我们把把顺序调好再拼接成一个int类型,得出源数据。
这里你需要搞清楚的是之前54字节int值是被拆成4个byte写进去的,但是位图数据虽然是一个int值,但是它只有三个字节的大小,所以写进的时候是把它拆成三个byte写进去的。这样字我们在读取的时候前面54个字节里面的int需要去读4个字节来拼成源数据,后面读取位图数据的时候只需要三个字节就可以拼成源数据。
相关推荐
BMP文件格式详细分析,对于需要做BMP解析的入门资料非常棒。。
BMP图片格式解析 BMP图片格式是当前计算机图形系统中最为常用的图像文件格式之一。它是由微软公司开发的,用于存储位图图像的文件格式。该格式的文件通常以“.bmp”结尾。 BITMAPFILEHEADER结构体是BMP图片文件的...
在计算机图形处理中,将BMP图片解析为RGB(Red, Green, Blue)三原色模式是基础且重要的步骤,因为RGB是数字图像处理中广泛使用的颜色模型。本文将深入探讨BMP文件的结构以及如何将其解析为RGB模式。 首先,理解BMP...
BMP文件解析器是一款用于理解和处理BMP(Bitmap)图像文件的工具,主要针对BMP文件格式进行解析,并能展示图像内容。BMP是Windows操作系统中最常见的位图图像格式,它以未经压缩的原始数据存储图像,因此理解其内部...
本篇将深入讲解如何使用C语言解析BMP位图文件。 首先,我们要了解BMP文件的基本结构。BMP文件分为两个主要部分:文件头和图像数据。文件头包含关于图像的信息,如宽度、高度、颜色深度等。它由以下几个结构组成: ...
这个“BMP.rar”压缩包包含了关于BMP图像处理的相关资源,包括一个用VC++(Visual C++)编写的BMP图像解析程序。让我们深入探讨BMP格式以及如何解析和处理这种类型的图像。 BMP文件格式: 1. 文件结构:BMP文件由一...
本项目" BMP图片解析C程序 "是一个C语言编写的程序,专门设计来读取BMP文件,并将其内容显示在LCD(Liquid Crystal Display)屏幕上。此外,该程序还具有生成缩略图的功能,这对于资源有限的设备或者需要高效处理...
BMP文件格式解析,主要用来分析CMOS SENSOR 的RAW图的,可以看具体的RGB值。
BMP图片解析软件是一款专注于处理8位、16位和24位BMP图像格式的应用程序。BMP,全称为Bitmap,是一种无损的位图文件格式,广泛用于Windows和Microsoft Office等系统中。该软件目前的功能限制在于仅能解析这三种位...
"BMP 图像格式详解" BMP 图像格式是与硬件设备无关的图像文件格式,使用非常广泛。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP 文件所占用的空间很大。BMP 文件的图像深度可选 ...
BMP图像解析是计算机图形学中的一个重要概念,主要涉及数字图像处理和编程技术。BMP(Bitmap)是一种未经压缩的位图文件格式,广泛应用于Windows操作系统和一些其他平台。在这个项目中,你将在WinXP环境下使用VC6.0...
网上有很多BMP转Texture2d的代码,但是大多都不能用!...这个脚本是通过读取BMP文件的字节流解析协议直接将BMP从Byte[]解析出来的算法,是解析BMP的算法,根据这个算法可以在所有平台上解析.BMP格式的图片。
BMP文件格式详解与Linux下的解析方法 BMP(Bitmap)是一种常见的位图图像文件格式,广泛应用于Windows操作系统和许多其他应用程序中。它以未经压缩的原始像素数据存储图像,因此文件大小通常较大,但易于处理。尽管...
本教程将介绍如何在CentOS 7.4上使用C++解析BMP文件。 首先,我们需要理解BMP文件的结构。BMP文件以头部信息开始,包括文件头(BITMAPFILEHEADER)和位图信息头(BITMAPINFOHEADER)。文件头包含了文件类型、文件...
本篇将重点介绍BMP格式的基本结构以及如何使用Visual C++进行解析。 BMP格式的文件头包括两个部分:位图文件头(Bitmap File Header)和位图信息头(Bitmap Information Header)。位图文件头定义了文件的基本信息...
### BMP 图片格式解析知识点 #### 一、BMP 文件格式简介 BMP(Bitmap Image File Format)是一种用于存储位图图像的文件格式,由Microsoft公司设计,主要用于Windows操作系统。BMP格式支持单色和彩色图像,并且...
本压缩包提供的资源包括C语言实现的BMP位图读写封装源码(BMPTool.cpp、BMPTool.h)以及BMP位图文件格式的详细分析PDF(BMP文件格式详解.pdf)。以下是对这些知识点的深入讲解: 1. BMP文件格式分析: BMP文件通常...
一、BMP文件分析 1. 什么是BMP(位图)? 常见的图像文件格式有:BMP、JPG(JPE,JPEG)、GIF等。 BMP图像文件(Bitmap-File)格式是Windows采用的图像文件存储格式,在Windows环境下运行的所有图像处理软件都支持这种格式。...