Bmp
BMP是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此, BMP 文件所占用的空间很大。 BMP 文件的图像深度可选 lbit 、 4bit 、 8bit 及 24bit 。 BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。 由于 BMP 文件格式是 Windows 环境中交换与图有关的数据的一种标准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。
是在之前的画图板基础上添加的功能.
现在的界面很简单:
之前的画图板简介:
1.创建JFrame窗口,添加三个JPanel,在左边加上单选按钮选择形状
2.在DrawListener中是想抽象的鼠标适配器.
3.在正中间添加画布,添加监听器
4.画直线,矩形,椭圆,画笔功能.
5.重写paint方法
6.创建Shape类,Line,Rect等类继承Shape类,单独实现绘制功能,用ArrayList<Shape> 存放绘制形状的列表.顺便加入撤销功能.
7.保存读取文件(实际是保存List对象.)
24位BMP格式特点:包括BMP文件头 (14 字节 ),位图信息头(40 字节 ),颜色表(24位没有),数据. 四个部分
//BMP文件头
int bfType1= 0x42; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 )
int bfType2= 0x4d;
int bfSize=14; // 位图文件的大小,以字节为单位 (2-5 字节 ) *********
int bfReserved1=0; // 位图文件保留字,必须为 0(6-7 字节 )
int bfReserved2=0; // 位图文件保留字,必须为 0(8-9 字节 )
int bfOffBits=54; // 位图数据的起始位置,以相对于位图 (10-13 字节 )
//Bmp信息头
int Size=40; // 本结构所占用字节数 (14-17 字节 )
int image_width=DrawListener.colors[0].length; // 位图的宽度,以像素为单位 (18-21 字节 )
int image_height=DrawListener.colors.length; // 位图的高度,以像素为单位 (22-25 字节 )
int Planes=1; // 目标设备的级别,必须为 1(26-27 字节 )
int biBitCount=24;// 每个像素所需的位数,必须是 1(双色),(28-29 字节) 4(16 色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一
int biCompression=0; // 位图压缩类型,必须是 0( 不压缩 ),(30-33 字节 ) 1(BI_RLE8 压缩类型 ) 或// 2(BI_RLE4 压缩类型 ) 之一
int SizeImage=height*width; // 位图的大小,以字节为单位 (34-37 字节 )
int biXPelsPerMeter=0; // 位图水平分辨率,每米像素数 (38-41 字节 ) 0是默认值
int biYPelsPerMeter=0; // 位图垂直分辨率,每米像素数 (42-45 字节 ) 0是默认值
int biClrUsed=0;// 位图实际使用的颜色表中的颜色数 (46-49 字节 ) 0说明全部用了
int biClrImportant=0;// 位图显示过程中重要的颜色数 (50-53 字节 ) 0说明全部重要
保存bmp文件就是先将这些数据保存在文件中,从第54字节开始保存位图数据.每个像素点都按照bgr的顺序(与red green blue相反)保存在文件中.每三个字节合成一个像素点的颜色.
注意:由于获得像素点颜色是用java提供的getRGB方法,返回的是一个int整数,需要拆分成4个byte.从低到高位依次是bgra.也就是说,bytes[0]是蓝色,bytes[1]是绿色,bytes[2]是红色.
public byte[] int2byte(int data){
byte[] bytes = {(byte)(((data)<<24)>>24),(byte)(((data)<<16)>>24),(byte)(((data)<<8)>>24),(byte)((data)>>24)};
return bytes;
}
将bytes[]合成int的方法(读取文件的时候用到)
public static int byte2int(byte b[]) {
int b3 = b[3] & 0xff;
int b2 = b[2] & 0xff;
int b1 = b[1] & 0xff;
int b0 = b[0] & 0xff;
int i = b3 << 24 | b2 << 16 | b1 << 8 | b0;
return i;
}
完整的FileSave代码:
public class FileSave {
static int height,width;
static int size;
static JFileChooser jfc = new JFileChooser("D:\\");
/**
* 保存hb格式图片
* @param f 文件
* @param b 形状对象
*/
public static void saveHb(File f,ArrayList<Shape> b){
try{
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
size=b.size();
oos.writeInt(size);//保存size,避免重新打开程序后不知道要读多少个对象,导致无法读取数据
for(int i=0;i<size;i++){
oos.writeObject(b.get(i));
}
oos.flush();
oos.close();
fos.close();
}catch(IOException e){
e.printStackTrace();
}
}
/**
* 保存成bmp的方法
* @param f bmp文件地址
*/
public void write24bmp(File f){
//BMP文件头
int bfType1= 0x42; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 )
int bfType2= 0x4d;
int bfSize=14; // 位图文件的大小,以字节为单位 (2-5 字节 ) *********
int bfReserved1=0; // 位图文件保留字,必须为 0(6-7 字节 )
int bfReserved2=0; // 位图文件保留字,必须为 0(8-9 字节 )
int bfOffBits=54; // 位图数据的起始位置,以相对于位图 (10-13 字节 )
//Bmp信息头
int Size=40; // 本结构所占用字节数 (14-17 字节 )
int image_width=DrawListener.colors[0].length; // 位图的宽度,以像素为单位 (18-21 字节 )
int image_height=DrawListener.colors.length; // 位图的高度,以像素为单位 (22-25 字节 )
int Planes=1; // 目标设备的级别,必须为 1(26-27 字节 )
int biBitCount=24;// 每个像素所需的位数,必须是 1(双色),(28-29 字节) 4(16 色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一
int biCompression=0; // 位图压缩类型,必须是 0( 不压缩 ),(30-33 字节 ) 1(BI_RLE8 压缩类型 ) 或// 2(BI_RLE4 压缩类型 ) 之一
int SizeImage=height*width; // 位图的大小,以字节为单位 (34-37 字节 )
int biXPelsPerMeter=0; // 位图水平分辨率,每米像素数 (38-41 字节 ) 0是默认值
int biYPelsPerMeter=0; // 位图垂直分辨率,每米像素数 (42-45 字节 ) 0是默认值
int biClrUsed=0;// 位图实际使用的颜色表中的颜色数 (46-49 字节 ) 0说明全部用了
int biClrImportant=0;// 位图显示过程中重要的颜色数 (50-53 字节 ) 0说明全部重要
//个人理解,用byte定义这些属性的话虽然不一定超出范围,但是写数据出去的时候不方便控制字节数,例如size占4字节,Planes占2字节,如果用byte定义还要判断填0
//不如直接用int然后再转为byte[4],再用write(byte[],开始位置,长度)这一个方法直接写出去.
//写出去文件头信息头
try {
FileOutputStream fos = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(bfType1);
bos.write(bfType2);
bos.write(int2byte(bfSize),0,4);
bos.write(int2byte(bfReserved1),0,2);
bos.write(int2byte(bfReserved2),0,2);
bos.write(int2byte(bfOffBits),0,4);
bos.write(int2byte(Size),0,4);// 输入信息头数据的总字节数
bos.write(int2byte(image_width),0,4);// 输入位图的宽
bos.write(int2byte(image_height),0,4);// 输入位图的高
bos.write(int2byte(Planes),0,2);// 输入位图的目标设备级别
bos.write(int2byte(biBitCount),0,2);// 输入每个像素占据的字节数
bos.write(int2byte(biCompression),0,4);// 输入位图的压缩类型
bos.write(int2byte(SizeImage),0,4);// 输入位图的实际大小
bos.write(int2byte(biXPelsPerMeter),0,4);// 输入位图的水平分辨率
bos.write(int2byte(biYPelsPerMeter),0,4);// 输入位图的垂直分辨率
bos.write(int2byte(biClrUsed),0,4);// 输入位图使用的总颜色数
bos.write(int2byte(biClrImportant),0,4);// 输入位图使用过程中重要的颜色数
//24位没有颜色表,所以接下来输出颜色信息.将我们的int数组变成byte[4]后将b[0] b[1] b[2] 依次作为blue green red输出去
// 这里遍历的时候注意,在计算机内存中位图数据是从左到右,从下到上来保存的,
// 也就是说实际图像的第一行的点在内存是最后一行
for (int h = image_height - 1; h >= 0; h--) {
for (int w = 0; w < image_width; w++) {
// 这里还需要注意的是,每个像素是有三个RGB颜色分量组成的,
// 而数据在windows操作系统下是小端存储,对多字节数据有用。
byte b[]=new byte[4];
b= int2byte(DrawListener.colors[h][w]);
byte red = b[2];// 得到红色分量
byte green = b[1];// 得到绿色分量
byte blue = b[0];// 得到蓝色分量
bos.write(blue);
bos.write(green);
bos.write(red);
}
}
//关闭数据的传输
bos.flush();
bos.close();
fos.close();
System.out.println("写入成功!!!");
} catch (IOException e) {
e.printStackTrace();
}
}
public byte[] int2byte(int data){
byte[] bytes = {(byte)(((data)<<24)>>24),(byte)(((data)<<16)>>24),(byte)(((data)<<8)>>24),(byte)((data)>>24)};
//{BGRA}
return bytes;
}
public static int byte2int(byte b[]) {
int b3 = b[3] & 0xff;
int b2 = b[2] & 0xff;
int b1 = b[1] & 0xff;
int b0 = b[0] & 0xff;
int i = b3 << 24 | b2 << 16 | b1 << 8 | b0;
return i;
}
/**
* 读取bmp的方法
* @param f
*/
public static void read24bmp(File f,Graphics g) {
try {
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
try {
int [][]data = null;
if (bis.read() == 66 && bis.read() == 77) {
bis.skip(16);// 先去除才开始18个,再取出4个byte拼成一个int型的宽度
byte wi[] = new byte[4];
bis.read(wi);
width = byte2int(wi);
byte he[] = new byte[4];
bis.read(he);
height = byte2int(he);
data = new int[height][width];
bis.skip(28);
int skipNum=4-width*3%4;
for (int h = height - 1; h >= 0; h--) {
for (int w = 0; w < width; w++) {
//
int b = bis.read();
int green = bis.read();
int r = bis.read();
Color c = new Color(r,green,b);
data[h][w] = c.getRGB();
}
if (skipNum != 4) {
bis.skip(skipNum);
}
}
bis.close();
fis.close();
DrawUI.center.setPreferredSize(new Dimension(width,height));
javax.swing.SwingUtilities.updateComponentTreeUI(DrawUI.center);
}
if (data != null) {
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
Color c = new Color(data[i][j]);
Line line = new Line(j, i, j, i, c);
line.draw(g);
if(data[i][j]!=-1){
DrawListener.list.add(line);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
* 读取hb格式图片
* @param f 文件
* @param b 形状对象
*/
public static ArrayList readHb(File f){
ArrayList<Shape> b=new ArrayList<Shape>();
try{
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
size = ois.readInt();
for(int i=0;i<size;i++){
try {
b.add((Shape) ois.readObject());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
ois.close();
fis.close();
return b;
}catch(EOFException e){
System.out.println("读取上次自动保存的文件发生错误.");
}
catch(IOException e){
e.printStackTrace();
}
return null;
}
public static void read(Graphics g){
int state = jfc.showOpenDialog(null);
if (state == 0) {// 点击了打开按钮
File f = new File(jfc.getSelectedFile().getAbsolutePath());
try {
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
if (bis.read() == 66 && bis.read() == 77) {
bis.close();
fis.close();
read24bmp(f,g);
}else{
bis.close();
fis.close();
DrawListener.list =readHb(f);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
其余类代码参考: