How to Draw a Line on a Bufferedimage in Java
The most practical method is at the end of the response after the Update
What happens is that you save all the coordinates in the list, and each time you finish and start a new line, paintComponent()
redraws the screen using all the stored coordinates, including the final coordinate of a line and the initial coordinate of another, since he does not know how to differentiate it.
An interesting way to solve this is to insert "negative coordinates", so that you can check when a line has finished and when another line has started.
To make it easier to work with coordinates, we recommend that you work with the Point
, so there is no need to create 2 lists to store each coordinate axis.
Create a variable in your main class that represents a list of coordinates:
private ArrayList<Point> points = new ArrayList<>();
it is where we store the coordinates, including the "null".
In the mouseDragged()
", add each coordinate where the mouse drags within the component area to the list, and then forces the redraw. Changing in your code, it looks like this:
draft.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); points.add(e.getPoint()); draft.repaint(); } });
In the mouseReleased()
", add the final coordinate and the" negative "coordinate to the list. This point with negative coordinates is what will serve as a reference to identify that a line has ended.
draft.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { //adiciona uma coordenada "nula" para ignorarmos //no paintComponent points.add(e.getPoint()); points.add(new Point(-1, -1)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } });
Now, in paintComponent()
, we need to control how lines are drawn. The form I used was similar to yours, taking a point and its successor to create each segment of the lines. The "cat leap" is to check if the next coordinate of the current iteration is "negative", if it is, skip 2 iterations, since we do not want to connect the end of a line with the beginning of the other:
@Override protected void paintComponent(Graphics g) { // TODO Auto-generated method stub super.paintComponent(g); int i = 0; while (i < points.size() - 1) { Point currentPoint = points.get(i); Point nextPoint = points.get(i + 1); if (nextPoint.x != -1 && nextPoint.y != -1) { Graphics2D g1 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g1.setRenderingHints(rh); g1.drawLine(currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y); i++; } else { // quando as coordenadas do ponto seguinte forem (-1, -1), // pulamos essa iteração para evitar que a linha anterior // seja ligada a nova linha que está sendo desenhada i += 2; } } }
In this way, the loop will jump between the end point of a line and the negative point, going straight to the starting point of the other line.
Your class with the changes will be much cleaner and simpler, see:
import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; //Essa classe tem o objetivo de imitar o recurso de desenhar linhas do softwere paint, da Microsoft. public class DrawingSketch extends JFrame { private static final long serialVersionUID = 5661286812709693531L; private JPanel contentPane; private JPanel draft; private ArrayList<Point> points = new ArrayList<>(); /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { DrawingSketch frame = new DrawingSketch(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame. */ public DrawingSketch() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 900, 700); setLocationRelativeTo(null); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPane.setLayout(new BorderLayout(0, 0)); setContentPane(contentPane); draft = new Draft(); draft.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); points.add(e.getPoint()); draft.repaint(); } }); draft.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { // adiciona uma coordenada nula para ignorarmos // no paintComponent points.add(e.getPoint()); points.add(new Point(-1, -1)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }); contentPane.add(draft, BorderLayout.CENTER); draft.setLayout(null); } public class Draft extends JPanel { /** * */ private static final long serialVersionUID = 4886600019364448097L; public Draft() { } @Override protected void paintComponent(Graphics g) { // TODO Auto-generated method stub super.paintComponent(g); int i = 0; while (i < points.size() - 1) { Point currentPoint = points.get(i); Point nextPoint = points.get(i + 1); if (nextPoint.x != -1 && nextPoint.y != -1) { Graphics2D g1 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g1.setRenderingHints(rh); g1.drawLine(currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y); i++; } else { // quando as coordenadas do ponto seguinte forem (-1, -1), // pulamos essa iteração para evitar que a linha anterior // seja ligada a nova linha que está sendo desenhada i += 2; } } } } }
Running:
Update
Thereisamucheasierandmorepracticalwayofdoingthis,withouthavingtostoreandredrawalistofpointsonthescreen.Theworkaroundistouse BufferedImage
, which is nothing more an image representation of the drawing. Thus, paintComponent()
work is much smaller than iterating through a list with many points.
I leave the code below with comments on the changes:
import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.image.BufferedImage; import javax.swing.JPanel; public class Draft extends JPanel { //usaremos para armazenar e desenhar novas imagens private BufferedImage bfImage; //usaremos para desenhar as linhas conforme movimentacao //do mouse na tela private Point oldPoint = null; private Point newPoint = null; private static final long serialVersionUID = 4886600019364448097L; public Draft() { //crio uma instancia de bufferedimage do mesmo tamanho do painel bfImage = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_ARGB); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); //armazenamos o novo ponto arrastado //atualizamos a imagem na tela //invertemos os pontos, pois após tela desenhada //o ponto atual passa a ser antigo newPoint = e.getPoint(); updateImage(); oldPoint = newPoint; } }); addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { //ao liberar o mouse, armazenamos o ponto atual para finalizar //o desenho da atual linha e "limpamos" as duas referencias de pontos newPoint = e.getPoint(); updateImage(); newPoint = null; oldPoint = null; setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } @Override public void mousePressed(MouseEvent e) { //ao pressionar o mouse, verificamos se o ponto antigo existe //pois ele é o ponto de partida para desenhar o primeiro segmento //da nova linha if (oldPoint == null) { oldPoint = e.getPoint(); } } }); } //sobrescrevi o método getSize para que o tamanho do painel possa //ser informado corretamente ao bufferedImage @Override public Dimension getSize() { return new Dimension(500, 350); } private void updateImage() { //se o ponto atual não for nulo, criamos um grafico no buffer e desenhamos o //segmento da linha atual, forçando o redesenho da tela com repaint() if (newPoint != null) { Graphics2D g2 = bfImage.createGraphics(); g2.setColor(Color.BLACK); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.drawLine(oldPoint.x, oldPoint.y, newPoint.x, newPoint.y); g2.dispose(); repaint(); } } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); //apenas passamos o buffer para o paintcomponent desenhar //o que está nele. g.drawImage(bfImage, 0, 0, null); } }
And to use, just add an instance to your screen:
seuFrame.add(new Draft());
With these changes, the possibilities increase, including saving an image as a file, recovering a saved drawing, among others that msPaint already does. :)
How to Draw a Line on a Bufferedimage in Java
Source: https://itqna.net/questions/39596/how-draw-lines-following-mouse-movement
0 Response to "How to Draw a Line on a Bufferedimage in Java"
Post a Comment