Java实现图片渲染((拖动)马赛克黑白照油画风格等)
Posted GuochaoHN
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现图片渲染((拖动)马赛克黑白照油画风格等)相关的知识,希望对你有一定的参考价值。
Java实现图片渲染
方法改进
紧接上一篇实现图片渲染链接: 图片渲染(图像编码原理).
可以发现:只前在绘画图片的时候速度非常的慢。那是因为每次只要一获得像素就立即将其绘画出来。
g.setColor(color);
g.fillRect(i, j,1, 1)
在内存中执行代码后,会向GPU请求刷新屏幕,一张图片有成千上万个像素,那么屏幕就会连续刷新很多次,速度非常慢。例如,如果电脑屏幕的刷新率是60赫兹的话,意味着一秒钟屏幕会刷新60次,放到这里来讲的话,一秒钟最多显示60个像素,对于分辨率极高的图片,要加载出来,可想而知等的时间也就会越久。
所以,就要用一种方法一次性将所有的像素画出来。BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中(BufferedImage生成的图片在内存里有一个图像缓冲区,利用这个缓冲区我们可以很方便地操作这个图片)
在请求刷新屏幕之前,现将所有的像素设置完毕,然后一次性将缓冲区的像素全部画出来(刷新一次屏幕)。有两种方法来设置缓冲区。
第一:直接设置缓冲区相应位置像素的RGB值。
实现如下:
//绘画原图改进方法
public BufferedImage paint1(Graphics g,int[][] arrpixel)
{
BufferedImage buffimage=new BufferedImage(arrpixel.length, arrpixel[0].length,BufferedImage.TYPE_INT_RGB);
for(int i=0;i<arrpixel.length;i++)
{
for(int j=0;j<arrpixel[0].length;j++)
{
int value=arrpixel[i][j];
buffimage.setRGB(i, j, value);//一次性将所有像素存储完成
}
}
g.drawImage(buffimage, 0, 0, null);//一次性画完
return buffimage;
}
第二:通过缓冲区获得画笔,将画笔赋相应的RGB值,然后通过fillRect方法来填充矩形小像素。这种方法还可以方便我们实现渲染(如后面的马赛克等)
实现如下:
//原图改进方法
public BufferedImage paint1(Graphics g,int[][] arrpixel)
{
BufferedImage buffimage=new BufferedImage(arrpixel.length, arrpixel[0].length,BufferedImage.TYPE_INT_RGB);
Graphics ng=buffimage.getGraphics();
for(int i=0;i<arrpixel.length;i++)
{
for(int j=0;j<arrpixel[0].length;j++)
{
int value=arrpixel[i][j];
if(i==0&&j==0)
{ System.out.println("arrpixel:"+value);}
ng.setColor(new Color(value));
ng.fillRect(i, j, 1, 1);
}
}
g.drawImage(buffimage, 0, 0, null);
}
之后就可以把其他的渲染方法逐个改进。以下是图片渲染程序的源代码。
ImageUI
public class ImageUI extends JPanel {
String []bt= {"原图","方形马赛克","灰度","二值化","轮廓检测","油画绘制","拖动原图马赛克","拖动彩色马赛克","截图","放大","缩小","撤回上一步","清空图片"};
ImageListener listener=new ImageListener();//什么时候static final
JFrame nj=new JFrame();//截图确认窗口
public void initUI()
{
JFrame jf=new JFrame();
jf.setSize(1500,780);
jf.setTitle("图片处理");
jf.setDefaultCloseOperation(3);
jf.setLocationRelativeTo(null);
//按钮功能区面板
JPanel jp1=new JPanel();
jp1.setBackground(new Color(0xafeeee));
Dimension dm=new Dimension(200,780);
jp1.setPreferredSize(dm);
Dimension btdm=new Dimension(190,40);
for(int i=0;i<bt.length;i++)
{
JButton jb =new JButton(bt[i]);
jb.setPreferredSize(btdm);
jb.setBackground(Color.white);
Font font= new Font("黑体",Font.BOLD,20);
jb.setFont(font);
jb.setForeground(new Color(0x000080));
jb.setBackground(new Color(0xf0fff0));
jb.addActionListener(listener);
jp1.add(jb);
}
//绘图区jframe默认边框布局
this.setBackground(new Color(0xffffe0));
jf.add(jp1,BorderLayout.EAST);
jf.add(this,BorderLayout.CENTER);
this.addMouseListener(listener);
this.addMouseMotionListener(listener);
this.addKeyListener(listener);
jf.setVisible(true);
//从jp2 获得画笔
Graphics g=jf.getRootPane().getGraphics();
listener.setgraphics(g);
listener.setjpanel(this);
nj.setTitle("提示");
nj.setSize(300, 150);
//nj.setDefaultCloseOperation(3);
//这里弹出窗口点叉不能设置关闭程序,否则主界面也会跟着关闭
nj.setLocationRelativeTo(jf);
nj.setResizable(false);
JLabel txt =new JLabel("是否确认截图",JLabel.CENTER);
Font fnt= new Font("黑体",Font.BOLD,30);
txt.setFont(fnt);
nj.setLayout(new FlowLayout());
nj.add(txt);
Font fnt2=new Font("黑体", Font.CENTER_BASELINE, 15);
JButton bt =new JButton("确认");
bt.addActionListener(listener);
bt.setPreferredSize(new Dimension(100, 30));
bt.setFont(fnt2);
nj.add(bt);
JButton bt2=new JButton("取消");
bt2.addActionListener(listener);
bt2.setPreferredSize(new Dimension(100, 30));
bt2.setFont(fnt2);
nj.add(bt2);
nj.addWindowListener(listener);
}
@Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
System.out.println("fresh"+!listener.freshbuff.isEmpty());
if( !listener.freshbuff.isEmpty())
{
BufferedImage top=( BufferedImage)listener.freshbuff.peek();
g.drawImage(top,0,0,top.getWidth(),top.getHeight() ,null);
System.out.println("调用paint");
}
}
public void clear(Graphics g)
{
super.paint(g);
}
public static void main(String[] args) {
new ImageUI().initUI();
}
}
ImageListener
#前提说明:
①如果一个类需要的接口太多,且接口中的方法又有很
多,且大部分无用,而这个类又不需要继承,那么就可以考虑做一个工具类——实现所有的接口。 然后用所需的类继承这个工具类,选择性的重写方法。
这里监听器类为ImageListener,继承监听器工具类ListenerUtils ,选择性重写接口方法:
public class ImageListener extends ListenerUtils
{
//选择性重写
... ...
}
public class ListenerUtils implements ActionListener,MouseListener,MouseMotionListener
,KeyListener,ChangeListener,WindowListener{
@Override
public void stateChanged(ChangeEvent e) {
// TODO Auto-generated method stub
}
... ... ... ... ..
②为了更好得实现返回上一步功能,定义了两个BufferedImage栈,考虑到,窗口改变执行的peek()操作,返回上一步执行的是pop()操作,所以用一个栈不容易将功能正确实现。(读者也可以自己定义不同的数据类型存储BufferedImage,可能会更加方便)
刷新栈:
窗口改变大小实现图片重绘。
public Stack<BufferedImage> freshbuff=new Stack<BufferedImage>();
操作栈:
记录每一次的执行结果,以实现返回上一步功能。
public Stack<BufferedImage> buffstack =new Stack<BufferedImage>();
具体代码如下:
public class ImageListener extends ListenerUtils {
int[][] imgarr;
String btn="";
String path="E:\\\\java\\\\eclipse\\\\eclipse javaee\\\\workspace\\\\Yu java\\\\66.jpg";
private Graphics g;
ImageUtils imageff;
ImageUI jp;
public Stack<BufferedImage> buffstack =new Stack<BufferedImage>();
public Stack<BufferedImage> freshbuff=new Stack<BufferedImage>();
int index;
int p_x,p_y;
int r_x,r_y;
int d_x,d_y;
public void setgraphics(Graphics g)
{
this.g=g;
}
public void setjpanel(ImageUI jp)
{
this.jp=jp;
}
//初始化监听器
ImageListener()
{
imageff=new ImageUtils();
imgarr=imageff.getImagePixel(path);
System.out.println("初始化成功"+imgarr.length);
}
@Override
public void windowClosing(WindowEvent e) {
// TODO Auto-generated method stub
super.windowClosed(e);
jp.paint(g);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
System.out.println("当前"+btn);
//System.out.println("调用actionperformed");
btn=e.getActionCommand();
if(btn.equals("原图"))
{
jp.clear(g);
BufferedImage img= imageff.paint1(g,imgarr);
buffstack.push(img);
freshbuff.push(img);
System.out.println("栈顶"+buffstack.peek().getWidth());
System.out.println("栈内元素数"+buffstack.size());
}
else if(btn.equals("方形马赛克"))
{
jp.clear(g);
imageff.paint2(g, freshbuff,buffstack);
}
else if(btn.equals("灰度"))
{
jp.clear(g);
imageff.paint3(g, freshbuff,buffstack);
}
else if(btn.equals("二值化"))
{
jp.clear(g);
imageff.paint4(g, freshbuff,buffstack);
}
else if(btn.equals("轮廓检测"))
{
jp.clear(g);
imageff.paint5(g, freshbuff,buffstack);
}
else if(btn.equals("油画绘制"))
{
jp.clear(g);
imageff.paint6(g, freshbuff,buffstack);
}
else if(btn.equals("放大"))
{
jp.clear(g);
imageff.piant7(g, freshbuff,buffstack);
jp.paint(g);
}
else if(btn.equals("缩小"))
{
jp.clear(g);
imageff.paint8(g,freshbuff,buffstack );
jp.paint(g);
}
else if(btn.equals("清空图片"))
{
jp.clear(g);
imageff.withdraw(g,jp,buffstack,freshbuff);
}
else if (btn.equals("撤回上一步"))
{
jp.clear(g);
freshbuff.push(imageff.backward(buffstack,g));
}
else if(btn.equals("确认"))
{
jp.clear(g);
imageff.paint12(g, freshbuff, buffstack,p_x,p_y,r_x,r_y);
System.out.println("截图完成");
jp.nj.dispose();
}
else if(btn.equals("取消"))
{
jp.nj.dispose();
jp.paint(g);
}
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
super.mousePressed(e);
p_x=e.getX();
p_y=e.getY();
d_x=e.getX();
d_y=e.getY();
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
super.mouseReleased(e);
r_x=e.getX();
r_y=e.getY();
if(btn.equals("截图"以上是关于Java实现图片渲染((拖动)马赛克黑白照油画风格等)的主要内容,如果未能解决你的问题,请参考以下文章
js进行数字图像处理:亮度对比度马赛克画笔放大缩小镜像贴纸旋转颜色值显示
ProcessingJoy —— 多种风格的字符马赛克JAVA