当前位置: 首页 > 面试题库 >

如何提高JPictureBox的大图绘制速度?

漆雕正奇
2023-03-14
问题内容

我有一个从java.awt.Component扩展的JPictureBox,请参见此处的代码http://pastebin.com/SAJc6Sht。但是,只有在没有图像拉伸的情况下,它才能很好地工作。特别是对于大局而言,它会极大地减慢程序速度。如何提高JPictureBox的大图绘制速度?

@Override
public void paint(Graphics g) {    
    super.paint(g);

    int x = 0;
    int y = 0;
    int w = 0;
    int h = 0;
    if (image != null) {
        switch (sizeMode) {
            case AUTO_SIZE:
            case NORMAL:
                w = image.getWidth();
                h = image.getHeight();
                break;
            case CENTER_IMAGE:
                w = image.getWidth();
                h = image.getHeight();
                x = (getWidth() - w) / 2;
                y = (getHeight() - h) / 2;
                break;
            case STRETCH_IMAGE:
                w = getWidth();
                h = getHeight();
                break;
            case ZOOM:
                w = (int) Math.round(image.getWidth() * zoomFactor);
                h = (int) Math.round(image.getHeight() * zoomFactor);
                break;
            case FIT_BOTH:
                if (image.getWidth() > image.getHeight()) {
                    w = getWidth();
                    h = (int) (w / getAR());

                    if (h > getHeight()) {
                        h = getHeight();
                        w = (int) (h * getAR());
                    }
                } else {
                    h = getHeight();
                    w = (int) (h * getAR());

                    if (w > getWidth()) {
                        w = getWidth();
                        h = (int) (w / getAR());
                    }
                }
                break;
            case FIT_WIDTH:
                w = getWidth();
                h = (int) (w / getAR());
                break;
            case FIT_HEIGHT:
                h = getHeight();
                w = (int) (h * getAR());
                break;
        }

        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g2d.drawImage(image, x, y, w, h, this);
    } else if (errorIcon != null) {
        w = errorIcon.getIconWidth();
        h = errorIcon.getIconHeight();
        x = (getWidth() - w) / 2;
        y = (getHeight() - h) / 2;
        errorIcon.paintIcon(this, g, x, y);
    }
}

问题答案:

基本上,您希望将图像的缩放卸载到后台线程,缩放很耗时,并且您不想在事件调度线程的上下文中进行。

然后,这又引发了一些问题。除非确实需要,否则您不希望缩放图像,而实际上只想要最新的结果。

您可以设置一个小的单一重复计时器,而不必每次缩放都按比例缩放图像,每次您要进行更改时都会重置该计时器。这会将多个调整大小的请求合并为尽可能少的请求。本示例使用一个javax.swing.Timer短125毫秒的延迟设置。因此,在两次更改请求之间至少要等待125毫秒,然后才能实际触发更新。

接下来,它使用ExecutorService具有单个线程的设置。这为我们提供了“尝试”取消任何先前存在的操作的方法,因为我们不希望得到结果并开始我们的最新请求。

接下来,实际的缩放操作采用两步缩放,首先,它尝试做一个快速,低质量的缩放比例,可以将其快速显示在屏幕上,然后执行较慢的高质量的缩放比例,该缩放比例会在以后的某个时间进行更新…

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(new TestPane()));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel implements Scrollable {

        private BufferedImage master;
        private Image scaled;
        private double zoom = 1d;
        private ExecutorService service;
        private List<Future> scaleTasks;
        private final Timer zoomTimer;

        public TestPane() {
            scaleTasks = new ArrayList<>(5);
            service = Executors.newSingleThreadExecutor();
            try {
                master = ImageIO.read(new File("Some image some where"));
                scaled = master;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            zoomTimer = new Timer(125, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("Update Zoom to " + getZoom());
                    updateToZoomFactor(getZoom());
                }
            });
            zoomTimer.setRepeats(false);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "plus");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "minus");

            am.put("plus", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    double zoom = getZoom() + 0.1;
                    setZoom(zoom);
                }
            });
            am.put("minus", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    double zoom = getZoom() - 0.1;
                    setZoom(zoom);
                }
            });

        }

        @Override
        public Dimension getPreferredSize() {
            return scaled == null
                            ? new Dimension(master.getWidth(), master.getHeight())
                            : new Dimension(scaled.getWidth(this), scaled.getHeight(this));
        }

        public BufferedImage getMaster() {
            return master;
        }

        public void setZoom(double value) {
            if (value < 0.1) {
                value = 0.1;
            } else if (value > 2) {
                value = 2d;
            }
            if (value != zoom) {
                zoom = value;
                zoomTimer.restart();
            }
        }

        public double getZoom() {
            return zoom;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (scaled != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                int x = (getWidth() - scaled.getWidth(this)) / 2;
                int y = (getHeight() - scaled.getHeight(this)) / 2;
                g2d.drawImage(scaled, x, y, this);
                g2d.dispose();
            }
        }

        protected void setScaledResult(final Image image) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    scaled = image;
                    invalidate();
                    revalidate();
                    repaint();
                }
            });
        }

        protected void updateToZoomFactor(double zoom) {
            Future[] tasks = scaleTasks.toArray(new Future[scaleTasks.size()]);
            for (Future task : tasks) {
                if (!task.isCancelled()) {
                    task.cancel(true);
                } else {
                    scaleTasks.remove(task);
                }
            }
            service.submit(new RescaleTask(zoom));
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return new Dimension(400, 400);
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 128;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 128;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return false;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return false;
        }

        protected class RescaleTask implements Callable<Image> {

            private double zoom;

            protected RescaleTask(double zoom) {
                this.zoom = zoom;
            }

            @Override
            public Image call() throws Exception {
                if (zoom == 1) {
                    scaled = getMaster();
                } else {
                    int width = (int) (getMaster().getWidth() * zoom);
                    int height = (int) (getMaster().getHeight() * zoom);
                    Image scaled = getMaster().getScaledInstance((int) width, (int) height, Image.SCALE_FAST);
                    if (!Thread.currentThread().isInterrupted()) {
                        setScaledResult(scaled);

                        if (zoom < 1) {
                            scaled = getScaledDownInstance(getMaster(), (int) width, (int) height);
                        } else {
                            scaled = getScaledUpInstance(getMaster(), (int) width, (int) height);
                        }

                        if (!Thread.currentThread().isInterrupted()) {
                            setScaledResult(scaled);
                        } else {
                            System.out.println("Was interrupted during quality scale");
                        }

                    } else {
                        System.out.println("Was interrupted during fast scale");
                    }
                }
                return scaled;
            }

            protected BufferedImage getScaledDownInstance(BufferedImage img,
                            int targetWidth,
                            int targetHeight) {

                int type = (img.getTransparency() == Transparency.OPAQUE)
                                ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

                BufferedImage ret = (BufferedImage) img;

                if (targetHeight > 0 || targetWidth > 0) {

                    int w = img.getWidth();
                    int h = img.getHeight();

                    do {

                        System.out.println(w + "x" + h + " -> " + targetWidth + "x" + targetHeight);

                        if (w > targetWidth) {
                            w /= 2;
                            if (w < targetWidth) {
                                w = targetWidth;
                            }
                        }

                        if (h > targetHeight) {
                            h /= 2;
                            if (h < targetHeight) {
                                h = targetHeight;
                            }
                        }

                        BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
                        Graphics2D g2 = tmp.createGraphics();
                        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                        g2.drawImage(ret, 0, 0, w, h, null);
                        g2.dispose();

                        ret = tmp;

                    } while (w != targetWidth || h != targetHeight);

                } else {

                    ret = new BufferedImage(1, 1, type);

                }

                return ret;

            }

            protected BufferedImage getScaledUpInstance(BufferedImage img,
                            int targetWidth,
                            int targetHeight) {

                int type = BufferedImage.TYPE_INT_ARGB;

                BufferedImage ret = (BufferedImage) img;
                int w = img.getWidth();
                int h = img.getHeight();

                do {

                    if (w < targetWidth) {
                        w *= 2;
                        if (w > targetWidth) {
                            w = targetWidth;
                        }
                    }

                    if (h < targetHeight) {
                        h *= 2;
                        if (h > targetHeight) {
                            h = targetHeight;
                        }
                    }

//          createCompatibleImage(w, h, type)
                    BufferedImage tmp = new BufferedImage(w, h, type);
                    Graphics2D g2 = tmp.createGraphics();
                    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    g2.drawImage(ret, 0, 0, w, h, null);
                    g2.dispose();

                    ret = tmp;
                    tmp = null;

                } while (w != targetWidth || h != targetHeight);

                return ret;

            }

        }

    }

}

nb:这一点杀不死,但展示了一些关键思想

可能有帮助的其他事情之一是将图像转换为的兼容颜色模型GraphicsDevice,例如…

            master = ImageIO.read(new File("Some image some where"));
            GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
            GraphicsConfiguration gc = gd.getDefaultConfiguration();
            BufferedImage compatible = gc.createCompatibleImage(master.getWidth(), master.getHeight(), Transparency.TRANSLUCENT);
            Graphics2D g2d = compatiable.createGraphics();
            g2d.drawImage(master, 0, 0, this);
            g2d.dispose();
            master = compatible;


 类似资料:
  • 我正在用Javafx制作一个基本的绘画应用程序。我想知道是否有办法提高画布上线条的绘制速度。 正如您所看到的,使用鼠标侦听器来检测线开始的(x1,y1)位置,以及设置线应该结束的(x2,y2)位置,图形非常简单。正如您所看到的,我在绘制后清除线,这是我的意图,因为这是一个应该“显示”线应该去哪里的函数,例如在Microsoft Paint中使用线函数。然而,所有这些的问题是,这条线“滞后”在光标后

  • 问题内容: 我已经使用matplotlib绘制了一些实验结果。但是,通过单击图像右侧来保存图片会产生非常差的质量/低分辨率图像。 我正在寻找的示例图:示例图 问题答案: 您可以用来导出到图像文件: 另外,您可以为某些标量值指定参数,例如:

  • 本文向大家介绍如何提高javascript加载速度,包括了如何提高javascript加载速度的使用技巧和注意事项,需要的朋友参考一下 方法如下: 1、将所有<script>标签放在尽可能接近<body>标签底部的位置,以保证页面在脚本运行之前完成解析尽量减少对整个页面下载的影响 2、限制页面的<script>总数也可以改善性能。每当页面解析碰到一个<script>标签时, 紧接着有一段时间用于代

  • 问题内容: 我试过了 但它产生错误: 任何的想法? 问题答案: 好吧,错误消息很清楚:不是实体。如果要映射基本元素的集合,请使用批注(来自Hibernate)或批注(来自JPA 2.0)。 因此,假设您使用的是Hibernate Annotations 3.4,请尝试以下操作: 或者,使用泛型时: 如果您使用的是Hibernate Annotations 3.5+,则更喜欢JPA 2.0注释: 或

  • 我是Apache Hbase的新手,我使用的是hbase-0.98.13,并且我已经创建了一个表示例,其列族为sample_family。并且我已经将pig脚本的输出加载到hbase表中。当我尝试基于列族中的一个列扫描表时,它需要超过2分钟。 是否为此进行任何配置更改?有人能帮我吗?

  • 嗨,有没有什么方法可以提高滚动速度。我找到了一些解决方案,但都不适合我。这就是我尝试的: 或者使用css: 还有别的办法吗?编辑:上面的解决方案都不起作用,我试图使用大得离谱的数字,但滚动速度还是一样