一尘不染

Java 自动缩放ImageIcon到标签大小

java

在我的JFrame上,我使用以下代码在面板上显示图像:

  ImageIcon img= new ImageIcon("res.png");
  jLabel.setIcon(img);

我想“自动调整大小”标签中的图片。确实,有时图像大小只有几个像素,有时更大。

有没有办法设置标签的大小,然后自动调整标签中图像的大小?


阅读 1547

收藏
2020-03-05

共1个答案

一尘不染

这是一个棘手的问题。你强调了一个事实,即你正在使用a JLabel来显示图像,这是标准的处理方式,但是却JLabel是一个复杂的小野兽,具有文本,图标以及文本的对齐和定位。

如果你不需要所有这些额外的功能,我将为你自己创建一个能够绘制缩放图像的自定义组件…

下一个问题是,你如何缩放图像?你是否要保持图像的长宽比?是否要将图像“适合”或“填充”到可用空间。

@大卫是正确的。你应该尽可能避免Image#getScaledInstance使用它,因为它不是最快的,但是更重要的是,通常它也不提供最高的质量。

适合与填充

以下示例非常简单(并且从我的代码库中借来了很多代码,因此可能还有些复杂;)。可以从后台缩放线程使用它,但是我将根据原始图像的潜在大小来做出决定。

public class ResizableImage {

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

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

                try {
                    BufferedImage image = ImageIO.read(new File("/path/to/your/image"));

                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new ScalablePane(image));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public class ScalablePane extends JPanel {

        private Image master;
        private boolean toFit;
        private Image scaled;

        public ScalablePane(Image master) {
            this(master, true);
        }

        public ScalablePane(Image master, boolean toFit) {
            this.master = master;
            setToFit(toFit);
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Image toDraw = null;
            if (scaled != null) {
                toDraw = scaled;
            } else if (master != null) {
                toDraw = master;
            }

            if (toDraw != null) {
                int x = (getWidth() - toDraw.getWidth(this)) / 2;
                int y = (getHeight() - toDraw.getHeight(this)) / 2;
                g.drawImage(toDraw, x, y, this);
            }
        }

        @Override
        public void invalidate() {
            generateScaledInstance();
            super.invalidate();
        }

        public boolean isToFit() {
            return toFit;
        }

        public void setToFit(boolean value) {
            if (value != toFit) {
                toFit = value;
                invalidate();
            }
        }

        protected void generateScaledInstance() {
            scaled = null;
            if (isToFit()) {
                scaled = getScaledInstanceToFit(master, getSize());
            } else {
                scaled = getScaledInstanceToFill(master, getSize());
            }
        }

        protected BufferedImage toBufferedImage(Image master) {
            Dimension masterSize = new Dimension(master.getWidth(this), master.getHeight(this));
            BufferedImage image = createCompatibleImage(masterSize);
            Graphics2D g2d = image.createGraphics();
            g2d.drawImage(master, 0, 0, this);
            g2d.dispose();
            return image;
        }

        public Image getScaledInstanceToFit(Image master, Dimension size) {
            Dimension masterSize = new Dimension(master.getWidth(this), master.getHeight(this));
            return getScaledInstance(
                            toBufferedImage(master),
                            getScaleFactorToFit(masterSize, size),
                            RenderingHints.VALUE_INTERPOLATION_BILINEAR,
                            true);
        }

        public Image getScaledInstanceToFill(Image master, Dimension size) {
            Dimension masterSize = new Dimension(master.getWidth(this), master.getHeight(this));
            return getScaledInstance(
                            toBufferedImage(master),
                            getScaleFactorToFill(masterSize, size),
                            RenderingHints.VALUE_INTERPOLATION_BILINEAR,
                            true);
        }

        public Dimension getSizeToFit(Dimension original, Dimension toFit) {
            double factor = getScaleFactorToFit(original, toFit);
            Dimension size = new Dimension(original);
            size.width *= factor;
            size.height *= factor;
            return size;
        }

        public Dimension getSizeToFill(Dimension original, Dimension toFit) {
            double factor = getScaleFactorToFill(original, toFit);
            Dimension size = new Dimension(original);
            size.width *= factor;
            size.height *= factor;
            return size;
        }

        public double getScaleFactor(int iMasterSize, int iTargetSize) {
            return (double) iTargetSize / (double) iMasterSize;
        }

        public double getScaleFactorToFit(Dimension original, Dimension toFit) {
            double dScale = 1d;
            if (original != null && toFit != null) {
                double dScaleWidth = getScaleFactor(original.width, toFit.width);
                double dScaleHeight = getScaleFactor(original.height, toFit.height);
                dScale = Math.min(dScaleHeight, dScaleWidth);
            }
            return dScale;
        }

        public double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
            double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
            double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);

            return Math.max(dScaleHeight, dScaleWidth);
        }

        public BufferedImage createCompatibleImage(Dimension size) {
            return createCompatibleImage(size.width, size.height);
        }

        public BufferedImage createCompatibleImage(int width, int height) {
            GraphicsConfiguration gc = getGraphicsConfiguration();
            if (gc == null) {
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
            }

            BufferedImage image = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
            image.coerceData(true);
            return image;
        }

        protected BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {
            BufferedImage imgScale = img;
            int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
            int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);

            if (dScaleFactor <= 1.0d) {
                imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
            } else {
                imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
            }

            return imgScale;
        }

        protected BufferedImage getScaledDownInstance(BufferedImage img,
                        int targetWidth,
                        int targetHeight,
                        Object hint,
                        boolean higherQuality) {

            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, h;
                if (higherQuality) {
                    // Use multi-step technique: start with original size, then
                    // scale down in multiple passes with drawImage()
                    // until the target size is reached
                    w = img.getWidth();
                    h = img.getHeight();
                } else {
                    // Use one-step technique: scale directly from original
                    // size to target size with a single drawImage() call
                    w = targetWidth;
                    h = targetHeight;
                }

                do {
                    if (higherQuality && w > targetWidth) {
                        w /= 2;
                        if (w < targetWidth) {
                            w = targetWidth;
                        }
                    }
                    if (higherQuality && 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, hint);
                    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,
                        Object hint,
                        boolean higherQuality) {

            int type = BufferedImage.TYPE_INT_ARGB;

            BufferedImage ret = (BufferedImage) img;
            int w, h;
            if (higherQuality) {
                // Use multi-step technique: start with original size, then
                // scale down in multiple passes with drawImage()
                // until the target size is reached
                w = img.getWidth();
                h = img.getHeight();
            } else {
                // Use one-step technique: scale directly from original
                // size to target size with a single drawImage() call
                w = targetWidth;
                h = targetHeight;
            }

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

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

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

                ret = tmp;
                tmp = null;
            } while (w != targetWidth || h != targetHeight);
            return ret;
        }
    }
}
2020-03-05