2017-09-06 4 views
1

Ich erstelle einen kleinen Bildbearbeitungsprogramm und gerade versuche ich, dem Benutzer die Möglichkeit zu geben, das Bild durch Ziehen der Maus zu zeichnen (wie Bleistift in MS Paint).Richtig über ein Bild zeichnen

Ich habe einige Schwierigkeiten, da, wenn ich den Cursor zu schnell bewege, die Anwendung nicht alle Pixel zeichnen kann, die gefärbt werden sollten, nur eine kleine Zahl ist richtig eingefärbt.

Ich habe zwei Lösungen ausprobiert, um die farbigen Pixel hinzuzufügen: Zuerst erstellte ich eine Liste, in der ich alle Punkte speicherte, die hinzugefügt wurden, als mouseDragged aufgerufen wurde. Danach entschied ich mich, einfach setRGB auf BufferedImage Objekt zu verwenden, da es nicht langsamer zu sein scheint.

Ich habe auch einen Test zu verstehen, wenn mouseMoved Methode in der Lage ist, alle Punkte zu erkennen, die durch den Cursor schweben, und ich hatte ein negatives Ergebnis, wenn ich eine Liste erstellen und jeden Punkt hinzufügen, wenn ich die Liste gibt es nur einige Punkte darin.

Ich dachte, ich könnte wieder die Liste auf ImagePanel Klasse verwenden, um drawLine Methode zwischen den Punkten, die in der Liste enthalten sind, zu versuchen, die leere Lücke zu füllen, aber ich glaube nicht, es ist eine gute Lösung, denn wenn Das Bild wird gezoomt, ich müsste die DrawLine-Methode neu erfinden und ich müsste auch den besten Moment finden, um alle Punkte auf das Bild zu zeichnen.

Gibt es eine bessere Lösung? Jede Hilfe wird geschätzt!

Below i mein MVCE Post (i alle Werkzeuge aus dem Bild-Editor entfernt, auch das Design der Anwendung ist sehr schlecht):

import java.awt.*; 
import java.awt.event.*; 
import java.awt.image.BufferedImage; 
import java.net.URL; 
import java.util.ArrayList; 
import javax.imageio.ImageIO; 
import javax.swing.*; 
import javax.swing.border.*; 
public class ImageEditor 
{ 
    public static void main (String [] a) { 
     SwingUtilities.invokeLater (new Runnable() { 
      @Override public void run() { 
       createAndShowGUI(); 
      } 
     }); 
    } 
    private static void createAndShowGUI() { 
     JFrame frame = new JFrame ("Image Editor"); 
     frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); 
     frame.setContentPane (new MainPanel()); 
     frame.setExtendedState (JFrame.MAXIMIZED_BOTH); 
     frame.pack(); 
     frame.setLocationRelativeTo (null); 
     frame.setVisible (true); 
    } 
} 
class MainPanel extends JPanel 
{ 
    // private ArrayList <Point> points = new ArrayList <Point>(); 
    private ImagePanel imagePanel; 
    private ZoomPanel zoomPanel; 

    public MainPanel() { 
     super (new BorderLayout()); 
     // --- Mouse Adapter --- 
     MouseAdapter mouseAdapter = new MouseAdapter() { 
      @Override public void mouseDragged (MouseEvent e) { 
       if (SwingUtilities.isLeftMouseButton (e)) imagePanel.setPixelColor (e.getX(), e.getY()); 
      } 
      /* @Override public void mouseMoved (MouseEvent e) { 
       points.add (e.getPoint()); 
      } */ 
      @Override public void mouseReleased (MouseEvent e) { 
       // for (Point p : points) System.out.println (p); 
       if (SwingUtilities.isLeftMouseButton (e)) imagePanel.setPixelColor (e.getX(), e.getY()); 
      } 
     }; 
     // --- Image Panel --- 
     imagePanel = new ImagePanel(); 
     imagePanel.addMouseMotionListener (mouseAdapter); 
     imagePanel.addMouseListener (mouseAdapter); 
     // --- Image Panel View --- 
     JPanel imagePanelView = new JPanel (new FlowLayout (FlowLayout.LEFT, 20, 20)); 
     imagePanelView.add (imagePanel); 
     // --- Image Panel Scroll Pane --- 
     JScrollPane scrollPane = new JScrollPane (imagePanelView); 
     scrollPane.addMouseWheelListener (new MouseWheelListener() { 
      @Override public void mouseWheelMoved (MouseWheelEvent e) { 
       if (e.isControlDown()) { 
        int rotation = e.getWheelRotation(); 
        if ((rotation < 0 && imagePanel.zoomIn()) || (rotation > 0 && imagePanel.zoomOut())) zoomPanel.zoomLevelChanged(); 
       } 
      } 
     }); 
     scrollPane.getHorizontalScrollBar().setUnitIncrement (100); 
     scrollPane.getVerticalScrollBar().setUnitIncrement (100); 
     scrollPane.setBorder (new EmptyBorder (0, 0, 0, 0)); 
     // --- Loading image --- 
     try { 
      imagePanel.setImage (ImageIO.read (new URL ("https://spotlight.it-notes.ru/wp-content/uploads/2016/10/255b4aa1455158ffde176a1e814c634f.jpg"))); 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 
     // --- Bottom Panel --- 
     JPanel bottomPanel = new JPanel (new BorderLayout (100, 0)); 
     bottomPanel.add (zoomPanel = new ZoomPanel (imagePanel), BorderLayout.EAST); 
     bottomPanel.setBorder (new MatteBorder (1, 0, 0, 0, getBackground().darker())); 
     // --- Adding components --- 
     add (scrollPane, BorderLayout.CENTER); 
     add (bottomPanel, BorderLayout.SOUTH); 
    } 
} 
class ImagePanel extends JPanel 
{ 
    private int zoomLevel; 
    private BufferedImage image; 
    private int rgb = Color.YELLOW.getRGB(); 
    //private ArrayList <Point> drawnPoints; 

    public ImagePanel() { 
     super (new FlowLayout (FlowLayout.LEFT, 0, 0)); 
     zoomLevel = 1; 
     //drawnPoints = new ArrayList <Point>(); 
    } 
    protected BufferedImage getImage() { 
     if (image == null) return null; 
     // A copy of original image is returned. 
     BufferedImage copy = new BufferedImage (image.getWidth(), image.getHeight(), image.getType()); 
     Graphics2D g = copy.createGraphics(); 
     g.drawImage (image, 0, 0, null); 
     g.dispose(); 
     return copy; 
    } 
    protected int getImageHeight() { 
     if (image == null) return 0; 
     return image.getHeight(); 
    } 
    protected int getImageWidth() { 
     if (image == null) return 0; 
     return image.getWidth(); 
    } 
    @Override public Dimension getPreferredSize() { 
     if (image == null) return new Dimension (0, 0); 
     return new Dimension (image.getWidth() * zoomLevel, image.getHeight() * zoomLevel); 
    } 
    public int getZoomLevel() { 
     return zoomLevel; 
    } 
    @Override protected void paintComponent (Graphics g) { 
     super.paintComponent (g); 
     g.drawImage (image, 0, 0, image.getWidth() * zoomLevel, image.getHeight() * zoomLevel, this); 
     //if (drawnPoints != null) { 
     // g.setColor (Color.YELLOW); 
     // for (Point point : drawnPoints) g.fillRect (point.x * zoomLevel, point.y * zoomLevel, zoomLevel, zoomLevel); 
     //} 
    } 
    private void refresh() { 
     Container parent = getParent(); 
     parent.revalidate(); 
     parent.repaint(); 
    } 
    protected void setImage (BufferedImage image) { 
     this.image = image; 
     refresh(); 
    } 
    protected void setPixelColor (int scaledX, int scaledY) { 
     int x = scaledX/zoomLevel, y = scaledY/zoomLevel; 
     if (x >= 0 && y >= 0 && x < image.getWidth() && y < image.getHeight()) { 
      //drawnPoints.add (new Point (x, y)); 
      image.setRGB (x, y, rgb); 
      refresh(); 
     } 
    } 
    protected boolean zoom (int zoomLevel) { 
     if (image == null || zoomLevel < 1 || zoomLevel > 8) return false; 
     this.zoomLevel = zoomLevel; 
     refresh(); 
     return true; 
    } 
    protected boolean zoomIn() { 
     return image != null && zoom (zoomLevel + 1); 
    } 
    protected boolean zoomOut() { 
     return image != null && zoom (zoomLevel - 1); 
    } 
} 
class ZoomPanel extends JPanel 
{ 
    private ImagePanel imagePanel; 
    private JLabel label; 

    protected ZoomPanel (ImagePanel imagePanel) { 
     super (new FlowLayout (FlowLayout.RIGHT, 20, 0)); 
     this.imagePanel = imagePanel; 
     add (label = new JLabel ("100%")); 
     add (new JButton (new AbstractAction ("-") { 
      @Override public void actionPerformed (ActionEvent e) { 
       if (imagePanel.zoomOut()) zoomLevelChanged(); 
      } 
     })); 
     add (new JButton (new AbstractAction ("+") { 
      @Override public void actionPerformed (ActionEvent e) { 
       if (imagePanel.zoomIn()) zoomLevelChanged(); 
      } 
     })); 
     setBorder (new EmptyBorder (3, 0, 3, 20)); 
    } 
    protected void zoomLevelChanged() { 
     label.setText (String.valueOf (imagePanel.getZoomLevel() * 100) + "%"); 
    } 
} 

Und unten gibt es einen Screenshot, der das Problem zeigt:

Screenshot from image editor application

EDIT

Dank @ug_ und @Mad Programmierer für ihre Erklärungen und Vorschläge. Ich hatte bereits daran gedacht, die drawLine-Methode zu verwenden, wie ich im Originalbeitrag gesagt habe, aber ich konnte nicht herausfinden, wie ich die oben genannten Probleme lösen kann.

Jetzt habe ich festgestellt, dass, wenn das Bild gezoomt ist, es ziemlich einfach ist, DrawLine auf dem Originalbild zu verwenden, indem man seine Grafiken erhält, und ich brauche überhaupt keine Liste von Punkten, die später gezeichnet werden, da ich nur den letzten Punkt behalten muss (wie es @ug_ in seinem Code tut).

ich meinen Code bearbeiten, ich nur die Blöcke schreiben, die überarbeitet wurden:

In Mainpanel-Konstruktor:

MouseAdapter mouseAdapter = new MouseAdapter() { 
      @Override public void mouseDragged (MouseEvent e) { 
       if (SwingUtilities.isLeftMouseButton (e)) imagePanel.addPoint (e.getX(), e.getY()); 
      } 
      @Override public void mouseReleased (MouseEvent e) { 
       if (SwingUtilities.isLeftMouseButton (e)) imagePanel.setPixelColor (e.getX(), e.getY()); 
      } 
     }; 

ImagePanel Klasse:

class ImagePanel extends JPanel 
{ 
    private int zoomLevel; 
    private BufferedImage image; 
    private int rgb = Color.YELLOW.getRGB(); 
    private Point lastPoint; 

    public ImagePanel() { 
     super (new FlowLayout (FlowLayout.LEFT, 0, 0)); 
     zoomLevel = 1; 
    } 
    protected void addPoint (int scaledX, int scaledY) { 
     int x = scaledX/zoomLevel, y = scaledY/zoomLevel; 
     if (x >= 0 && y >= 0 && x < image.getWidth() && y < image.getHeight()) { 
      if (lastPoint == null) image.setRGB (x, y, rgb); 
      else { 
       Graphics2D g = image.createGraphics(); 
       g.setColor (Color.YELLOW); 
       g.drawLine (lastPoint.x, lastPoint.y, x, y); 
       g.dispose(); 
      } 
      lastPoint = new Point (x, y); 
      refresh(); 
     } 
    } 
    protected int getImageHeight() { 
     if (image == null) return 0; 
     return image.getHeight(); 
    } 
    protected int getImageWidth() { 
     if (image == null) return 0; 
     return image.getWidth(); 
    } 
    @Override public Dimension getPreferredSize() { 
     if (image == null) return new Dimension (0, 0); 
     return new Dimension (image.getWidth() * zoomLevel, image.getHeight() * zoomLevel); 
    } 
    public int getZoomLevel() { 
     return zoomLevel; 
    } 
    @Override protected void paintComponent (Graphics g) { 
     super.paintComponent (g); 
     g.drawImage (image, 0, 0, image.getWidth() * zoomLevel, image.getHeight() * zoomLevel, this); 
    } 
    private void refresh() { 
     Container parent = getParent(); 
     parent.revalidate(); 
     parent.repaint(); 
    } 
    protected void setImage (BufferedImage image) { 
     this.image = image; 
     refresh(); 
    } 
    protected void setPixelColor (int scaledX, int scaledY) { 
     int x = scaledX/zoomLevel, y = scaledY/zoomLevel; 
     if (x >= 0 && y >= 0 && x < image.getWidth() && y < image.getHeight()) { 
      lastPoint = null; 
      image.setRGB (x, y, rgb); 
      refresh(); 
     } 
    } 
    protected boolean zoom (int zoomLevel) { 
     if (image == null || zoomLevel < 1 || zoomLevel > 8) return false; 
     this.zoomLevel = zoomLevel; 
     refresh(); 
     return true; 
    } 
    protected boolean zoomIn() { 
     return image != null && zoom (zoomLevel + 1); 
    } 
    protected boolean zoomOut() { 
     return image != null && zoom (zoomLevel - 1); 
    } 
} 

Jetzt funktioniert es ziemlich gut!

+3

Vielmehr dann versuchen, einzeln alle Pixel zu zeichnen, eine Linie zwischen jedem Pixel zu zeichnen, diese für solche Situationen zu kompensieren, wo das Betriebssystem keine Ereignisse erzeugen für "jedes einzelne Pixel, das es bewegt", weil es nicht geht. Wenn Sie die Maus schnell genug bewegen, wird das Betriebssystem Ereignisse zugunsten der Geschwindigkeit fallen lassen und reaktionsfähig – MadProgrammer

+0

@MadProgrammer Vielen Dank für Ihre Hilfe, ich habe meine Frage bearbeitet, und jetzt funktioniert es ganz gut – Ansharja

Antwort

2

Sie erhalten kein Mausereignis für jeden Pixel, den Ihre Maus bewegt, dies gilt insbesondere, wenn Sie es wirklich schnell bewegen. Ich habe versucht, eine gute Dokumentation zu finden, die darauf hinweist, warum das so ist, aber ich konnte es nicht aus der Hand geben. Vielleicht finden Sie etwas in https://docs.oracle.com/javase/8/docs/api/java/awt/event/MouseEvent.html tho.

Was ich tun würde, um dieses Problem zu lösen, ist die von den java.awt.Graphics Verfahren bereitgestellt Methoden verwenden, um eine Zeile aus Ihrer vorherigen Position auf den neuen zu ziehen. Tue dies entweder auf dein Bild oder auf eine andere Ebene. Heres Code einige, die gerade das tut:

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 

public class SO46085131 extends JPanel { 

    private final Dimension LAYER_SIZE = new Dimension(300, 300); 

    private Point prevPoint = null; 
    private BufferedImage paintLayer; 
    private Graphics paintLayerGraphics; 

    public SO46085131(){ 
     setBackground(Color.black); 
     // create our layer that we will paint onto 
     paintLayer = new BufferedImage(LAYER_SIZE.width, LAYER_SIZE.height, BufferedImage.TYPE_INT_ARGB); 

     // get our graphics for the painting layer and fill in a background cause thats cool 
     paintLayerGraphics = paintLayer.getGraphics(); 
     paintLayerGraphics.setColor(Color.red); 
     paintLayerGraphics.fillRect(0, 0, paintLayer.getWidth(), paintLayer.getHeight()); 

     setBackground(Color.WHITE); 
     // listen for drag events, then draw 
     // TODO: You should listen for mouse up and down events instead of dragging so you can clear your previous point 
     // TODO: Big boy bugs here! for you to fix 
     addMouseMotionListener(new MouseAdapter() { 
      @Override 
      public void mouseDragged(MouseEvent e) { 
       // if we moved the mouse previously draw a line from our prev point to our current position 
       if(prevPoint != null) { 
        paintLayerGraphics.setColor(Color.black); 
        paintLayerGraphics.drawLine(prevPoint.x, prevPoint.y, e.getX(), e.getY()); 
        repaint(); 
       } 
       // store previous point 
       prevPoint = e.getPoint(); 
      } 
     }); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     // draw our sweet painting layer ontop of our component. 
     g.drawImage(paintLayer, 0, 0, this); 
    } 

    public static void main(String [] args) { 
     // just new up a sample jframe to display our stuff on 
     JFrame frame = new JFrame(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(new SO46085131()); 
     frame.setSize(500, 400); 
     frame.setVisible(true); 
    } 
} 

result

+0

Vielen Dank für Ihre Antwort, ich bearbeitet meine Stellen Sie mithilfe Ihrer prevPoint-Methode eine Frage, um zu vermeiden, dass eine Liste mit Punkten erstellt wird – Ansharja