以前玩CF的时候,官方网站有个网页鼠标进入左右移动两张图片可以进行切换,看起来很好看。
数字图象处理课上需要将BMP图像进行处理,还要用C++,于是借此机会专门了解了BMP,了解下图片不同于文本的2进制文件,了解下C++。
之后是一个一个字节一个字节的测试了BMP图片的信息,做了一个可以切换图片的东西完了一下。
关于BMP自己的测试理解是:前54字节是位图信息即文件的一些信息,之后如果图像深度是少于24位的有调色板也称颜色查询器,之后是存放索引的一个数组,对于24位的是没有调色板这一项,直接用3个字节保存每个像素的B、G、R,的数组。有个文档很专业分享下。
测试代码是:
/** * 14个字节大小的:位图文件头 */ System.out.println("格式是: " + (char) dis.readByte()); System.out.println("格式是(共2字节): " + (char) dis.readByte()); // 此处可以借助windows 的计算器实现逆序的转换 System.out.println("文件大小4字节---第一个: " + dis.readByte()); System.out.println("文件大小4字节---第二个 : " + dis.readByte()); System.out.println("文件大小4字节---第三个 : " + dis.readByte()); System.out.println("文件大小4字节---第四个: " + dis.readByte()); System.out.println("位图文件头 --文件大小逆序后转换成了: 525430"); System.out.println("位图文件头 --文件保留大小: " + dis.readInt()); System.out.println("文件偏移4字节---第一个: " + dis.readByte()); System.out.println("文件偏移4字节---第二个 : " + dis.readByte()); System.out.println("文件偏移4字节---第三个 : " + dis.readByte()); System.out.println("文件偏移4字节---第四个: " + dis.readByte()); System.out.println("位图文件头 --文件偏移逆序后转换成了: 118字节"); // 16色调色板是64字节+文件头14+位图信息头40=118 /** * 40字节的位图信息头 */ System.out.println("位图信息头之大小4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之大小4字节---第二个 : " + dis.readByte()); System.out.println("位图信息头之大小4字节---第三个 : " + dis.readByte()); System.out.println("位图信息头之大小4字节---第四个: " + dis.readByte()); System.out.println("位图信息头--大小逆序后转换成了: 40字节"); System.out.println("位图信息头之位图宽度4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图宽度4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图宽度4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图宽度4字节---第四个: " + dis.readByte()); System.out.println("位图信息头--宽度逆序后转换成了: 1366字节"); System.out.println("位图信息头之位图高度4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图高度4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图高度4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图高度4字节---第四个: " + dis.readByte()); System.out.println("位图信息头--高度逆序后转换成了: 768字节"); System.out.println("位图信息头--位面数2字节 --- 第一个:" + dis.readByte()); System.out.println("位图信息头--位面数2字 节 --- 第二个:" + dis.readByte()); System.out.println("位图信息头--位面数逆序后转换成了数值是: 1"); System.out.println("位图信息头--图像深度2字节:---第一个:" + dis.readByte()); System.out.println("位图信息头--图像深度2字节:---第二个:" + dis.readByte()); System.out.println("位图信息头--图像深度逆序后准换成的值是: 4"); System.out.println("位图信息头--压缩编码4字节:---第一个" + dis.readByte()); System.out.println("位图信息头--压缩编码4字节:---第二个" + dis.readByte()); System.out.println("位图信息头--压缩编码4字节:---第三个" + dis.readByte()); System.out.println("位图信息头--压缩编码4字节:---第四个" + dis.readByte()); System.out.println("4个全是0 表示是:-------无压缩"); System.out.println("位图信息头之位图数据大小4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图数据大小4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图数据大小4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图数据大小4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之位图数据 逆序转换数值是 525312+位图文件头+位图信息头+彩色表=总的大小"); System.out.println("位图信息头之水平分辨率4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率转换成最后的数值是: 4个0 "); System.out.println("位图信息头之垂直分辨率4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率转换成最后的数值是:4个0 "); System.out.println("位图信息头之位图使用的颜色数4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数 4个0"); System.out.println("位图信息头之位图使用的重要颜色数4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4个0 表示彼此都很重要");
上面是24位前54个字节的信息测试,有一点是:文件大小是4个字节表示的,假如是int 1,图像中存放数据是00000001 00000000 00000000 000000000 以字节为单位逆序排放。
下面是深度为24位的BMP图片切换代码:
package com.wlh.BMPPhoto; import java.awt.Color; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseWheelEvent; import java.io.*; import javax.swing.*; public class BMPPhoto_1_24 extends JFrame { int width = 1366; int height = 768; // 定义rgb数组 int[][] R=new int[height][width]; int[][] G=new int[height][width]; int[][] B=new int[height][width]; int[][] R1=new int[height][width]; int[][] G1=new int[height][width]; int[][] B1=new int[height][width]; int count = 0; int count1=0; //表示是否读取文件完毕 可用于paint的时候的判断 boolean state=false; private Graphics gra; int myTest_X=1366; // 构造函数 public BMPPhoto_1_24() { this.setTitle("BMP测试"); this.setSize(1366, 760); this.setVisible(true); this.setAlwaysOnTop(true); this.setDefaultCloseOperation(3); gra = this.getGraphics(); } public static void main(String args[]) { BMPPhoto_1_24 bmp = new BMPPhoto_1_24(); try { bmp.readBMP(); } catch (IOException e) { e.printStackTrace(); } } // 读取BMP图像的方法 public void readBMP() throws IOException { InputStream in = new FileInputStream("F:\\1.bmp"); DataInputStream dis = new DataInputStream(in); InputStream in_1 = new FileInputStream("F:\\22.bmp"); DataInputStream dis_1 = new DataInputStream(in_1); /** * 14个字节大小的:位图文件头 */ System.out.println("格式是: " + (char) dis.readByte()); System.out.println("格式是(共2字节): " + (char) dis.readByte()); // 此处可以借助windows 的计算器实现逆序的转换 System.out.println("文件大小4字节---第一个: " + dis.readByte()); System.out.println("文件大小4字节---第二个 : " + dis.readByte()); System.out.println("文件大小4字节---第三个 : " + dis.readByte()); System.out.println("文件大小4字节---第四个: " + dis.readByte()); System.out.println("位图文件头 --文件大小逆序后转换成了: 525430"); System.out.println("位图文件头 --文件保留大小: " + dis.readInt()); System.out.println("文件偏移4字节---第一个: " + dis.readByte()); System.out.println("文件偏移4字节---第二个 : " + dis.readByte()); System.out.println("文件偏移4字节---第三个 : " + dis.readByte()); System.out.println("文件偏移4字节---第四个: " + dis.readByte()); System.out.println("位图文件头 --文件偏移逆序后转换成了: 118字节"); // 16色调色板是64字节+文件头14+位图信息头40=118 /** * 40字节的位图信息头 */ System.out.println("位图信息头之大小4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之大小4字节---第二个 : " + dis.readByte()); System.out.println("位图信息头之大小4字节---第三个 : " + dis.readByte()); System.out.println("位图信息头之大小4字节---第四个: " + dis.readByte()); System.out.println("位图信息头--大小逆序后转换成了: 40字节"); System.out.println("位图信息头之位图宽度4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图宽度4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图宽度4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图宽度4字节---第四个: " + dis.readByte()); System.out.println("位图信息头--宽度逆序后转换成了: 1366字节"); System.out.println("位图信息头之位图高度4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图高度4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图高度4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图高度4字节---第四个: " + dis.readByte()); System.out.println("位图信息头--高度逆序后转换成了: 768字节"); System.out.println("位图信息头--位面数2字节 --- 第一个:" + dis.readByte()); System.out.println("位图信息头--位面数2字 节 --- 第二个:" + dis.readByte()); System.out.println("位图信息头--位面数逆序后转换成了数值是: 1"); System.out.println("位图信息头--图像深度2字节:---第一个:" + dis.readByte()); System.out.println("位图信息头--图像深度2字节:---第二个:" + dis.readByte()); System.out.println("位图信息头--图像深度逆序后准换成的值是: 4"); System.out.println("位图信息头--压缩编码4字节:---第一个" + dis.readByte()); System.out.println("位图信息头--压缩编码4字节:---第二个" + dis.readByte()); System.out.println("位图信息头--压缩编码4字节:---第三个" + dis.readByte()); System.out.println("位图信息头--压缩编码4字节:---第四个" + dis.readByte()); System.out.println("4个全是0 表示是:-------无压缩"); System.out.println("位图信息头之位图数据大小4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图数据大小4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图数据大小4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图数据大小4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之位图数据 逆序转换数值是 525312+位图文件头+位图信息头+彩色表=总的大小"); System.out.println("位图信息头之水平分辨率4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之水平分辨率转换成最后的数值是: 4个0 "); System.out.println("位图信息头之垂直分辨率4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之垂直分辨率转换成最后的数值是:4个0 "); System.out.println("位图信息头之位图使用的颜色数4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之位图使用的颜色数 4个0"); System.out.println("位图信息头之位图使用的重要颜色数4字节---第一个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4字节---第二个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4字节---第三个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4字节---第四个: " + dis.readByte()); System.out.println("位图信息头之位图使用的重要颜色数4个0 表示彼此都很重要"); /** * 下面开始读取调色板 width=1366 的深度是24的bmp 需要行对齐 每行补上2个字节 */ // 初始化颜色索引数组 int b; int g; int r; for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { count++; // 当达到1366 的整数倍的时候 跳过两个字节 ======专门为1366 分辨率设置的算法 if (count % 1366 == 0) { dis.readByte(); dis.readByte(); } //读取RGB b = dis.readByte(); g = dis.readByte(); r = dis.readByte(); if(b<0){ b = 256 + b; } if(g<0){ g= 256 + g; } if(r<0){ r=256+r; } B[i][j]=b; G[i][j]=g; R[i][j]=r; //画到JFrame上 this.gra.setColor(new Color(r, g, b)); this.gra.drawLine(j, i, j, i); } } dis_1.skip(54); for (int i = height - 1; i >= 0; i--) { for (int j = 0; j < width; j++) { count1++; // 当达到1366 的整数倍的时候 跳过两个字节 ======专门为1366 分辨率设置的算法 if (count1 % 1366 == 0) { dis_1.readByte(); dis_1.readByte(); } //读取RGB b = dis_1.readByte(); g = dis_1.readByte(); r = dis_1.readByte(); if(b<0){ b = 256 + b; } if(g<0){ g= 256 + g; } if(r<0){ r=256+r; } B1[i][j]=b; G1[i][j]=g; R1[i][j]=r; } } state=true; System.out.println("第二章初始化完毕......"); this.addMouseMotionListener(new MouseAdapter(){ public void mouseMoved(MouseEvent e){ if(state){ int x=e.getX(); System.out.println("测试的X:="+x); if(x<myTest_X){ for(int i=0;i<height;i++){ for(int j=x;j<myTest_X;j++){ gra.setColor(new Color(R1[i][j], G1[i][j], B1[i][j])); gra.drawLine(j, i, j, i); } } }else{ for(int i=0;i<height;i++){ for(int j=myTest_X;j<x;j++){ gra.setColor(new Color(R[i][j], G[i][j], B[i][j])); gra.drawLine(j, i, j, i); } } } myTest_X=x; } } }); } public void paint(Graphics g){ if(state){ for(int i=0;i<height;i++){ for(int j=0;j<width;j++){ gra.setColor(new Color(R[i][j], G[i][j], B[i][j])); gra.drawLine(j, i, j, i); } } } } }
下面是深度为8位的,和24位不同的是需要先读取出文件的调色板信息,根据数组里的索引查看BGR:
package com.wlh.BMPPhoto; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.*; public class BMPPhoto_1_256 extends JFrame { /** * 读取深度为8位的BMP指定图像 */ // 定义256色的调色板 BMPColor[] colorSelector_1 = new BMPColor[256]; BMPColor[] colorSelector_2 = new BMPColor[256]; // 定义一个存放索引数据的信息数组 // 图片1 int weith_1 = 1048;// 存放索引的数组的长度 不一定等于图片的宽度 int height_1 = 468;// 存放索引的数组的高度=图片的高度 int[][] data_1 = new int[height_1][weith_1]; // 图片2 int weith_2 = weith_1; int height_2 = height_1; int[][] data_2 = new int[height_2][weith_2]; //定义一个状态是为了在paint在两张图片数据初始化完成后才能进行重绘 boolean state = false; //画笔 private Graphics G; //用来保存上次鼠标监听的位置 private int myText_X=weith_1; public static void main(String[] args) { BMPPhoto_1_256 bmp = new BMPPhoto_1_256(); } public BMPPhoto_1_256() { this.setTitle("BMP 之深度为8位测试"); this.setAlwaysOnTop(true); this.setSize(1050, 470); this.setDefaultCloseOperation(3); this.setVisible(true); try { // 创建两个文件流对象 InputStream in_1 = new FileInputStream("F:\\b1.bmp"); DataInputStream dis_1 = new DataInputStream(in_1); /** * 读取第一个文件 */ // 先跳过位图信息部分 直接读取调色板信息 dis_1.skipBytes(54); for (int i = 0; i < 256; i++) { /** * 先初始化对象 */ colorSelector_1[i] = new BMPColor(); int b = dis_1.readByte(); int g = dis_1.readByte(); int r = dis_1.readByte(); dis_1.readByte(); if (g < 0) { //至于此处 DataInputStream的方法readByte 返回的是-128——127的值 要进行转换 g += 256; colorSelector_1[i].g = g; } else { colorSelector_1[i].g = g; } if (r < 0) { r += 256; colorSelector_1[i].r = r; } else { colorSelector_1[i].r = r; } if (b < 0) { b += 256; colorSelector_1[i].b = b; } else { colorSelector_1[i].b = b; } } // 数组的索引初始化 for (int i = data_1.length - 1; i > 0; i--) {// 468 行 for (int j = 0; j < data_1[i].length; j++) {// 1048列 byte index = dis_1.readByte(); if (index < 0) { //此处转换同样是上面的道理返回的有可能是负数 data_1[i][j] = index + 256; } else { data_1[i][j] = index; } } } state = true; // 将图片画在JFrame上 G = this.getGraphics(); BMPColor bmpCol = new BMPColor(); for (int i = 0; i < data_1.length; i++) { for (int j = 0; j < data_1[i].length; j++) { // 取出调色板对应的索引的颜色类对象 bmpCol = colorSelector_1[data_1[i][j]]; G.setColor(new Color(bmpCol.getR(), bmpCol.getG(), bmpCol .getB())); G.drawLine(j, i, j, i); } } /** * 初始化第二个图片 */ InputStream in_2 = new FileInputStream("F:\\b2.bmp"); DataInputStream dis_2 = new DataInputStream(in_2); dis_2.skipBytes(54); for (int i = 0; i < 256; i++) { /** * 先初始化对象 */ colorSelector_2[i] = new BMPColor(); int b = dis_2.readByte(); int g = dis_2.readByte(); int r = dis_2.readByte(); dis_2.readByte(); if (g < 0) { g += 256; colorSelector_2[i].g = g; } else { colorSelector_2[i].g = g; } if (r < 0) { r += 256; colorSelector_2[i].r = r; } else { colorSelector_2[i].r = r; } if (b < 0) { b += 256; colorSelector_2[i].b = b; } else { colorSelector_2[i].b = b; } } // 数组的索引初始化 for (int i = data_2.length - 1; i > 0; i--) {// 468 行 for (int j = 0; j < data_2[i].length; j++) {// 1048列 byte index = dis_2.readByte(); if (index < 0) { data_2[i][j] = index + 256; } else { data_2[i][j] = index; } } } System.out.println("第二章图片初始化完成........"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } this.addMouseMotionListener(new MouseAdapter(){ public void mouseMoved(MouseEvent e) { System.out.println("@@@@@@@@@@@@@@"); if(state){ int x=e.getX(); System.out.println("Enter: X="+myText_X); //通过x进行X方向的定位 //右边 BMPColor bmpColor; if(x<myText_X){ for(int i=0;i<data_2.length;i++){ for(int j=x;j<myText_X;j++){ bmpColor=colorSelector_2[data_2[i][j]]; G.setColor(new Color(bmpColor.r, bmpColor.g, bmpColor.b)); G.drawLine(j, i, j, i); } } }else{ //左边 for(int i=0;i<data_1.length;i++){ for(int j=myText_X;j<x;j++){ bmpColor=colorSelector_1[data_1[i][j]]; G.setColor(new Color(bmpColor.r, bmpColor.g, bmpColor.b)); G.drawLine(j, i, j, i); } } } myText_X=e.getX(); } } }); } class BMPColor { int b = 0; int g = 0; int r = 0; int a = 0; public int getB() { return b; } public int getG() { return g; } public int getR() { return r; } public int getA() { return a; } public void set_B(int b) { if (b < 0) { b += 256; } this.b = b; } public void set_G(int g) { if (g < 0) { g += 256; } this.g = g; } public void set_R(int r) { if (r < 0) { r += 256; } this.r = r; } public void set_A(int a) { if (a < 0) { a += 256; } this.a = a; } } public void paint(Graphics g) { if (state) { BMPColor bmpCol = new BMPColor(); for (int i = 0; i < data_1.length; i++) { for (int j = 0; j < data_1[i].length; j++) { // 取出调色板对应的索引的颜色类对象 bmpCol = colorSelector_1[data_1[i][j]]; g.setColor(new Color(bmpCol.getR(), bmpCol.getG(), bmpCol.getB())); g.drawLine(j, i, j, i); } } } } }