之前写了美颜相机的简单系统,但是仅仅可以实现传入图片的地址,完成加载,灰度化,马赛克等等,本次的美颜相机不仅包含这些方法,还进行鼠标绘制,文件管理的操作等等
首先分为3个类进行操作,界面类ImageProUI,监听器类ImageListener,图形绘制类ImageUtils
在界面类中,我们还是常规的新建JFream界面,这一次不使用流式布局,用边框布局,在界面中个设置面板,将面板放在窗体的4个方向,设置宽度形成布局。
public void shouUI(){
//创建窗体
JFrame jf = new JFrame();
jf.setTitle("美颜相机.增强版V1");
jf.setSize(2000,1200);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
//设置边框布局
//创建面板
JPanel btnPanel = new JPanel();
JPanel imgPanel = new JPanel();
//设置颜色
btnPanel.setBackground(Color. DARK_GRAY);
imgPanel.setBackground(Color. BLACK);
//设置宽高
Dimension dim = new Dimension(0,100);
btnPanel.setPreferredSize(dim);
//初始化按钮
initBtnPanel(btnPanel);
//添加到窗体,东西南北指示方向
jf.add(btnPanel,BorderLayout.NORTH);
jf.add(imgPanel,BorderLayout.CENTER);
jf.setVisible(true);
}
public static void main(String[] args) {
ImageProUI imageProUI = new ImageProUI();
imageProUI.shouUI();
}
由于按钮的种类很多,我们采用String数组的形式来统一创建
public void initBtnPanel(JPanel btnPanel){
String[] btnText ={"打开","保存","原图","马赛克","灰度"};
for (int i = 0; i < btnText.length; i++) {
JButton btn = new JButton(btnText[i]);
btn.setBackground(Color. WHITE);
btnPanel.add(btn);
}
String[] btnText2 ={"画笔","直线","矩形","填充","截图","马赛克画笔"};
for (int i = 0; i < btnText.length; i++) {
JButton btn = new JButton(btnText2[i]);
btn.setBackground(Color. WHITE);
btnPanel.add(btn);
}
}
现在就基本完成了界面的设计(下方操作暂定未写)
接下来要对这几个按钮添加动作监听器,将按钮与功能联系起来
在监听器类中,添加动作监听器,用来监听点击按钮上的文字,对文字进行匹配,来实现对应操作
@Override
public void actionPerformed(ActionEvent e) {
String ac=e.getActionCommand();
System.out.println("ac:"+ac);
if (ac.equals("打开")){
}
} else if (ac.equals("保存")) {
} else if (ac.equals("原图")) {
} else if (ac.equals("灰度")) {
} else if (ac.equals("马赛克")) {
}
}
处理打开按钮,打开时要获取图片的绝对路径,让File类能检索到
这样的话就可以使用文件选择器,在选中图片点击确认时,获取当前选中的文件的路径(String)
之后传入文件处理类中
在文件选择器中,所有文件都在其中,需要对文件选择器添加文件名过滤器,只出现图片
if (ac.equals("打开")){
//打开是为了获取图像的绝对地址,传递给图片处理类
JFileChooser jfc = new JFileChooser();
//文件名过滤器
FileNameExtensionFilter filter=new FileNameExtensionFilter
("JPG &PNG","jpg","png");
jfc.setFileFilter(filter);
jfc.showOpenDialog(null);//位置居中
int state =jfc.showOpenDialog(null);
if (state == JFileChooser.APPROVE_OPTION){//点击了确认按钮
String path=jfc.getSelectedFile().getAbsolutePath();//根据选择的文件,获取绝对路径
imageUtils.loadImage(path);
}
}
传入文件处理类中
loadImage操作中使用一个try,catch,将文件的存放在一个二维数组中取出图片的长宽,来定制数组的长度,在对每一个像素取出RGB值,存入
//属性
int[][] imgArr;
int w;
int h;
Graphics gra;//从绘制面板上传过来
//加载照片
public void loadImage(String path){
File file = new File(path);
try {
BufferedImage image= ImageIO.read(file);
w = image.getWidth();
h = image.getHeight();
//创建一个空的数组,来存储图片像素值
imgArr = new int[w][h];
//分别存入像素值
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
imgArr[i][j] = image.getRGB(i, j);//获取RGB值,存入
}
}
System.out.println("加载图片完成");
}catch (IOException e){
throw new RuntimeException(e);
}
}
现在加载图片就实现了,接下来就是绘制原图
为了加速这个原图的绘制过程,可以先创建一个空的图片,获取这个图片的渲染器,再在循环完成后一次性会在窗体上
public void drawImage(){
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
//获取这个图片对象的图像渲染器
Graphics imgGra = image.getGraphics();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int pixNum = imgArr[i][j];
Color color = new Color(pixNum);
imgGra.setColor(color);
imgGra.fillRect(i, j, 1, 1);
}
}
//循环结束一次性的绘制在窗体上
gra.drawImage(image, 0, 0, null);
}
(回到监听器类)
ImageUtils imageUtils = new ImageUtils();
else if (ac.equals("原图")) {
imageUtils.drawImage();
}
为了能生成在界面类中的窗体上,需要将界面类中创建的Graphics传递到图片处理类
Graphics gra = imgPanel.getGraphics();
imgl.imageUtils.gra=gra;
接下来是常见的灰度处理方法
public void drawGray(){
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
//获取这个图片对象的图像渲染器
Graphics imgGra = image.getGraphics();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int pixNum = imgArr[i][j];
Color color = new Color(pixNum);
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int gray = (red + green + blue)/3;
imgGra.setColor(new Color(gray, gray, gray));
imgGra.drawRect(i, j, 1, 1);
}
}
gra.drawImage(image, 0, 0, null);
}
现在的图片在加载时是直接创建在左上角,为了能在出现时就在屏幕中间,需要再写一个类ImagePanel,这个类继承JPanel,以绘制一个矩形为例
import javax.swing.*;
import java.awt.*;
public class ImagePanel extends JPanel {
//实现绘制(函数paint)绘制图片
@Override
public void paint(Graphics g) {
super.paint(g);
int w=800,h=600;
//计算居中位置
int x=this.getWidth()/2-w/2;
int y=this.getHeight()/2-h/2;
g.setColor(Color.white);
g.fillRect(x,y,w,h);
}
}
将图像绘制类中的方法范围值改为BufferedImage,返回图片
public BufferedImage drawImage(){
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
...
return image;
}
再新建一个数组,用来存放这些图片,将返回的图片都存入数组中,再在ImagePanel中调用
else if (ac.equals("原图")) {
BufferedImage img = imageUtils.drawImage();
imgList.add(img);
} else if (ac.equals("灰度")) {
BufferedImage img = imageUtils.drawGray();
imgList.add(img);
}
传进来
ImageListener imgl = new ImageListener();
public void addImageL(ImageListener l) {
imgl = l;
}
//实现绘制(函数paint)绘制图片
@Override
public void paint(Graphics g) {
super.paint(g);
if (imgl.imgList.isEmpty()) {
return;
}
//从数组中获取最后一张图片
int lastImageIndex= imgl.imgList.size()-1;
BufferedImage img = imgl.imgList.get(lastImageIndex);
int w=img.getWidth();
int h=img.getHeight();
//计算居中位置
int x=this.getWidth()/2-w/2;
int y=this.getHeight()/2-h/2;
g.drawImage(img,x,y,null);
}
为了能在打开图片时可以直接刷新页面,要在监听器类中新建一个面板,实现刷新
//面板
ImagePanel imagePanel;
//进行面板刷新
imagePanel.repaint();
在UI类中
imgl.imagePanel=imgPanel;//在点击按钮时刷新面板