缩放 BufferedImage 实例会改变图片的亮度

5

我使用这段代码将图像绘制到 Graphics 组件中。如果图像足够大,它应该将其调整为最大可用空间:

    // getWidth() = component width, image.getWidth() = image width
    double w = getWidth() * 1.0 / image.getWidth();
    double h = getHeight() * 1.0 / image.getHeight();
    if (w < 1 || h < 1) {
        double d = Math.min(Math.min(w, h), 1);
        g.drawImage(bi.getScaledInstance((int) (d * image.getWidth()), (int) (d * image.getHeight()), Image.SCALE_REPLICATE), 0, 0, null);
    } else {
        g.drawImage(bi, 0, 0, null);
    }

代码能正常运行,图片大小也被正确地缩放了。但不幸的是,当图片被缩放时,Graphics上的图像亮度也会发生变化!
有没有人知道这是怎么回事?我附上了屏幕大小被缩放 (第一张) 和未被缩放的版本。
希望有人能够帮助我解决这个问题!
谢谢! Sebastian 使用 <code>getScaledInstance</code> 缩放后的图片 未使用 <code>getScaledInstance</code> 的图片
1个回答

6
这似乎是与Image.getScaledInstance有关的问题(也可能与灰度图像有关)。我尝试了几个其他提示,但结果相同。
相反,我使用了自己的缩放算法(从互联网上偷来的),它使用分治方法,通常会产生更好的结果...
所以,原始图片在顶部,左侧是Image#getScaledInstance,右侧是自定义缩放。
nb:这使用了我自己的个人库代码,所以它可能不完全适合您,但这呈现了基本内容...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ImageScaleTest {

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

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

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

    public static enum RenderQuality {

        High,
        Medium,
        Low
    }

    public class TestPane extends JPanel {

        private BufferedImage original;
        private BufferedImage scaled2;
        private Image scaled;

        public TestPane() {
            try {
                original = ImageIO.read(new File("/path/to/image"));
                scaled = original.getScaledInstance(original.getWidth() / 2, original.getHeight() / 2, Image.SCALE_DEFAULT);
                scaled2 = getScaledInstance(original, 0.5d, RenderQuality.High);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.drawImage(original, 0, 0, this);
            g2d.drawImage(scaled, 0, original.getHeight(), this);
            g2d.drawImage(scaled2, scaled.getWidth(this), original.getHeight(), this);
            g2d.dispose();
        }
    }

    public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, RenderQuality quality) {

        BufferedImage imgBuffer = null;

        if (quality == RenderQuality.High) {

//            System.out.println("Scale high quality...");
            imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);

        } else if (quality == RenderQuality.Medium) {

            imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);

        } else {

//            System.out.println("Scale low quality...");
            imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR, false);

        }

        return imgBuffer;

    }

    protected static 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 static 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 static 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;
    }
}

您可能也想查看Image.getScaledInstance()的危险性

附注:我进行了一些快速搜索,这似乎是API中的一个错误(或特性)


1
+1 for the perils; AffineTransformOp,在这里概述(https://dev59.com/f2855IYBdhLWcg3wp2N0#4216635),是一种替代方法。 - trashgod
谢谢!我会使用这个实现! - Sebastian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接