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

时间:2013-04-25 06:59:03

标签: java image swing masking

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

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

最好的方法是什么?

我更喜欢定义一个多边形,然后使用这个形状作为绘图的面具,如果可能的话。

我当前的想法:制作单个图像的副本并将所需形状外的所有像素设置为透明,然后绘制整个图像。我认为这可能有效,但复制和所有这些都可能太慢。

编辑:

我测试了Guillaume的解决方案,发现它的工作原理并没有使绘画速度极慢。使用剪辑导致绘图时间从14ms增加到35ms,但这些时间非常不准确。我使用了来自here的EDT的分析。这是代码。

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 个答案:

答案 0 :(得分:4)

实现此效果的一种简单方法是修改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();
                }
            }
        });
    }
}