零、前一天的练习
源代码如下:
package sy0118;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class DrawMouse implements MouseListener, ActionListener {
public String message;
public int cnt; //点的个数
public int[] x = new int[105], y = new int[105];
public Graphics g;
public void actionPerformed(ActionEvent e){
message = e.getActionCommand();
//判断是否进行新动作
if (message.equals("红色")){
g.setColor(Color.RED);
} else if (message.equals("黑色")){
g.setColor(Color.BLACK);
} else {
cnt = 0;
}
}
public void mouseClicked(MouseEvent e){
if (message.equals("三角形")){
g.drawLine(x[cnt-3],y[cnt-3],x[cnt],y[cnt]);
g.drawLine(x[cnt-2],y[cnt-2],x[cnt],y[cnt]);
cnt = 0;
}
if (message.equals("多边形")) {
if (e.getClickCount() == 2) {
g.drawLine(x[1], y[1], x[cnt], y[cnt]);
cnt = 0;
} else {
g.drawLine(x[cnt - 2], y[cnt - 2], x[cnt], y[cnt]);
}
}
}
public void mousePressed(MouseEvent e){
x[++cnt] = e.getX();
y[cnt] = e.getY();
}
public void mouseReleased(MouseEvent e){
x[++cnt] = e.getX();
y[cnt] = e.getY();
if ((message.equals("直线")) || (message.equals("三角形")) || (message.equals("多边形"))){
g.drawLine(x[cnt-1],y[cnt-1],x[cnt],y[cnt]);
} else if (message.equals("矩形")){
g.drawRect(Math.min(x[cnt-1],x[cnt]),Math.min(y[cnt-1],y[cnt]),Math.abs(x[cnt-1]-x[cnt]),Math.abs(y[cnt-1]-y[cnt]));
}
}
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
}
使用了数组来存储获取鼠标的位置,并在合适的时间对已获取的点进行重置(很简单,应该都能看懂)
package sy0118;
import javax.swing.*;
import java.awt.*;
public class DrawUI {
public void showUI(){
JFrame jf = new JFrame();
jf.setTitle("画图工具");
jf.setSize(1000,1000);
jf.setLocationRelativeTo(null);
jf.setDefaultCloseOperation(3);
FlowLayout flow = new FlowLayout();
jf.setLayout(flow);
//功能
JButton Line = new JButton("直线");
JButton Rectangle = new JButton("矩形");
JButton Triangle = new JButton("三角形");
JButton Polygon = new JButton("多边形");
JButton Red = new JButton("红色");
JButton Black = new JButton("黑色");
jf.add(Line); jf.add(Rectangle); jf.add(Triangle); jf.add(Polygon); jf.add(Red); jf.add(Black);
jf.setVisible(true);
//监听器
Graphics g = jf.getGraphics();
DrawMouse mouse = new DrawMouse();
jf.addMouseListener(mouse);
Line.addActionListener(mouse);
Rectangle.addActionListener(mouse);
Triangle.addActionListener(mouse);
Polygon.addActionListener(mouse);
Red.addActionListener(mouse);
Black.addActionListener(mouse);
mouse.g = g;
}
public static void main(String[] args) {
DrawUI ui = new DrawUI();
ui.showUI();
}
}
总的来说还是比较简单的,变化不多,只需要稍微注意一下怎么处理多边形的点、边和闭合情况即可(同样的,看不懂可以私信噢!)
一、今日内容
因为每天要做的事情稍微有点多(绝对不是因为内容太多了),后面每天的内容将只保留这部分和源代码部分 :)
前几天初步对 java 有了个初步的了解,那么今天正式开始我们的第一个项目——多功能美颜相机(这会是一个多功能的结合体,需要花费至少十多天来完成)
那么今天一开始,我们先来写写最基础的功能及框架
(怕你们弄晕,我先把全部代码贴出来,直接跳过就行)
package 多功能美颜相机.初版;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class PixelListener implements ActionListener, MouseMotionListener {
public Graphics g;
public int w,h;
public String message;
public int x,y;
public int[][] PixelArr = new int[0][];
public int[][] Staging = new int[0][];
public int[][] flag = new int[1505][1505];
@Override
public void actionPerformed(ActionEvent e) {
String path = "图片地址";
message = e.getActionCommand();
try {
Staging = getImagePixel(path);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
if ((message.equals("灰度")) || (message.equals("原图"))) {
PixelArr = Staging;
DrawPixel();
}
}
public void DrawPixel(){
BufferedImage buffer = new BufferedImage(w+900,h+900,BufferedImage.TYPE_INT_ARGB);
if (message.equals("马赛克")){
//int pixel = PixelArr[(y-100)/8*8][x/8*8];
Graphics bg = buffer.getGraphics();
int RED=0, GREEN=0, BLUE=0;
for (int i=x/8*8;i<(x/8+1)*8;i++){
for (int j=y/8*8;j<(y/8+1)*8;j++){
flag[i][j] = 1;
int Pixel = PixelArr[j-100][i];
RED += Pixel >> 16 & 0xFF;
GREEN += Pixel >> 8 & 0xFF;
BLUE += Pixel & 0xFF;
}
}
Color color = new Color(RED/64, GREEN/64, BLUE/64);
bg.setColor(color);
bg.fillRect(x/8*8,y/8*8,8,8);
//buffer.setRGB(x/8*8,y/8*8);
} else {
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
flag[i][j] = 0;
int pixel = PixelArr[i][j];
int red = pixel >> 16 & 0xFF;
int green = pixel >> 8 & 0xFF;
int blue = pixel & 0xFF;
int A = pixel >> 24 & 0xFF;
//Color color = new Color(red, green, blue);
if (message.equals("灰度")) {
int average = (int) (0.3 * red + 0.59 * green + 0.11 * blue);
//color = new Color(average, average, average);
pixel = A << 24 | average << 16 | average << 8 | average;
PixelArr[i][j] = pixel;
}
//g.setColor(color);
//g.fillRect(j, i + 100, 1, 1);
buffer.setRGB(j,i+100,pixel);
}
}
}
g.drawImage(buffer,0,0,null);
}
public int[][] getImagePixel(String path) throws IOException {
File file = new File(path);
BufferedImage bufferedImage = null;
try {
bufferedImage = ImageIO.read(file);
} catch (IOException e){
throw new RuntimeException(e);
}
w = bufferedImage.getWidth();
h = bufferedImage.getHeight();
int[][] PixelArr = new int[h][w];
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
PixelArr[i][j] = bufferedImage.getRGB(j,i);
}
}
return PixelArr;
}
@Override
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
if (message.equals("马赛克")){
if (flag[x][y] == 0){
DrawPixel();
}
}
}
@Override
public void mouseMoved(MouseEvent e) {
//x = e.getX();
//y = e.getY();
}
}
(还有主函数)
package 多功能美颜相机.初版;
import javax.swing.*;
import java.awt.*;
import java.util.PropertyPermission;
public class PixelUI {
public void showUI(){
JFrame jf = new JFrame();
jf.setLayout(new FlowLayout());
jf.setSize(900,900);
jf.setDefaultCloseOperation(3);
jf.setLocationRelativeTo(null);
jf.setTitle("图像处理");
JButton Origin = new JButton("原图");
JButton Grayscale = new JButton("灰度");
JButton Mosaic = new JButton("马赛克");
jf.add(Origin); jf.add(Grayscale); jf.add(Mosaic);
PixelListener listener = new PixelListener();
Origin.addActionListener(listener);
Grayscale.addActionListener(listener);
Mosaic.addActionListener(listener);
jf.addMouseMotionListener(listener);
jf.setVisible(true);
Graphics g = jf.getGraphics();
listener.g = g;
}
public static void main(String[] args) {
PixelUI pixelUI = new PixelUI();
pixelUI.showUI();
}
}
主函数还是非常简单的,先把窗体和按钮创建完,并添加画笔,完成原图、灰度和马赛克三种情况的绘画。
先讲原图的绘画,先拍这部分的源代码:
BufferedImage buffer = new BufferedImage(w+900,h+900,BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
flag[i][j] = 0;
int pixel = PixelArr[i][j];
//int red = pixel >> 16 & 0xFF;
//int green = pixel >> 8 & 0xFF;
//int blue = pixel & 0xFF;
//int A = pixel >> 24 & 0xFF;
//Color color = new Color(red, green, blue);
//g.setColor(color);
//g.fillRect(j, i + 100, 1, 1);
buffer.setRGB(j,i+100,pixel);
}
}
g.drawImage(buffer,0,0,null);
原理是把图片里每个像素(pixel)的ARGB(或RGB)值(PixelArr)取出来,并存储(setRGB)给缓冲图片(BufferedImage)后,在循环结束一起输出(g.drawImage)。这里有两种做法,注释掉的是另一种做法,不需要使用缓冲图片,虽然绘制慢,但是助于理解什么是ARGB值。
这里有个问题,我们该怎么取出图片中的像素值(ARGB)并存储到数组 PixelArr 呢?同样的,先贴代码:
public int[][] getImagePixel(String path) throws IOException {
File file = new File(path);
BufferedImage bufferedImage = null;
try {
bufferedImage = ImageIO.read(file);
} catch (IOException e){
throw new RuntimeException(e);
}
w = bufferedImage.getWidth();
h = bufferedImage.getHeight();
int[][] PixelArr = new int[h][w];
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
PixelArr[i][j] = bufferedImage.getRGB(j,i);
}
}
return PixelArr;
}
可以看到,当我们有图片的地址(path)后,通过文件(File)读入(ImageIO.read)并存储到缓冲图片区(BufferedImage)后,通过 getRGB 值赋值给对应位置的 PixelArr 即可!(千万注意这里的位置坐标!还有包括后面对图片处理时的位置坐标)
这样一顿操作之后,我们就可以画出原图啦!然后是灰度的算法,其实非常简单,只需要特判一波,并采用灰度算法均衡 RED,GREEN,BLUE 值即可!
if (message.equals("灰度")) {
int average = (int) (0.3 * red + 0.59 * green + 0.11 * blue);
//color = new Color(average, average, average);
pixel = A << 24 | average << 16 | average << 8 | average;
PixelArr[i][j] = pixel;
}
同样的是两种方法,注释掉的是不用缓冲图片的方法(但是画的慢)。
马赛克其实是同理,只需要把一个区块(比如10*10)内的全部像素值(其实就是颜色)变成同一种颜色即可!
if (message.equals("马赛克")){
//int pixel = PixelArr[(y-100)/8*8][x/8*8];
Graphics bg = buffer.getGraphics();
int RED=0, GREEN=0, BLUE=0;
for (int i=x/8*8;i<(x/8+1)*8;i++){
for (int j=y/8*8;j<(y/8+1)*8;j++){
flag[i][j] = 1;
int Pixel = PixelArr[j-100][i];
RED += Pixel >> 16 & 0xFF;
GREEN += Pixel >> 8 & 0xFF;
BLUE += Pixel & 0xFF;
}
}
Color color = new Color(RED/64, GREEN/64, BLUE/64);
bg.setColor(color);
bg.fillRect(x/8*8,y/8*8,8,8);
//buffer.setRGB(x/8*8,y/8*8);
}
同样的是两种方法(但是这里我没有完全用注释区分开来),我选用的是 8*8 的区块全部变成一个颜色。为了使得画面颜色更加平滑,我将这个颜色设置为区块内所有像素(也就是64个像素)颜色的平均值,然后 fillRect 画矩形即可!
另外,我们可以让马赛克的绘制变成通过鼠标拖动来控制,初步实现截屏里面的马赛克功能。这就需要用到上一天里获取鼠标位置的方法了!
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
if (message.equals("马赛克")){
if (flag[x][y] == 0){
DrawPixel();
}
}
}
这里我使用了标记数组 flag 来判断一个像素之前是否被打过马赛克,防止重复打出不同颜色的马赛克。
那么事情到这里就已经完成啦,支持画出原图、画出灰度图、在原图或者灰度图上用鼠标进行马赛克,所有的源码在最上面。
接下来就是一些问题或者后面几天需要改进的地方:
1. 打马赛克时不连贯,鼠标移动快的时候中间会断开
2. 代码不太美观
3. 添加新的画面算法(如二值化、底片、浮雕等)
4. 添加滑动条组件来控制各种数据(如马赛克画笔大小调整、图片大小调整、RGB值调整等等)
5. 对比微信截图里的马赛克,我们可以发现:它的马赛克宽度固定,但长度末端位于鼠标位置(说白了就是拖到哪打到哪),而我们是一块一块涂上去的,有点像量子态一样,怪怪的文章来源:https://www.toymoban.com/news/detail-837102.html
6. 添加新功能,如高清修复等等文章来源地址https://www.toymoban.com/news/detail-837102.html
到了这里,关于从零开始面向对象编程 Java & Day 5,6 (美颜相机 Day 1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!