(文件数据的写入顺序要与读取顺序一致)
一.用自定义的队列保存图像与读取图像
由于在画图板上每画一种图形,就相当于在自定义的队列中装入此图形,那么在保存画图板图形时,就只需从自定义队列中获取每个图形的基本属性,如颜、点的坐标,图形的种类,然后将这些基本信息写入文件,存储到硬盘。不过先要写入图形的个数,方便打开时获得队列的长度。
打开保存的图形时,只需要定义一个自定义队列,在从保存的文件中读取图形的中种类,根据种类不同,分别读取图形的属性,颜色、坐标,创建具有这些属性的图形队形,装入到队列中去。然后再根据队列中的装入的图形,通过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);
}
}
}
}
}
通过两种方法的保存与读取,不难看出第一种方法写入文件和读取出来的过程都比较繁琐,而好处则是保存的文件在所画的图形个数不多的情况下,占用空间较少;第二种方法是每个每个相位点的保存,不难看出,无论什么情况下,文件的大小都是跟画板的大小相关的,好处是保存与读取的过程简单,但在画了很多图形情况下,画布的重载速度会很慢。