一、程序说明
- 制作窗体:将窗体分为功能按钮区与绘图区;功能按钮区添加功能按钮,实现各功能之间的切换;绘图区用来绘制图片,将功能区放在窗体右边,绘图区放在窗体正中间即可;
- 添加监听器:需要实现动作监听,鼠标监听;
- 创建方法类:用于存放各种绘图方法;用于存放监听器中的方法使监听器代码更加简洁;
- 获取图片对象:将图片转化为可操作的图片输入输出流。
二、方法说明
- 获取可操作的图片对象:读取一个图片对象,创建文件对象,将图片通过ImageIO.read(file)方法读取图片,通过BufferedImage对象保存读取到的图像对象;创建一个二维数组,获取Image的每一个像素点,通过Image对象调用getRGB方法获取像素点;通过对像素点的操作可以实现对图片的操作。
- 几种基础的图像处理:
原图:
①马赛克效果:
通过获取GRB值后画一个方块,实现对图像的打码处理;代码如下:
效果图:
public void Mosaic() {
for(int i=0;i<RGB2.length;i=i+10) {
for(int j=0;j<RGB2[0].length;j=j+10) {
bg.setColor(new Color(RGB2[i][j]));
bg.fillRect(i,j,10 , 10);
}
}
}
②油画效果:
与马赛克效果类似,这里通过画圆,通过不同大小的圆实现一种油画的朦胧感;
效果图:
public void OilPainting() {
Random random = new Random();
int size = random.nextInt(5)+5;
for(int i=0;i<RGB2.length;i+=3) {
for(int j=0;j<RGB2[i].length;j+=3) {
bg.setColor(new Color(RGB2[i][j]));
bg.fillOval(i, j, size, size);
}
}
}
③黑白化效果:
获取某一个像素点的RGB三个值,对三个值进行加权运算,或者取平均获得一个值,然后令该点的RGB三个值都为此值,即可获得黑白化效果;
计算式:value=0.3R+0.59G+0.11*B
效果图:
public void BlackAndWrite() {
int img[][] = new int[RGB2.length][RGB2[0].length];
for(int i=0;i<RGB2.length ;i++) {
for(int j=0;j<RGB2[i].length ;j++) {
Color c=new Color(RGB2[i][j]);
int r=(int)(0.3*c.getRed()+0.59*c.getGreen()+0.11*c.getBlue());
img[i][j]=((255 & 0xFF) << 24) |
((r & 0xFF) << 16) |
((r & 0xFF) << 8) |
((r & 0xFF) << 0);
bg.setColor(new Color(img[i][j]));
bg.fillOval(i,j,i,j);
}
}
}
④水墨画(轮廓化)
轮廓,是图像中的线条,处于轮廓附近的RGB值均相似,因此,我们可以通过设置一个值,使每一个像素点与它旁边的像素点进行对比,若,大于我们设置的值,则说明这两点RGB值不想近,可能不在一个轮廓上,可以将其RGB值设置成白色,若小于我们设置的值,则说明这两点RGB值相似,可将此点RGB值设为黑色,实现对轮廓的分析;
效果图:
⑤反向滤镜:
用255分别减去某一个像素点的R,G,B其中一个值,再将这个值作为新的R,G,B转化为RGB值即可;计算式:r=255-C.getred();g=255-C.get.getred();b=255-C.get.getred();
效果图:
public void Reversefilter() {
int img[][] = new int[RGB2.length][RGB2[0].length];
for(int i=0;i<RGB2.length ;i++) {
for(int j=0;j<RGB2[i].length ;j++) {
Color c=new Color(RGB2[i][j]);
int r=255-c.getRed();
int g=255-c.getRed()();
int b=255-c.getRed();
img[i][j]=((255 & 0xFF) << 24) |
((r & 0xFF) << 16) |
((g & 0xFF) << 8) |
((b & 0xFF) << 0);
bg.setColor(new Color(img[i][j]));
bg.fillOval(i,j,i,j);
}
}
}
⑥怀旧滤镜:
计算式:
double r=0.393*c.getRed()+0.769*c.getGreen()+0.189*c.getBlue();if(r>255)r=255;
double g=0.349*c.getRed()+0.686*c.getGreen()+0.168*c.getBlue();if(g>255)g=255;
double b=0.272*c.getRed()+0.534*c.getGreen()+0.131*c.getBlue();if(b>255)b=255;
效果图:
虽然自己在现实中画画比较丑,但是现在能有程序画了不是?
- 保存图像以及提高画图效率:
为什么我们绘图速度慢?
①像素点太多
②一个点一个点的绘制
③保存在数组中,再取出像素点
绘图的原理是什么?
每绘制一个点:设置画笔颜色-通知GPU-通知屏幕-画出这个点来;每一个像素点的绘制都需要经历这几个过程,大大减缓了绘制速度。
如何提高绘图速度?
先创建一个BufferedImage缓冲图片对象,先将所有像素点存入缓冲图片中,最后用drawImage的方法将这张图片画出来;在这个过程中,经历了:将像素点存入BufferedImage(在内存中进行操作,速度快)-用画笔画出这个缓冲图片-通知GPU-通知屏幕;在这个过程中,只经历了一次通知GPU和屏幕的操作,大大提高了绘图速率;
如何保存图片?
创建一个缓冲图像对象数组,用于存放操作后的图片,实现对图像的保存。
马赛克、油画如何保存?
创建一个空的BufferedImage对象,获取图片上的画笔,用这个画笔在缓冲区画出图像来,这个在缓冲区画出来的图片将自动保存在BufferedImage对象里,然后,将这个对象返回后保存进缓冲图像数组中即可实现对图像的保存。
代码实现:
public BufferedImage QuicklyDraw(int [][] imageArr,Graphics g) {
BufferedImage buffimg=new BufferedImage(imageArr.length ,imageArr[0].length ,BufferedImage.TYPE_INT_ARGB);
for(int i=0;i<imageArr.length ;i++) {
for(int j=0;j<imageArr[i].length ;j++) {
int value=imageArr[i][j];
buffimg.setRGB(i, j, value);
}
}
g.drawImage(buffimg,0,0,null);
return buffimg;
}
public void OilPainting() {
Random random = new Random();
int size = random.nextInt(5)+5;
BufferedImage buffimg=new BufferedImage(RGB2.length,RGB2[0].length,BufferedImage.TYPE_INT_ARGB);
Graphics bg = buffimg.getGraphics();
for(int i=0;i<RGB2.length;i+=3) {
for(int j=0;j<RGB2[i].length;j+=3) {
bg.setColor(new Color(RGB2[i][j]));
bg.fillOval(i, j, size, size);
}
}
buffimgs[index++]=this.QuickDraw1(buffimg,s);
}
4.鼠标操作与图层问题
实现鼠标拖动绘制马赛克:
第一步:获取当前鼠标拖动位置的RGB值;
第二步:绘制方块;
第三步:判断此时距离前一个方块距离;
代码实现
@Override
public void mouseDragged(MouseEvent e) {
BufferedImage buffimg=new BufferedImage(RGB2.length,RGB2[0].length,BufferedImage.TYPE_INT_ARGB);
Graphics bg = buffimg.getGraphics();
bg.drawImage(t, 0,0,null);
int a=0,b=0;
// TODO Auto-generated method stub
int x = e.getX();
int y = e.getY();
if(bitstr2.equals("拖动马赛克")){
Color color = null;
try {
color = getScreenPixel(x1,y1);
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
bg.setColor(color);
bg.fillRect(x1, y1, 20, 20);
a=x1;b=y1;
if(Math.abs(x-a)>20||Math.abs(y-b)>20) {
Color color1 = null;
try {
color1 = getScreenPixel(x,y);
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
bg.setColor(color1);
bg.fillRect(x, y, 20, 20);
a=x;b=y;
}
}
buffimgs[index++]=this.QuickDraw1(buffimg,s);
// 去当前显示的图像中取出来像素值 绘制方块
// 注意越界问题 -- 间隔
// 绘制的马赛克效果-存在图片上
}
public Color getScreenPixel(int x, int y) throws AWTException {
Robot rb = null;
rb = new Robot();
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension di = tk.getScreenSize();
Rectangle rec = new Rectangle(0, 0, di.width, di.height);
BufferedImage bi = rb.createScreenCapture(rec);
int pixelColor = bi.getRGB(x, y);
Color color=new Color(16777216 + pixelColor);
return color;
}
图层图像处理,拖动窗体后,图片大小不变,在鼠标拖动过程中可能产生越界问题,因此,我们需要实现图片随窗体大小变化的功能,并且实现撤回功能;我们可以发现,我们保存了每一份缓冲图片后,每一张图片是堆叠在一起的,而拖动窗体以及撤回,我们都需要对最上层的图片进行处理,撤回,则是将最上层的图片删除,与窗体适配则是需要最上面的图在窗体进行拖动时使图片随窗体变大或变小。这需要在重绘方法中实现。代码实现如下:
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
BufferedImage[] imgs=m.buffimgs;
if(m.index<=0) {
return;
}
g.drawImage(imgs[m.index-1], 0, 46,null);
// System.out.println(jpanel2.getHeight()+" "+jpanel2.getWidth());
BufferedImage bufferedImage=new BufferedImage(jpanel2.getWidth(),jpanel2.getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics bg=bufferedImage.getGraphics();
bg.drawImage(imgs[m.index-1],0,0,jpanel2.getWidth(),jpanel2.getHeight(),null);
imgs[m.index-1]=bufferedImage;
g.drawImage(bufferedImage, 0, 0,jpanel2.getWidth(),jpanel2.getHeight(),null);
}
三、总结
这个项目与之前做过的很相似,首先是窗体界面,项目分层结构,分区等操作,然后是图像获取与处理,之后就是程序不断完善的过程,功能完善,修改bug,这个项目远没有结束,仍然有许多可以完善与更改的地方。