将微小的低分辨率应用扩展到更大的屏幕尺寸

时间:2017-06-05 07:06:49

标签: java swing awt

我正在用Java创建一个复古的街机游戏。游戏的屏幕分辨率为304 x 256,我想保留它以保留游戏的复古特征(视觉效果,动画,字体阻挡等)。

但是当我在大型桌面显示器上渲染它时,它太小了,正如人们所期望的那样。

我希望能够通过一个常数因子来扩展窗口,而不必编写各种paint(Graphics)方法来了解有关扩展的事实。也就是说,我希望渲染代码相信屏幕是304 x 256.我也不想更改桌面分辨率或进入全屏独占模式。只是想要一个缩小像素的大窗口。

我正在寻找以下内容:

scale(myJFrame, 4);

并让所有内容自动放大。

UPDATE:关于输入,我的游戏恰好使用键盘输入,所以我自己不需要trashgod描述的逆变换。我仍然可以想象其他人会需要它,所以我认为这是一个恰当的建议。

3 个答案:

答案 0 :(得分:2)

建议here建议的一种方法是依靠drawImage()来扩展内容的图像。您的游戏会在BufferedImage的图形上下文中呈现自己,而不是paintComponent()的实现。如果游戏包含鼠标交互,则必须缩放鼠标坐标,如图所示。在下面的变体形式中,我为CENTER面板提供了SCALE = 8倍数的首选尺寸,并将原始图标添加为WEST的{​​{1}} }}。默认情况下,BorderLayout会忽略组件的首选大小,您可能希望将其添加到CENTER的(可能是嵌套的)面板中。调整框架大小以查看效果。

FlowLayout

image

f.setLayout(new FlowLayout());
f.add(new Grid(NAME));
//f.add(new JLabel(ICON), BorderLayout.WEST);

答案 1 :(得分:2)

建议here的一种方法是依赖图形上下文的scale()方法,并构造为逆变换以在鼠标坐标和图像坐标之间进行转换。在下面的示例中,请注意原始图像的大小为256 x 256,而显示的图像按SCALE = 2.0缩放。鼠标悬停在图像的中心上方;工具提示显示显示中的任意点和原始中心点(127,127)。

image

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

/** @see https://stackoverflow.com/a/2244285/230513 */
public class InverseTransform {

    private static final double SCALE = 2.0;

    public static void main(String[] args) {
        JFrame frame = new JFrame("Inverse Test");
        BufferedImage image = getImage(256, 'F');
        AffineTransform at = new AffineTransform();
        at.scale(SCALE, SCALE);
        frame.add(new ImageView(image, at));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    private static BufferedImage getImage(int size, char c) {
        final Font font = new Font("Serif", Font.BOLD, size);
        BufferedImage bi = new BufferedImage(
            size, size, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = bi.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setPaint(Color.white);
        g2d.fillRect(0, 0, size, size);
        g2d.setPaint(Color.blue);
        g2d.setFont(font);
        FontMetrics fm = g2d.getFontMetrics();
        int x = (size - fm.charWidth(c)) / 2;
        int y = fm.getAscent() + fm.getDescent() / 4;
        g2d.drawString(String.valueOf(c), x, y);
        g2d.setPaint(Color.black);
        g2d.drawLine(0, y, size, y);
        g2d.drawLine(x, 0, x, size);
        g2d.fillOval(x - 3, y - 3, 6, 6);
        g2d.drawRect(0, 0, size - 1, size - 1);
        g2d.dispose();
        return bi;
    }

    private static class ImageView extends JPanel {

        private BufferedImage image;
        private AffineTransform at;
        private AffineTransform inverse;
        private Graphics2D canvas;
        private Point oldPt = new Point();
        private Point newPt;

        @Override
        public Dimension getPreferredSize() {
            return new Dimension( // arbitrary multiple of SCALE
                (int)(image.getWidth()  * SCALE * 1.25),
                (int)(image.getHeight() * SCALE * 1.25));
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            try {
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
                inverse = g2d.getTransform();
                inverse.invert();
                g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
                g2d.transform(at);
                g2d.translate(-image.getWidth() / 2, -image.getHeight() / 2);
                inverse.concatenate(g2d.getTransform());
                g2d.drawImage(image, 0, 0, this);
            } catch (NoninvertibleTransformException ex) {
                ex.printStackTrace(System.err);
            }
        }

        ImageView(final BufferedImage image, final AffineTransform at) {
            this.setBackground(Color.lightGray);
            this.image = image;
            this.at = at;
            this.canvas = image.createGraphics();
            this.canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
            this.canvas.setColor(Color.BLACK);
            this.addMouseMotionListener(new MouseMotionAdapter() {

                @Override
                public void mouseMoved(MouseEvent e) {
                    Point m = e.getPoint();
                    Point i = e.getPoint();
                    try {
                        inverse.inverseTransform(m, i);
                        setToolTipText("<html>Mouse: " + m.x + "," + m.y
                            + "<br>Inverse: " + i.x + "," + i.y + "</html>");
                    } catch (NoninvertibleTransformException ex) {
                        ex.printStackTrace();
                    }
                }
            });
        }
    }
}

答案 2 :(得分:2)

感谢trashgod用他的两个答案指出了正确的方向。我能够将两个答案的元素结合起来,得到适合我需要做的事情。

首先,我的目标是扩展整个UI而不是扩展单个图标或其他简单组件。通过“整个UI”,我特别指的是包含使用JPanel布局的多个自定义子组件的BorderLayout。没有JButtons或任何其他交互式Swing组件,也没有鼠标输入(它都是基于键盘的输入),所以我只需要将一个304 x 256 JPanel缩放3倍或者4。

这就是我的所作所为:

package bb.view;

import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;

import static bb.BBConfig.SCREEN_WIDTH_PX;   // 304
import static bb.BBConfig.SCREEN_HEIGHT_PX;  // 256

public class Resizer extends JPanel {
    private static final int K = 3;
    private static final Dimension PREF_SIZE =
        new Dimension(K * SCREEN_WIDTH_PX, K * SCREEN_HEIGHT_PX);
    private static final AffineTransform SCALE_XFORM =
        AffineTransform.getScaleInstance(K, K);

    public Resizer(JComponent component) {
        setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        add(component);
    }

    @Override
    public Dimension getPreferredSize() {
        return PREF_SIZE;
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setTransform(SCALE_XFORM);
        super.paint(g2);
    }
}

解决方案的一些重要元素:

  • 在这里使用FlowLayout收缩包装子组件,这就是我想要的。 (感谢trashgod。)也就是说,我不希望孩子扩展以填充Resizer首选大小,因为这会破坏子组件的布局。特别是它在子组件的CENTERSOUTH区域之间创造了巨大的差距。
  • 我将FlowLayout配置为左对齐和hgap,vgap = 0.这样我的缩放变换也会将缩放版本固定在左上角。
  • 我使用AffineTransform来完成缩放。 (再次感谢trashgod。)
  • 我使用paint()代替paintComponent(),因为Resizer只是一个包装器。我不想画边框。我基本上想拦截paint()调用,插入缩放变换,然后让JPanel.paint()执行通常的操作。
  • 我最终不需要在单独的BufferedImage
  • 中呈现任何内容

最终结果是UI很大,但除了Resizer之外的所有代码都认为UI是304 x 256。