您的位置:首页 > 其它

画图板小结

2016-03-12 17:01 316 查看

画图板的24位位图BMP保存

       将画图板完善了一些,实现了画图板的重绘及24位位图BMP的保存与打开,算的上是一个较为完善的XP画图板,现在对画图板的制作过程做个小结:

(带有部分代码介绍,详细代码会以附件的形式传上来,完成的最终成品在com.jk.yt.draw9包中,其余包均为一步步完善的过程。)

(之前所做的画图板只实现了按钮和取色的功能,详细的介绍及代码可以去http://leaf-stop.iteye.comhttps://blog.csdn.net/blog/2251203查看,这里将不做详细介绍,)

 

1.画图板界面的绘制

       先绘制一个总界面,然后将他们分为三个部分,左边面板添加形状选择工具,中间面板添加绘图面板,底部面板添加颜色选择工具,最后加上一个菜单栏就完成了画图板的基本布局,如下图所示:

 

2.按钮功能的实现

       先利用按钮组对象,给单选按钮进行逻辑分组ButtonGroup group = new ButtonGroup();再创建一个DrawListener监听器,DrawListener dlis = new DrawListener(drawPanel, group, frontLabel,backLabel);将各个参数传入,后由mousePressed,mouseReleased,mouseDragged实现其按钮的功能。

 

3.颜色的选择

       先定义好颜色数组,然后创建ColorListener 监听器,由ColorListener clis = new ColorListener(frontLabel, backLabel);传入前景色和背景色。我们还可以创建一个颜色板(新增功能),获得更多颜色选择,代码如下:
 

JButton jb = new JButton("颜色板");
jb.setBounds(470, 30, 90, 40);
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Color newcolor = JColorChooser.showDialog(drawPanel, "调色板",
drawPanel.getBackground());
if (newcolor != null) {
frontLabel.setBackground(newcolor);// JFrame类里面只能由内容面板来设定颜色
}
}
});
foot.add(jb);

 

 

4.重绘

       在DrawListener中,

1)根据面板大小创建保存面板数据的二维数组

// 获得面板的大小
Dimension dim = drawPanel.getPreferredSize();
// 根据面板大小创建保存面板数据的二维数组
array = new int[dim.height][dim.width];
// 保存初始颜色
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
array[i][j] = Color.WHITE.getRGB();
}
}
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}

 

2)由截屏方式实现重绘

// 释放一次,就从新保存一次
// 1.截屏
Point point = drawPanel.getLocationOnScreen();
Dimension dim = drawPanel.getPreferredSize();
Rectangle screenRect = new Rectangle(point, dim);
BufferedImage bufferImg = robot.createScreenCapture(screenRect);
// 根据面板大小调整数组大小
array = new int[dim.height][dim.width];

for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
array[i][j] = bufferImg.getRGB(j, i);
}
}

 

5.     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图片。

        读取一个BMP文件实际上只要获取到文件头的宽度,高度和它的位图数据,而其他部分都是可以跳过的。

 

 (1).创建一个BMP文件的读取类:BMPReader

/**
* BMP文件的读取类
*
* @author leaf-stop
*
*/
public class BMPReader {
/**
* 读取BMP文件的方法
*
* @param path
*            文件路径
* @param panel
*            显示的画板
* @throws IOException
*             流异常
*/
public void readBMP(String path, MyPanel 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).创建一个BMP文件的保存写入类:BMPWriter

/**
* BMP文件的保存写入类
*
* @author leaf-stop
*
*/
public class BMPWriter {

/**
* 保存BMP图片头部信息的方法
*
* @param fos
*            输出流
* @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);
}

}

 

(3).创建一个监听器:MenuListener来实现BMP的打开和保存。

 

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileOutputStream;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

/**
* 菜单处理监听器
*
* @author leaf-stop
*
*/
public class MenuListener implements ActionListener {

private MyPanel panel;

public MenuListener(MyPanel panel) {
this.panel = panel;
}

@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// 获得被点击的组件的动作命令
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);
BMPWriter writer = new BMPWriter();
writer.savebmpTop(fos);
writer.savebmpInfo(fos);
writer.savebmpDate(fos);
fos.flush();
fos.close();

} catch (Exception e1) {
// TODO Auto-generated catch block
JOptionPane.showMessageDialog(null, "文件保存失败");
e1.printStackTrace();
}

}
} 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 e1) {
// TODO Auto-generated catch block
JOptionPane.showMessageDialog(null, "文件打开失败");
e1.printStackTrace();
}

}
}

}
}

 

         最后,到这里我目前所做的画图板就基本完成了,前期的布局,取色较为简单,重绘及BMP保存与打开较为困难,参考了很多资料才略懂,有些按钮的功能还是没有实现,仍需要努力,希望大家指出代码中的不足,并与我交流改进,完善画图板。

ps:虚线五角星的绘制是新增的(但不是正五角星),这里将不再介绍,请查看附件。其它按钮的功能实现在另一个博客http://leaf-stop.iteye.comhttps://blog.csdn.net/blog/2251203中有介绍

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: