目录
一.界面概览?
二.功能的实现完善与一些技术细节
(1)ListenerImp类
(2)DrawPanel类
(3)解决图像加载缓慢的问题
(4)关于buffimglist
(5)录制功能:
(6)回放功能
(7)撤回与清空功能
三.Thx
一.界面概览?
这次我们用Java来实现图像处理,先给hxdm上成果:
?如图窗体右方(East)面板按钮的所示的功能是我们的图像处理小项目的主要内容。
关于图像处理的具体方法可以参考我的上一篇博客:
图像处理Image Processing(一)
二.功能的实现完善与一些技术细节
(1)ListenerImp类
? 利用ListenerImp类来实现ActionListener等接口(并不需要实现,真正意义上的实现在ImageListener类中进行),然后再由ImageListener类来继承,来实现我们需要的方法;
这样做有如下几个好处:
- 如果需要实现的监听器过多,类中的方法太多不便于阅读,况且并不是每个方法我们都要实现;
- 方便进行方法的添加;
- 增加了代码的可读性,美观就完事
public class ListenerImp implements ActionListener, MouseListener, MouseMotionListener, ChangeListener, KeyListener {
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void stateChanged(ChangeEvent e) {
}
}
(2)DrawPanel类
DrawPanel类是用来重写paint方法实现重绘的类;
? ?其中buffimglist是保存每步操作图像的ArrayList<BufferedImage>,需要注意的是指针悬空问题和绘制图像时要取buffimglist中栈顶元素。(为什么是直接drawImage,而不是打印保存有像素点的二维数组,后文会提到);
public class DrawPanel extends JPanel {
ArrayList<BufferedImage> buffimglist = new ArrayList<>();
public void setBuffimglist(ArrayList<BufferedImage> buffimglist) {
this.buffimglist = buffimglist;
}
public void paint(Graphics gr) {
super.paint(gr);
System.out.println("paint");
//防止指针悬空
if(buffimglist==null ){
return;
}
if(buffimglist.size()==0){
return;
}
//绘制最后存储的图像
gr.drawImage(buffimglist.get(buffimglist.size()-1), 0, 0, null);
}
}
(3)解决图像加载缓慢的问题
? ?尝试了鄙人上一篇博客中的方法的hxdm会发现一个问题:图像在窗体的打印十分缓慢。
? ?这是因为IO与底层GPU速度不匹配造成的,此时我们进行的是一个一个像素的数据传输,导致图片加载缓慢。解决方法:
- 将整张图片的像素点存储起来;
- 声明一个BufferedImage对象来存储每张图像的像素点,此操作为纯内存操作;
- 整体绘制:在窗体上直接绘制BufferedImage(drawImage方法);
用绘制原图的方法来给大家举例,(其他绘制方法同理):‘
//绘制原图
public BufferedImage drawImage(int[][] imgArr) {
//声明一个空的BufferedImage
BufferedImage buffimg = new BufferedImage(imgArr.length, imgArr[0].length, BufferedImage.TYPE_INT_ARGB);
//绘制过程与屏幕刷新无关,纯内存操作
Graphics bfg = buffimg.getGraphics();
for(int i = 0; i < imgArr.length; i++) {
for(int j = 0; j < imgArr[0].length; j++) {
int rgb = imgArr[i][j];
Color color = new Color(rgb);
//方法一:把像素保存在缓冲区中
// buffimg.setRGB(i, j, rgb);
//方法二:先在内存中绘制
bfg.setColor(color);
bfg.fillRect(i,j,1,1);
}
}
//窗体上绘制
//因为imageListener中调用了repaint,按下按钮后会绘制,所以这里不需要绘制
// gr.drawImage(buffimg, 0, 0, null);
return buffimg;
}
- 我们并不需要在 drawImage方法中实现窗体绘制的功能,因为我们在imageListener中调用了repaint方法,按下按钮后会绘制,所以这里不需要绘制。这里的绘制方法都只需要存储图像在buffimg中。
- 修改后的返回值类型为为BufferedImage,原因如下:
if(str.equals("原图")) {
buffimg = imageUtils.drawImage(imgArr);
//加入队列
buffimglist.add(buffimg);
}
这样我们可以将每次存储在内存中的图像加入buffimglist中,便于我们的重绘、录制、回放、清空功能的实现。
(4)关于buffimglist
buffimglist的作用是保存我们每一步操作产生的图像,便于我们的重绘、录制、回放、清空功能的实现。
ArrayList<BufferedImage> buffimglist = new ArrayList<>();
(5)录制功能:
录制功能:因为我们的每步操作都保存在了buffimglist中,所以我们只需要保存录制的起始位置即可还原操作顺序;
//记录录制的开始与结束位置
public int start = 0;
public int end = 0;
//录制
public void recordImage() {
start = this.buffimglist.size() - 1;
System.out.println("开始录制:" + start);
}
(6)回放功能
回放功能:有了起始位置,回放就是一个遍历buffimglist的过程;
//回放
public void playback() {
end = this.buffimglist.size();
for(int i = start; i < end; i++) {
gr.drawImage(this.buffimglist.get(i), 0, 0, null);
//相邻图片间隔1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(7)撤回与清空功能
//撤回
public void withDraw() {
if(this.buffimglist.size() > 1) {
this.buffimglist.remove(this.buffimglist.size()-1);
} else {
System.out.println("不能撤回!!");
}
}
//清空
public void clearScreen() {
this.buffimglist.clear();
}
三.Thx
谢谢你这么好看还看完了我的博客!
|