代码之家  ›  专栏  ›  技术社区  ›  NoDataDumpNoContribution

如何在Swing中有效地绘制图像的子部分

  •  1
  • NoDataDumpNoContribution  · 技术社区  · 11 年前

    假设我有一个类型为的BufferedImage TYPE_4BYTE_ABGR 在Swing中,我只想画它的一部分。例如,我只画左半部分,或者一些三角形或更复杂的东西。

    原因是最终的图像应该由我拥有的单个图像的子部分组成。

    最好的方法是什么?

    如果可能的话,我更喜欢定义一个多边形,然后用这个形状作为绘制的遮罩。

    我目前的想法是:复制单个图像,将所需形状之外的所有像素设置为透明,然后绘制整个图像。我认为这可能会奏效,但在复制和其他方面可能太慢了。

    编辑:

    我测试了纪尧姆的解决方案,发现它有效,而且不会极大地减慢绘画速度。使用剪辑导致绘制时间从14毫秒增加到35毫秒,但这些时间非常不准确。我使用了来自 here 。这是代码。

    import java.awt.AWTEvent;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Toolkit;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.URL;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    /**
     *
     */
    public class ClipTilesTest {
    
        // tile size and number of tiles in each row/column
        private static int TILE_SIZE = 100;
        private static int TILE_NUM = 6;
    
        // taken from https://stackoverflow.com/questions/5541493/how-do-i-profile-the-edt-in-java-swing
        public static class TimedEventQueue extends EventQueue {
    
            @Override
            protected void dispatchEvent(AWTEvent event) {
                long startNano = System.nanoTime();
                super.dispatchEvent(event);
                long endNano = System.nanoTime();
    
                if (endNano - startNano > 5000000) {
                    System.out.println(((endNano - startNano) / 1000000) + "ms : " + event);
                }
            }
        }
    
        private static void initUI() {
    
            Toolkit.getDefaultToolkit().getSystemEventQueue().push(new TimedEventQueue());
    
            // download image
            BufferedImage image;
            try {
                image = ImageIO.read(new URL("http://download.chip.eu//ii/163859211_4b28e1e687.jpg"));
            } catch (IOException ex) {
                ex.printStackTrace();
                return;
            }
            // take out small chunk
            final BufferedImage tile = image.getSubimage(0, 0, TILE_SIZE, TILE_SIZE);
    
            JFrame frame = new JFrame();
            frame.setTitle(ClipTilesTest.class.getSimpleName());
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    
            // the panel containing some tiles
            JPanel view = new JPanel() {
                @Override
                public void paint(Graphics g) {
                    super.paint(g);
                    Graphics2D g2d = (Graphics2D) g;
    
                    for (int i = 0; i < TILE_NUM; i++) {
                        for (int j = 0; j < TILE_NUM; j++) {
    
                            // version 1
                            /*
                            g2d.setClip(i * TILE_SIZE, j * TILE_SIZE , (i+1)*TILE_SIZE, (j+1)*TILE_SIZE);
                            g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
                            */
    
                            // version 2
    
                            g2d.setClip(i * TILE_SIZE, j * TILE_SIZE , i*TILE_SIZE + TILE_SIZE/2, (j+1)*TILE_SIZE);
                            g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
                            g2d.setClip(i * TILE_SIZE + TILE_SIZE/2, j * TILE_SIZE , (i+1)*TILE_SIZE , (j+1)*TILE_SIZE);
                            g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
    
                        }
                    }
    
                }
            };
            view.setPreferredSize(new Dimension(TILE_SIZE * TILE_NUM, TILE_SIZE * TILE_NUM));
    
            // add, pack, set visible
            frame.add(view);
            frame.pack();
            frame.setVisible(true);
    
            // now make a repaint event, so we can start measuring
            view.repaint();
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    ClipTilesTest.initUI();
                }
            });
    
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  4
  •   Guillaume Polet    11 年前

    实现这种效果的一个简单方法是修改 Graphics 对象,并将其设置为要绘制的形状。

    我不知道这有多有效,但你可以考虑缓存剪切的图像,然后绘制整个缓存的图像。

    这是一个小的演示代码:

    result

    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.Rectangle;
    import java.awt.Shape;
    import java.awt.geom.Ellipse2D;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    
    import javax.swing.ImageIcon;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class TestClippedPanel {
    
        private static class ClippedPanel extends JPanel {
    
            private ImageIcon image;
    
            private List<Shape> shapes;
    
            public ClippedPanel() throws MalformedURLException {
                shapes = new ArrayList<Shape>();
                image = new ImageIcon(new URL("http://download.chip.eu//ii/163859211_4b28e1e687.jpg"));
                Random random = new Random();
                for (int i = 0; i < 10; i++) {
                    int x = random.nextInt(image.getIconWidth() - 1);
                    int y = random.nextInt(image.getIconHeight() - 1);
                    int w = random.nextInt(image.getIconWidth() - x) + 1;
                    int h = random.nextInt(image.getIconHeight() - y) + 1;
                    shapes.add(new Rectangle(x, y, w, h));
                }
                for (int i = 0; i < 10; i++) {
                    int x = random.nextInt(image.getIconWidth() - 1);
                    int y = random.nextInt(image.getIconHeight() - 1);
                    int w = random.nextInt(image.getIconWidth() - x) + 1;
                    int h = random.nextInt(image.getIconHeight() - y) + 1;
                    shapes.add(new Ellipse2D.Double(x, y, w, h));
                }
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Image img = image.getImage();
                for (Shape shape : shapes) {
                    ((Graphics2D) g).setClip(shape);
                    g.drawImage(img, 0, 0, this);
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(image.getIconWidth(), image.getIconHeight());
            }
    
        }
    
        protected void initUI() throws MalformedURLException {
            final JFrame frame = new JFrame(TestClippedPanel.class.getSimpleName());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            final ClippedPanel panel = new ClippedPanel();
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        new TestClippedPanel().initUI();
                    } catch (MalformedURLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }
    }