在Java 2D API中将图像缩放为单个表面

arn*_*bpl 5 java swing 2d image awt

有一个叫方法scale(double sx, double sy)Graphics2DJava中.但是这种方法似乎将图像缩放为单独的表面而不是单个表面.结果,如果原始图像没有额外的宽度和高度,则缩放图像具有尖角.以下屏幕截图演示了此问题:

在此输入图像描述

这是代码:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class TestJava {
    static int scale = 10;

    public static class Test extends JPanel {
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.scale(scale, scale);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

            BufferedImage img = null;
            try {
                img = ImageIO.read(new File("Sprite.png"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            g2.drawImage(img, null, 5, 5);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        Test test = new Test();
        test.setBackground(Color.WHITE);
        frame.add(test);
        frame.setSize(300, 350);
        frame.setVisible(true);
    }
}
Run Code Online (Sandbox Code Playgroud)

该问题的一种可能的解决方案是具有额外宽度和高度的原始图像(在这种情况下"Sprite.png").但这似乎不是解决问题的好方法.所以我在Java中寻求一种编程方式来解决这个问题,而不是使用图像编辑器.这样做的方法是什么?

Mar*_*o13 2

一个有趣的问题(+1)。我认为为此找到一个好的解决方案并不简单:放大图像时的插值总是发生在图像内部,并且我无法想象一种方法可以使其模糊图像外部的缩放像素。

这导致了相当简单的解决方案:可以在整个图像周围添加 1 像素边距。事实上,这就是您自己提出的解决方案的编程方式。结果如下:

缩放图像

(左边是原始的,右边是额外的1像素边框)

这里作为 MCVE,基于您的示例

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class ScaledPaint
{
    static int scale = 10;

    public static class Test extends JPanel
    {
        BufferedImage image = createTestImage();
        BufferedImage imageWithMargin = addMargin(image);

        @Override
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.scale(scale, scale);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);

            g2.drawImage(image, 5, 5, null);
            g2.drawImage(imageWithMargin, 30, 5, null);
        }
    }

    private static BufferedImage createTestImage()
    {
        BufferedImage image =
            new BufferedImage(20, 20, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.RED);
        g.drawOval(0, 0, 19, 19);
        g.dispose();
        return image;
    }

    private static BufferedImage addMargin(BufferedImage image)
    {
        return addMargin(image, 1, 1, 1, 1);
    }

    private static BufferedImage addMargin(BufferedImage image, 
        int left, int right, int top, int bottom)
    {
        BufferedImage newImage =
            new BufferedImage(
                image.getWidth() + left + right,
                image.getHeight() + top + bottom, 
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, left, top, null);
        g.dispose();
        return newImage;
    }

    private static BufferedImage convertToARGB(BufferedImage image)
    {
        BufferedImage newImage =
            new BufferedImage(image.getWidth(), image.getHeight(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {

            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        Test test = new Test();
        test.setBackground(Color.WHITE);
        frame.add(test);
        frame.setSize(600, 350);
        frame.setVisible(true);
    }
}
Run Code Online (Sandbox Code Playgroud)

但...

...这种方法的一个问题已经可以在屏幕截图中看到:图像变大。绘制图像时必须考虑到这一点。因此,如果您的原始精灵都有一个漂亮的、预定义的、易于处理的尺寸(例如 16x32),那么它们之后的尺寸将为 18x34,这对于图块来说相当奇怪。这可能不是问题,具体取决于您处理图块尺寸的方式。但如果这是一个问题,人们可以考虑可能的解决方案。一种解决方案可能是...

  • 获取 16x32 输入图像
  • 创建 16x32 输出图像
  • 将 16x32 输入图像绘制到输出图像的 (1,1)-(15,31) 区域

但考虑到在这种大小的精灵中,每个像素都可能很重要,这也可能会产生不良影响......


旁白:虽然我认为您发布的代码只是作为 MCVE,但我想指出(对于可能阅读此问题和代码的其他人):

  • 不应该paintComponent在方法中加载图像
  • 为了高效绘制,任何加载的 PNG(特别是当它包含透明度时)都应该转换为已知类型的图像。这可以通过我的代码片段中的方法来完成convertToARGB