2017-09-06 19 views
1

저는 작은 이미지 편집기를 만들고 있는데, 지금은 마우스를 끌면 이미지를 그릴 수있는 기회가 생깁니다 (MS Paint의 연필 도구처럼).이미지를 올바르게 그립니다.

커서를 너무 빠르게 움직이면 응용 프로그램에서 채색되어야하는 모든 픽셀을 그릴 수 없으므로 약간의 숫자가 올바르게 채색됩니다.

나는 착색 한 화소를 추가하기 위하여 2 개의 해결책을 시도했다 : 나는 처음에 mouseDragged가 불릴 때 추가 된 모든 점을 저장 한 명부를 창조했다. 그 이후로 나는 을 BufferedImage 개체에 단순히 사용하기로 결정했습니다. 느린 것 같지 않습니다.

나는 또한 mouseMoved 메서드가 커서로 가리킨 모든 포인트를 감지 할 수 있는지 이해하기위한 테스트를 만들었고, 목록을 만들고 모든 포인트를 추가하면 음수 결과가 나타납니다. 목록에 몇 가지 점이 있습니다.

나는 빈 간격을 채우려고, 목록에 포함 된 점 사이 drawLine 메서드를 사용하려면 ImagePanel 클래스에서 목록을 다시 사용할 수 있다고 생각하지만 그것은 좋은 해결책이라고 생각하지 않습니다. if 이미지가 확대되어 drawLine 메서드를 다시 작성해야하며 모든 점을 이미지에 그리는 데 가장 좋은 순간을 찾아야합니다.

더 좋은 해결책이 있습니까? 어떤 도움을 주셔서 감사합니다!

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) + "%"); 
    } 
} 

그리고 아래의 문제를 보여주는 스크린 샷이 있습니다 : 난 내 MVCE을 게시 아래

(나는 이미지 편집기에서 모든 도구를 제거, 또한 애플리케이션의 디자인은 매우 가난하다)

Screenshot from image editor application

편집

덕분에 @ug_ 및 @Mad하기 프로그래머의 설명과 제안. 원래 게시물에서 말했듯이 이미 drawLine 메서드를 사용하려고 생각했지만 위에서 설명한 문제를 해결하는 방법을 알 수 없었습니다.

이제 이미지가 확대 된 경우 그래픽을 가져 와서 원본 이미지에 drawLine을 사용하는 것이 매우 간단하다는 것을 알았습니다. 나중에 포인트 목록을 유지해야 할 필요가 전혀 없습니다. 왜냐하면 나는 마지막 점을 그려야하기 때문에 (@ug_가 그의 코드에서하는 것처럼).

MainPanel 생성자에서 :

내 코드를 편집, 난 그냥 업데이트 된 블록을 게시

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 클래스 :

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); 
    } 
} 

것은 지금은 꽤 잘 작동!

+3

오히려 다음, 개별적으로 모든 픽셀을 그릴 각 픽셀 사이에 선이는 OS 이벤트를 생성하지 않습니다 이러한 상황에 대한 보상 것을 끌기 위해 노력 "모든 단일 픽셀에 대해 이동합니다"때문에 그렇지 않습니다. 마우스를 충분히 빠르게 움직이면 OS는 속도를 높이고 응답 속도가 느린 이벤트를 드롭합니다. – MadProgrammer

+0

@MadProgrammer 내 질문에 대한 편집을 도와 주셔서 감사합니다. 이제는 꽤 잘 작동합니다. – Ansharja

답변

2

마우스를 움직이면 모든 픽셀에 마우스 이벤트가 표시되지 않으므로 빠르게 움직이는 경우 특히 그렇습니다. 나는 왜 이것이 좋은지를 설명하는 좋은 문서를 찾으려고 노력했지만 손을 떼지는 못했습니다. https://docs.oracle.com/javase/8/docs/api/java/awt/event/MouseEvent.html tho에서 뭔가를 찾을 수 있습니다.

이 문제를 해결하기 위해 수행 할 작업은 java.awt.Graphics 메서드에서 제공하는 메서드를 사용하여 이전 위치에서 새로운 위치로 선을 그립니다. 이미지 나 일종의 레이어에이 작업을 수행하십시오. 그냥하지 Heres는 일부 코드 :

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

답변 해 주셔서 감사합니다. prevPoint 메서드를 사용하여 점 목록을 작성하지 않도록하는 질문 – Ansharja