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:
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!
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
@MadProgrammer Vielen Dank für Ihre Hilfe, ich habe meine Frage bearbeitet, und jetzt funktioniert es ganz gut – Ansharja