Вопрос по java, swing, jframe, render, image-resizing – Как вы используете изменить размер всех Graphic2D

2

В 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);

Любая идея?

Вещи, которые я пробовал:

Рисуем всю графику в Buffered-image, а затем рисуем это изображение на экране.Просто используя SCALE и делая WIDTH * SCALE и т. Д.Много математики

Вещи, которые я не против

Если у вас есть ШИРОКИЙ ЭКРАН, он растягивает объекты graphic2D до размера.Если у вас есть КВАДРАТНЫЙ ЭКРАН, он подгоняет объекты graphics2D к размеру.

Итак, как я могу сделать полностью повторно закрываемую игру, используя Graphics2D, JFrame.

Lots of math + SCALE and doing WIDTH * SCALE + frame.setSize(WIDTH, HEIGHT); == для лучшей помощи, скорее отправьтеSSCCE/MCVE, короткий, запускаемый, компилируемый, но по умолчанию (методы вLayoutManager) ребенок вернет свой размер обратно в контейнер, тогда все будет (Lots of math + SCALE and doing WIDTH * SCALE) околоgetHeight/Weight mKorbel
@Dima Maligin Это делает его не таким эффективным, как некоторые другие методы. Но я нашел эффективный метод. Не стесняйтесь использовать его. Есть только проблемы при использовании этого метода. user4329506
рисунок кBufferedImage фиксированного размера, а затем вывести его на экран, какg.drawImage(image, 0, 0, screenWidth, screenHeight, null) не делает работу? Dima Maligin

Ваш Ответ

3   ответа
1

Graphics2d окружение, логическая система координат (координаты, которые вы используете в процедурах рисования) и физическая система координат (координаты в том виде, в котором они отображаются) на экране совершенно не связаны. Каждый раз, когда вы рисуете наGraphics2d объект, логические координаты сначала переводятся в физические координатыAffineTransform объект, и этоAffineTransform объект может быть изменен. Для этого вы можете использоватьGraphics2D.scale(double,double), Graphics2D.rotate(double), Graphics2D.translate(double,double) а такжеGraphics2D.shear(double,double) методы.

Так что если вы сначала позвоните

g2d.scale(2.0,2.0);

тогда вся ваша графика, которую вы впоследствии рисуете, будет в два раза больше в обоих направлениях.

Это работает довольно хорошо. Есть ли способ изменить алгоритм, в котором он отображается? Как один по качеству? Или это что Rendering Hints в графике? user4329506
@ Marco13 Должно быть, я думал о другом, потому что я верю, что ты прав. Тогда вопрос сводится к тому, в каком направлении вы будете масштабироваться (т.е. предпочитаете ли вы ширину или высоту цели), но это, вероятно, выходит за рамки вопроса MadProgrammer
@ Macro13 Конечно, если бы вы могли превратить его в ответ, который был бы полезен. Для базы данных и для справки. Также его RenderingHints, это именно то, что я думал. Спасибо всем за помощь, многие люди получают эту проблему. - Эллиот Бьюи user4329506
10

у графического программирования, а именно как преобразование измировые координаты вкоординаты экрана, У вас есть объект, который имеет размер "1,0 х 1,0" в вашей мировой системе координат (независимо от того, какойединица измерения это имеет). И этот объект должен быть нарисован так, чтобы он имел размер, например, «600 пикселей * 600 пикселей» на экране.

Вообще говоря, в Swing есть как минимум три варианта:

Вы можете нарисовать изображение, а затем нарисовать уменьшенную версию изображенияВы можете нарисовать в масштабеGraphics2D объектВы можете рисовать масштабированные объекты

У каждого из этого есть возможные преимущества и недостатки, и скрытые предостережения.

Рисование в изображение и масштабирование версии изображения:

Это может выглядеть как простое решение, но имеет потенциальный недостаток: само изображение имеет определенное разрешение (размер). Если изображение слишком маленькое, и вы увеличиваете его, чтобы заполнить экран, оно может выглядеть блочным. Если изображение слишком велико, и вы уменьшаете его, чтобы уместить на экране, пиксели изображения могут быть потеряны.

В обоих случаях есть несколько параметров настройки для процесса масштабирования изображения. На самом деле, масштабирование изображения гораздо сложнее, чем кажется на первый взгляд. За подробностями можно обратиться к статьеОпасности Image.getScaledInstance () Крис Кэмпбелл.

Рисование в масштабеGraphics2D объект

Graphics2D учебный класс уже предлагает полную функциональность, необходимую для создания преобразования междумировая система координат исистема координат экрана, Это достигается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 класс то, что потом,все будет масштабироваться. В частности, это масштабирование будеттакже влияет на ширину линии (то есть ширинуИнсульт). Например, рассмотрим последовательность вызовов, как этот:

// 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);

Второй вызов приведет к рисованию линии шириной 10 пикселей. Это может быть нежелательно во многих случаях. Этого эффекта можно избежать с помощью третьей альтернативы:

Рисование масштабированных объектов

Преобразование междумировая система координат исистема координат экрана можно также поддерживать вручную. Это удобно представлять какAffineTransform,AffineTransform класс может быть использован для создания преобразованных версийShape объект, который затем может быть нарисован непосредственно в (ООН-transformed)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 объекты) нарисованы. В каждой строке сравниваются три разных метода масштабирования, упомянутых выше. Имея размер «по умолчанию», объекты заполняют прямоугольник в мировых координатах размером 100x100. В первых двух строках они масштабируются, чтобы заполнить область на экране размером 190x190 пикселей. В последних двух строках они уменьшены, чтобы заполнить область на экране размером 60x60 пикселей. (Эти размеры были выбраны для того, чтобы иметь некоторые «нечетные» коэффициенты масштабирования 1,9 и 0,6. Некоторые эффекты (артефакты) могут не отображаться, когда коэффициентами масштабирования являются целые числа или, например, точно 0,5).

Для увеличения и уменьшения масштаба дополнительно проводится сравнение между «стандартным» способом рисования и «высококачественным» рисунком (обозначается «(HQ)» в заголовке каждой панели). «Высокое качество» здесь просто означает, что намеки на рендеринг

KEY_ANTIALIAS = VALUE_ANTIALIAS_ON
KEY_RENDERING = VALUE_RENDER_QUALITY

были установлены:

Вот соответствующая программа, как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));
            }
        }
    }
}
Удивительный ответ! user4329506
Java Q больше не получают достаточного внимания;) Спасибо за этот превосходный ответ! Florian Sesser
0

все, что вам нужно, это рисовать графику в разных разрешениях, не удаляя и не добавляя контент.

Ну, одна из «вещей, которые вы пробовали» может сделать это.

Рисование до фиксированного размераBufferedImage будет гарантировать, что все ваши компоненты видны в этом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();
            }
        });
    }

}

Это только быстрая реализация, есть вещи, которые можно сделать лучше в этом коде, но вы получите картину. Надеюсь это поможет.

Спасибо! Я буду использовать часть кода, который вы написали в будущих проектах. Это было в основном то, что я делал. Но это создало проблемы в будущем. user4329506

Похожие вопросы