Java 将微小的低分辨率应用程序扩展到更大的屏幕大小

Java 将微小的低分辨率应用程序扩展到更大的屏幕大小,java,swing,awt,Java,Swing,Awt,我正在用Java创建一个复古街机游戏。游戏的屏幕分辨率是304 x 256,我想保留它以保留游戏的复古特征(视觉效果、动画、字体块状等) 但当我在一个大的桌面显示器上渲染时,它太小了,正如人们所期望的那样 我希望能够放大窗口,比如通过一个常数因子,而不必编写各种paint(Graphics)方法来了解放大的事实。也就是说,我希望渲染代码相信屏幕是304 x 256。我也不想改变我的桌面分辨率或进入全屏独占模式。只需要一个放大像素的大窗口 我会按照以下思路寻找一些东西: scale(myJFram

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

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

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

我会按照以下思路寻找一些东西:

scale(myJFrame, 4);
并让所有内容自动放大

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

建议的一种方法是依靠
drawImage()
来缩放内容的图像。游戏将在
buffereImage
的图形上下文中呈现,而不是在
paintComponent()的实现中呈现。如果游戏包含鼠标交互,则必须缩放鼠标坐标,如图所示。在下面的变体中,我给了
中心
面板一个首选大小,它是
比例=8的倍数
,并在
边界布局的
西部
添加了原始图标。默认情况下,
CENTER
会忽略零部件的首选尺寸,您可能希望将其添加到具有
FlowLayout
的(可能是嵌套的)面板中。调整框架大小以查看效果

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

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

导入java.awt.Color;
导入java.awt.Component;
导入java.awt.Dimension;
导入java.awt.FlowLayout;
导入java.awt.Font;
导入java.awt.FontMetrics;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.Point;
导入java.awt.RenderingHints;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入java.awt.event.MouseMotionAdapter;
导入java.awt.geom.AffineTransform;
导入java.awt.geom.NoninvertibleTransformException;
导入java.awt.image.buffereImage;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
/**@见https://stackoverflow.com/a/2244285/230513 */
公共类反变换{
专用静态最终双刻度=2.0;
公共静态void main(字符串[]args){
JFrame=新JFrame(“反向测试”);
BuffereImage=getImage(256,'F');
AffineTransform at=新的AffineTransform();
比例(比例,比例);
frame.add(新的ImageView(image,at));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
私有静态缓冲区映像getImage(整数大小,字符c){
最终字体=新字体(“衬线”,Font.BOLD,size);
BuffereImage bi=新的BuffereImage(
大小、大小、缓冲区映像。键入\ u INT \ u ARGB);
Graphics2D g2d=bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_抗锯齿,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(颜色:白色);
g2d.fillRect(0,0,size,size);
g2d.setPaint(颜色:蓝色);
g2d.setFont(字体);
FontMetrics fm=g2d.getFontMetrics();
intx=(size-fm.charWidth(c))/2;
int y=fm.getAscent()+fm.getDescent()/4;
g2d.抽绳(串.值(c),x,y);
g2d.setPaint(颜色:黑色);
g2d.绘制线(0,y,尺寸,y);
g2d.抽绳(x,0,x,尺寸);
g2d.椭圆形(x-3,y-3,6,6);
g2d.drawRect(0,0,大小-1,大小-1);
g2d.dispose();
返回bi;
}
私有静态类ImageView扩展了JPanel{
私有缓冲图像;
私有仿射变换at;
私有仿射变换逆;
私有图形2D画布;
专用点oldPt=新点();
私人点新港;
@凌驾
公共维度getPreferredSize(){
返回新维度(//比例的任意倍数
(int)(image.getWidth()*比例*1.25),
(int)(image.getHeight()*比例*1.25));
}
@凌驾
公共组件(图形g){
超级组件(g);
Graphics2D g2d=(Graphics2D)g;
试一试{
g2d.setRenderingHint(RenderingHints.KEY_抗锯齿,
RenderingHints.VALUE_ANTIALIAS_ON);
inverse=g2d.getTransform();
反转();
g2d.translate(this.getWidth()/2,this.getHeight()/2);
g2d.变换(at);
translate(-image.getWidth()/2,-image.getHeight()/2);
concatenate(g2d.getTransform());
g2d.drawImage(图像,0,0,this);
}捕获(不可逆转换异常){
例如printStackTrace(System.err);
}
}
图像视图(最终缓冲区图像图像、最终仿射变换){
本图为背景色(颜色:浅灰色);
这个图像=图像;
this.at=at;
T
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;

/**
 * @see https://stackoverflow.com/a/44373975/230513
 * @see http://stackoverflow.com/questions/2900801
 */
public class Grid extends JPanel implements MouseMotionListener {

    private static final String NAME = "OptionPane.informationIcon";
    private static final Icon ICON = UIManager.getIcon(NAME);
    private static final int SCALE = 8;
    private final BufferedImage image;
    private int imgW, imgH, paneW, paneH;

    public Grid(String name) {
        super(true);
        imgW = ICON.getIconWidth();
        imgH = ICON.getIconHeight();
        image = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D) image.getGraphics();
        ICON.paintIcon(null, g2d, 0, 0);
        g2d.dispose();
        this.addMouseMotionListener(this);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(imgW * SCALE, imgH * SCALE);
    }

    @Override
    protected void paintComponent(Graphics g) {
        paneW = this.getWidth();
        paneH = this.getHeight();
        g.drawImage(image, 0, 0, paneW, paneH, null);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        Point p = e.getPoint();
        int x = p.x * imgW / paneW;
        int y = p.y * imgH / paneH;
        int c = image.getRGB(x, y);
        this.setToolTipText(x + "," + y + ": "
            + String.format("%08X", c));
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    private static void create() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new Grid(NAME));
        f.add(new JLabel(ICON), BorderLayout.WEST);
        f.pack();
        f.setVisible(true);
    }

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

            @Override
            public void run() {
                create();
            }
        });
    }
}
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();
                    }
                }
            });
        }
    }
}
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);
    }
}