你如何使用调整大小所有Graphics2D

问题描述 投票:2回答:3

在java中你怎么能让游戏完全实现!但是逻辑和图形可以用它吗?我尝试过使用SCALE方法。但这并不能让每台电脑完全全屏。所以我做了这个:

    public void resize(int WIDTH, int HEIGHT, boolean UNDECORATED) {

          frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
          frame.setMaximumSize(new Dimension(WIDTH, HEIGHT));
          frame.setMinimumSize(new Dimension(WIDTH, HEIGHT));

          this.WIDTH = WIDTH;
          this.HEIGHT = HEIGHT;
          frame.setUndecorated(UNDECORATED);
          frame.setSize(WIDTH, HEIGHT);
      }

因此,您可以将屏幕尺寸设置为您想要的任何尺寸!它工作但图形不适用它? Graphics2D中是否有一种方法可以拉伸所有图形以使其适合?例如,如果存在类似的方法:

            G2D.resize(WIDTH, HEIGHT, Image.NEAREST_PARENT_RESCALE);

任何的想法?

我尝试过的事情:

  • 将所有图形绘制到缓冲图像,然后将该图像绘制到屏幕尺寸上。
  • 只需使用SCALE并进行WIDTH * SCALE等
  • 很多数学

我不介意的事情

  • 如果你有一个WIDE-SCREEN,它会将graphic2D对象拉伸到这个大小。
  • 如果你有一个SQUARE-SCREEN,它会将graphics2D对象调整到这个大小。

那么如何使用Graphics2D,JFrame制作完全可重复密封的游戏。

java swing jframe render image-resizing
3个回答
12
投票

在最通用的形式中,人们可以将其视为图形编程的经典问题,即从世界坐标到屏幕坐标的转换。在世界坐标系中有一个大小为“1.0 x 1.0”的对象(无论它具有哪个单位)。并且应该对该对象进行绘制,使其在屏幕上具有例如“600像素×600像素”的大小。

从广义上讲,在Swing中至少有三种方法可以实现这一目标:

  • 您可以绘制成图像,然后绘制图像的缩放版本
  • 你可以绘制成缩放的Graphics2D对象
  • 您可以绘制缩放的对象

每一个都有可能的优点和缺点,以及隐藏的警告。

绘制图像,并绘制图像的缩放版本:

这可能看起来像一个简单的解决方案,但有一个潜在的缺点:图像本身具有一定的分辨率(大小)。如果图像太小,并且您要将其缩放以填充屏幕,则可能看起来很块。如果图像太大,并且您将其缩小以适合屏幕,则图像的像素可能会丢失。

在这两种情况下,缩放图像的过程都有几个调整参数。实际上,缩放图像远比第一眼看上去要复杂得多。有关详细信息,请参阅Chris Campbell撰写的文章The Perils of Image.getScaledInstance()

绘制成缩放的Graphics2D对象

Graphics2D class已经提供了在世界坐标系和屏幕坐标系之间创建转换所需的全部功能。这是由Graphics2D类通过内部存储AffineTransform来完成的,AffineTransform描述了这种转换。这个Graphics2D可以直接通过void paintSomething(Graphics2D g) { ... g.draw(someShape); // Everything that is painted after this line will // be painted 3 times as large: g.scale(3.0, 3.0); g.draw(someShape); // Will be drawn larger } 对象修改:

Graphics2D

必须注意正确管理存储在AffineTransform对象中的变换。通常,应该在应用其他转换之前创建原始// Create a backup of the original transform AffineTransform oldAT = g.getTransform(); // Apply some transformations g.scale(3.0, 4.0); g.translate(10.0, 20.0); // Do custom painting the the transformed graphics paintSomething(g): // Restore the original transformation g.setTransform(oldAT); 的备份,然后恢复此原始转换:

Graphics2D#setTransform

(对于最后一种方法的另一个建议:永远不应该使用Graphics2D方法在现有变换之上应用新的坐标变换。它仅用于恢复“旧”变换,如本示例所示(并在文档中)这种方法))。

使用Stroke类扩展的一个潜在缺点是,之后,所有内容都将被缩放。特别是,这种缩放也会影响线宽(即// By default, this will paint a line with a width (stroke) of 1.0: g.draw(someLine); // Apply some scaling... g.scale(10.0, 10.0); // Now, this will paint the same line, but with a width of 10. g.draw(someLine); 的宽度)。例如,考虑一下这样的调用序列:

AffineTransform

第二次调用将导致绘制一条宽10像素的线。在许多情况下可能不需要这样做。第三种选择可以避免这种影响:

绘制缩放的对象

世界坐标系和屏幕坐标系之间的转换也可以手动维护。将此表示为AffineTransform很方便。 Shape类可用于创建Graphics2D对象的转换版本,然后可以直接绘制到(未转换的)AffineTransform#createTransformedShape对象中。这是通过void paintSomething(Graphics2D g) { ... // Draw some shape in its normal size g.draw(someShape); // Create a scaling transform AffineTransform at = AffineTransform.getScaleInstance(3.0, 3.0); // Create a scaled version of the shape Shape transformedShape = at.createTransformedShape(someShape); // Draw the scaled shape g.draw(transformedShape); } 方法完成的:

Shape

这可能是最通用的方法。唯一的潜在缺点是,当绘制许多小而简单的形状时,这将导致产生许多小的临时变形形状,这可能导致性能降低。 (有一些方法可以缓解这个问题,但详细的性能考虑和优化超出了这个答案的范围)。


摘要

下图显示了所有方法的比较。绘制了一些示例对象(表示为KEY_ANTIALIAS = VALUE_ANTIALIAS_ON KEY_RENDERING = VALUE_RENDER_QUALITY 对象)。每行比较上面提到的三种不同的缩放方法。使用“默认”大小,对象将填充大小为100x100的世界坐标中的矩形。在前两行中,它们按比例放大以填充屏幕上190x190像素的区域。在最后两行中,它们按比例缩小以填充屏幕上60x60像素的区域。 (选择这些尺寸是为了使一些“奇数”缩放因子为1.9和0.6。当缩放因子是整数时,某些效果(伪像)可能不会出现,或者恰好为0.5)。

对于升级和降尺度,还有“标准”绘画方式和“高质量”绘画之间的比较(在每个小组的标题中用“(HQ)”表示)。这里的“高质量”仅仅意味着渲染提示

MCVE

已经设定:

这是相应的程序,作为import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class ScalingMethodComparison { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } private static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().setLayout(new GridLayout(0,1)); Dimension larger = new Dimension(190,190); Dimension smaller = new Dimension(60,60); f.getContentPane().add(createPanel(larger, false)); f.getContentPane().add(createPanel(larger, true)); f.getContentPane().add(createPanel(smaller, false)); f.getContentPane().add(createPanel(smaller, true)); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } private static JPanel createPanel(Dimension d, boolean highQuality) { JPanel p = new JPanel(new GridLayout(1,3)); for (ScalingMethodComparisonPanel.ScalingMethod scalingMethod : ScalingMethodComparisonPanel.ScalingMethod.values()) { p.add(createPanel(d, scalingMethod, highQuality)); } return p; } private static JPanel createPanel( Dimension d, ScalingMethodComparisonPanel.ScalingMethod scalingMethod, boolean highQuality) { JPanel p = new JPanel(new GridLayout(1,1)); p.setBorder(BorderFactory.createTitledBorder( scalingMethod.toString()+(highQuality?" (HQ)":""))); JPanel scalingMethodComparisonPanel = new ScalingMethodComparisonPanel( createObjects(), d, scalingMethod, highQuality); p.add(scalingMethodComparisonPanel); return p; } // Returns a list of objects that should be drawn, // occupying a rectangle of 100x100 in WORLD COORDINATES private static List<Shape> createObjects() { List<Shape> objects = new ArrayList<Shape>(); objects.add(new Ellipse2D.Double(10,10,80,80)); objects.add(new Rectangle2D.Double(20,20,60,60)); objects.add(new Line2D.Double(30,30,70,70)); return objects; } } class ScalingMethodComparisonPanel extends JPanel { private static final Color COLORS[] = { Color.RED, Color.GREEN, Color.BLUE, }; enum ScalingMethod { SCALING_IMAGE, SCALING_GRAPHICS, SCALING_SHAPES, } private final List<Shape> objects; private final ScalingMethod scalingMethod; private final boolean highQuality; private final Dimension originalSize = new Dimension(100,100); private final Dimension scaledSize; private BufferedImage image; public ScalingMethodComparisonPanel( List<Shape> objects, Dimension scaledSize, ScalingMethod scalingMethod, boolean highQuality) { this.objects = objects; this.scaledSize = new Dimension(scaledSize); this.scalingMethod = scalingMethod; this.highQuality = highQuality; } @Override public Dimension getPreferredSize() { return new Dimension(scaledSize); } @Override protected void paintComponent(Graphics gr) { super.paintComponent(gr); Graphics2D g = (Graphics2D)gr; g.setColor(Color.WHITE); g.fillRect(0,0,getWidth(), getHeight()); if (highQuality) { g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); } if (scalingMethod == ScalingMethod.SCALING_IMAGE) { paintByScalingImage(g); } else if (scalingMethod == ScalingMethod.SCALING_GRAPHICS) { paintByScalingGraphics(g); } else if (scalingMethod == ScalingMethod.SCALING_SHAPES) { paintByScalingShapes(g); } } private void paintByScalingImage(Graphics2D g) { if (image == null) { image = new BufferedImage( originalSize.width, originalSize.height, BufferedImage.TYPE_INT_ARGB); } Graphics2D ig = image.createGraphics(); paintObjects(ig, null); ig.dispose(); g.drawImage(image, 0, 0, scaledSize.width, scaledSize.height, null); } private void paintByScalingGraphics(Graphics2D g) { AffineTransform oldAT = g.getTransform(); double scaleX = (double)scaledSize.width / originalSize.width; double scaleY = (double)scaledSize.height / originalSize.height; g.scale(scaleX, scaleY); paintObjects(g, null); g.setTransform(oldAT); } private void paintByScalingShapes(Graphics2D g) { double scaleX = (double)scaledSize.width / originalSize.width; double scaleY = (double)scaledSize.height / originalSize.height; AffineTransform at = AffineTransform.getScaleInstance(scaleX, scaleY); paintObjects(g, at); } private void paintObjects(Graphics2D g, AffineTransform at) { for (int i=0; i<objects.size(); i++) { Shape shape = objects.get(i); g.setColor(COLORS[i%COLORS.length]); if (at == null) { g.draw(shape); } else { g.draw(at.createTransformedShape(shape)); } } } }

Graphics2d

1
投票

这在Java中实际上非常简单。在Graphics2d环境中,屏幕上的逻辑坐标系(您在绘图程序中使用的坐标)和物理坐标系(它们出现的坐标)完全不相关。每次绘制到AffineTransform对象时,逻辑坐标首先由AffineTransform对象转换为物理坐标,并且可以修改此Graphics2D.scale(double,double)对象。为此你可以使用Graphics2D.rotate(double)Graphics2D.translate(double,double)Graphics2D.shear(double,double)g2d.scale(2.0,2.0); 方法。

所以如果你第一次打电话

BufferedImage

那么你随后绘制的所有图形在两个方向上都将是两倍大。


0
投票

如果我理解你正确你想要的是在不删除或添加任何内容的情况下以不同的分辨率绘制图形。

其中一个“你尝试过的东西”可以做到这一点。

绘制到固定大小的BufferedImage将确保所有组件在import java.awt.Canvas; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class Test extends Canvas implements Runnable { // fixed size for the image private static final int WIDTH = 640; private static final int HEIGHT = 480; private BufferedImage image; private boolean running; private Thread t; public Test(Dimension dims) { super(); setPreferredSize(dims); // actual screen size image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); running = false; } public synchronized void start() { if (running) return; t = new Thread(this); running = true; t.start(); } public synchronized void stop() { if (!running) return; running = false; boolean retry = true; while (retry) { try { t.join(); retry = false; } catch (InterruptedException e) { e.printStackTrace(); } } } private void render() { // draw to your image Graphics2D g2d = (Graphics2D) image.getGraphics().create(); g2d.fillRect((WIDTH / 2) - 25, (HEIGHT / 2) - 25, 50, 50); g2d.dispose(); // draw the image to your screen BufferStrategy bs = getBufferStrategy(); if (bs == null) { createBufferStrategy(3); return; } g2d = (Graphics2D) bs.getDrawGraphics().create(); g2d.drawImage(image, 0, 0, getWidth(), getHeight(), null); g2d.dispose(); bs.show(); } public void run() { // approximately sync rendering to 60 FPS don't use it as it is. // there are much better ways to do this. long startTime = System.currentTimeMillis(); long frameTime = 1000 / 60; long tick = 0; while (running) { while ((System.currentTimeMillis() - startTime) > tick) { render(); tick += frameTime; } try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Test test = new Test(new Dimension(800, 600)); JFrame frame = new JFrame("Fit to screen"); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { test.stop(); frame.dispose(); super.windowClosing(e); } }); frame.getContentPane().add(test); frame.pack(); frame.setLocationRelativeTo(null); frame.setResizable(false); frame.setVisible(true); SwingUtilities.invokeLater(new Runnable() { public void run() { test.start(); } }); } } 中可见(假设您正确地绘制它们并且相对于它的固定大小)然后您可以将图像绘制到灵活大小的屏幕。

这是一个完整的可运行代码示例:

qazxswpoi

这只是一个快速实现,有些事情可以在代码中更好地完成。希望这可以帮助。

© www.soinside.com 2019 - 2024. All rights reserved.