容易画板的保存与读取
(文件数据的写入顺序要与读取顺序一致)
一.用自定义的队列保存图像与读取图像
由于在画图板上每画一种图形,就相当于在自定义的队列中装入此图形,那么在保存画图板图形时,就只需从自定义队列中获取每个图形的基本属性,如颜、点的坐标,图形的种类,然后将这些基本信息写入文件,存储到硬盘。不过先要写入图形的个数,方便打开时获得队列的长度。
打开保存的图形时,只需要定义一个自定义队列,在从保存的文件中读取图形的中种类,根据种类不同,分别读取图形的属性,颜色、坐标,创建具有这些属性的图形队形,装入到队列中去。然后再根据队列中的装入的图形,通过draw方法画出来。
保存:
①文件头信息:int(表示队列中图形的个数)
②:文件数据信息:1)直线:int(表type)+int(表color值)+int+int+int+int
2)矩形:int(表type)+int(表color值)+int+int+int+int
3)椭圆:int(表type)+int(表color值)+int+int+int+int
4)多边形;int(表type)+int(表color值)+int[]
/** * 文件的保存 * * @param shapes * :要保存的图形队列 * @param path * :保存的路径 * @return:是否保存成功 */ public boolean savefile(QueueImp<Shape> shapes, String path) { boolean isb = false; try { // 根据路径创建一个文件输出流对象 java.io.FileOutputStream fos = new java.io.FileOutputStream(path); // 将文件流包装为基本类型数据流 java.io.DataOutputStream dos = new java.io.DataOutputStream(fos); // 先写入队列中图形的个数 dos.writeInt(shapes.size()); // 读取队列 for (int i = 0; i < shapes.size(); i++) { // 取出一个形状 Shape sh = shapes.get(i); // 得到形状的类型 byte type = sh.type; // 写入形状类型 dos.writeByte(type); if (type == 0) { // 如果是直线 // 将形状强制转化为直线 Line line = (Line) sh; // 得到直线的属性 int x1 = line.x1; int y1 = line.y1; int x2 = line.x2; int y2 = line.y2; int rgb = line.color.getRGB(); // 写入属性 dos.writeInt(rgb); dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); } else if (type == 1) { // 如果是矩形 // 将形状强制转化为矩形 Rect rect = (Rect) sh; // 得到矩形的属性 int x1 = rect.x1; int y1 = rect.y1; int x2 = rect.x2; int y2 = rect.y2; int rgb = rect.color.getRGB(); // 写入属性 dos.writeInt(rgb); dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); } else if (type == 2) { // 如果是椭圆 // 将形状强制转化为椭圆 Oval oval = (Oval) sh; // 得到椭圆的属性 int x1 = oval.x1; int y1 = oval.y1; int x2 = oval.x2; int y2 = oval.y2; int rgb = oval.color.getRGB(); // 写入属性 dos.writeInt(rgb); dos.writeInt(x1); dos.writeInt(y1); dos.writeInt(x2); dos.writeInt(y2); } else if (type == 3) { // 如果是多边形 // 将形状强制转化为多边形 Polygon polygon = (Polygon) sh; // 得到颜色 int rgb = polygon.color.getRGB(); // 写入颜色 dos.writeInt(rgb); // 创建一个队列,用来得到多边形的属性 QueueImp<Integer> gp = polygon.point; // 写入队列的长度 dos.writeInt(gp.size()); // 遍历队列,写入属性 for (int j = 0; j < gp.size(); j++) { int p = gp.get(j); dos.writeInt(p); } } isb = true; } // 强制刷新 dos.flush(); // 关闭流 fos.close(); } catch (Exception ef) { ef.printStackTrace(); } return isb; }
读取:按照写入文件的顺序,将数据包装成形状一个个读取出来,保存到队列中去
/** * 读取文件 * * @param path * :所读取的文件的路径 * @return:图形数组 */ public QueueImp<Shape> openfile(String path) { // 创建一个用来保存图形的队列 QueueImp<Shape> shapes = new QueueImp<Shape>(); try { // 根据路径创建输入流对象 java.io.FileInputStream fis = new java.io.FileInputStream(path); // 将输入流包装成基本类型的数据流 java.io.DataInputStream dis = new java.io.DataInputStream(fis); // 读取一个int,表示图形队列中图形的个数 int len = dis.readInt(); // 循环读取每一个图形信息 for (int i = 0; i < len; i++) { // 读取图形类型 byte type = dis.readByte(); if (type == 0) { // 如果是直线 // 读取直线的属性 int rgb = dis.readInt(); int x1 = dis.readInt(); int y1 = dis.readInt(); int x2 = dis.readInt(); int y2 = dis.readInt(); Color color = new Color(rgb); // 创建一个直线对象 Line line = new Line(x1, y1, x2, y2, color); // 将直线装入队列 shapes.add(line); } else if (type == 1) { // 如果是矩形 // 读取矩形的属性 int rgb = dis.readInt(); int x1 = dis.readInt(); int y1 = dis.readInt(); int x2 = dis.readInt(); int y2 = dis.readInt(); Color color = new Color(rgb); // 创建一个矩形对象 Rect rect = new Rect(x1, y1, x2, y2, color); // 将矩形装入队列 shapes.add(rect); } else if (type == 2) { // 如果是椭圆 // 读取椭圆的属性 int rgb = dis.readInt(); int x1 = dis.readInt(); int y1 = dis.readInt(); int x2 = dis.readInt(); int y2 = dis.readInt(); Color color = new Color(rgb); // 创建一个椭圆对象 Oval oval = new Oval(x1, y1, x2, y2, color); // 将椭圆装入队列 shapes.add(oval); } else if (type == 3) { // 读取多边形的颜色 int rgb = dis.readInt(); // 读取队列的长度 int size = dis.readInt(); // 创建队列,得到数组的内容 QueueImp<Integer> qi = new QueueImp<Integer>(); // 遍历数组,写入多边形的属性 for (int j = 0; j < size; j++) { int s = dis.readInt(); qi.add(s); } Color color = new Color(rgb); // 创建一个多边形对象 Polygon polygon = new Polygon(qi, color); // 将多边形装入队列 shapes.add(polygon); } } } catch (Exception ef) { ef.printStackTrace(); } return shapes; }
// 读取图像
QueueImp<Shape> getshapes = fu.openfile("D:\\jm.hu"); // 将getshapes中的图形装入到shapes中 for (int i = 0; i < getshapes.size(); i++) { shapes.add(getshapes.get(i)); } // 将队列中的形状取出来绘制 for (int i = 0; i < shapes.size(); i++) { // 根据下标取出一个形状对象 Shape sh = shapes.get(i); // 绘制 sh.draw(g); }
由于每种格式的文件都有固定的格式,而文件的后缀只是方便系统快速的找到打开此类文件的程序,故文件的类型不是由文件的后缀名决定的,而是文件固定的写入格式。我们用队列保存的这个文件格式,故也只能由我们自己的画图板才能打开。
二.仿BMP格式保存读取(以相位点的形式保存读取)
画板的画布,是由一个个相位点组成的,如果在画布上画了图形,那么一定会有一些相位点的颜色发生了改变,那么只要保存每一个相位点的颜色值就可以了。定义一个二维数组来实现,读取时,在根据二维数组的信息,把每个相位点画出来。
抓取一张图片,而图片的大小由画布的大小决定,比如我们在JPanel上加的画布,则图片的大小就是JPanel的大小,再将图片每一个相位点的颜色值保存到数组中去。
/** * 山寨BMP保存 * * @author * */ public class Save4Bit { static int width; static int height; static int[][] colors;// 用来保存每一个像素点颜色的数组 static java.awt.Robot robot; public static void savePic(JPanel panel) { try { robot = new java.awt.Robot(); } catch (Exception ef) { ef.printStackTrace(); } width = panel.getWidth(); height = panel.getHeight(); colors = new int[height][width]; // 得到panel左上角的点相对于屏幕的坐标 Point p = panel.getLocationOnScreen(); java.awt.Rectangle rect = new java.awt.Rectangle(p.x, p.y, panel.getWidth(), panel.getHeight()); // 从屏幕上抓取一张图片 java.awt.image.BufferedImage img = robot.createScreenCapture(rect); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { colors[i][j] = img.getRGB(j, i); //数组行列分别与画板的y坐标和x坐标对应 } } }
文件头:画面的大小 int(高)+int(宽)
文件数据:画面的每个点的颜色值 int
/* 图像的保存
* @author
*
*/
public class FileUtil {
/**
* 保存成文件
*
* @param path
* :保存路径
*/
public void saveFile(String path, int[][] color, JPanel jp) {
try {
// 创建一个文件输出流
java.io.FileOutputStream fos = new FileOutputStream(path);
// 将文件输出输出到缓冲流
java.io.BufferedOutputStream bos = new BufferedOutputStream(fos);
// 将缓冲流包装成Data的数据流
java.io.DataOutputStream dos = new java.io.DataOutputStream(bos);
// 首先保存画面的大小
dos.writeInt(jp.getHeight());
dos.writeInt(jp.getWidth());
// 遍历列队
for (int i = 0; i < jp.getHeight(); i++) {
for (int j = 0; j < jp.getWidth(); j++) {
dos.writeInt(color[i][j]);
}
}
dos.flush(); //强制刷新
fos.close();
} catch (Exception ef) {
ef.printStackTrace();
}
}
/** * 打开文件 * * @param path * @return */ public int[][] openFile(String path) { //创建一个二维数组,用来装入位点 int[][] color = null; try { // 创建文件输入流 java.io.FileInputStream fit = new FileInputStream(path); // 将文件输入流输入缓冲流 java.io.BufferedInputStream bis = new BufferedInputStream(fit); // 把缓冲流打包成Data输入流 java.io.DataInputStream dos = new DataInputStream(bis); // 先读取大小 int width = dos.readInt(); int height = dos.readInt(); //创建一个二维数组,用来保存相位点的值 color=new int[height][width]; // 把文件中的数据加入队列中 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { color[i][j] = dos.readInt(); } } } catch (Exception ef) { ef.printStackTrace(); } return color; }
重绘,从二维数组中读取颜色值,再画点
public void paint(Graphics g) { // 调用父类的方法来绘制窗体 super.paint(g); if (Save4Bit.colors != null) { for (int i = 0; i < Save4Bit.colors.length; i++) { for (int j = 0; j < Save4Bit.colors[i].length; j++) { int rgb = Save4Bit.colors[i][j]; if (panel.getBackground().getRGB() != rgb) { Color color = new Color(rgb); g.setColor(color); g.drawLine(i, j, i, j); } } } } }
通过两种方法的保存与读取,不难看出第一种方法写入文件和读取出来的过程都比较繁琐,而好处则是保存的文件在所画的图形个数不多的情况下,占用空间较少;第二种方法是每个每个相位点的保存,不难看出,无论什么情况下,文件的大小都是跟画板的大小相关的,好处是保存与读取的过程简单,但在画了很多图形情况下,画布的重载速度会很慢。