8
\$\begingroup\$

I'm sorry if I have not worded the question title to well, so let me explain.

I am creating a game, actually a few and I see that most of the time when painting ect I use a JPanel, so I went and found a good method of painting (i.e it has great performance when multiple sprites etc are being painted) see here: https://stackoverflow.com/questions/1963494/java-2d-game-graphics. I implemented that into my own kind of GameJPanel which will allow me to always have performance when painting on my games JPanels. Now to that I have added the methods like setDoubleBuffered(true) and RenderHints on when drawing:

                if (isTextRenderHintsOn()) {
                    bg.setRenderingHints(textRenderHints);
                }
                if (isImageRenderHintsOn()) {
                    bg.setRenderingHints(imageRenderHints);
                }
                if (isColorRenderHintsOn()) {
                    bg.setRenderingHints(colorRenderHints);
                }
                if (isInterpolationRenderHintsOn()) {
                    bg.setRenderingHints(iterpolationRenderHints);
                }
                if (isRenderHintsOn()) {
                    bg.setRenderingHints(renderHints);
                }

now without going to other libraries etc what more can I do to adjust the quality and or performance of whats being drawn on the JPanels?

It might also help to know that I use the GraphicsEnviroment and set the JFrame with the JPanel to full screen to gain a hardware accelerated image via a Buffer Strategy (not the linked performance drawing in swing also used some great techniques as is) below is an example of my GameDrawLibrary which implements the Java2D game graphics links' method and the rendering hints, the FullScreen class will be used for setting to and from full screen mode and the Test driver is just that:) :

Test Driver:

import java.awt.BorderLayout;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 * Test Driver
 *
 * @author David
 */
public class PureSwingPaintingTest extends JFrame {

    static GameDrawLibrary gdl;
    static Image image;
    static final FullScreen fs = new FullScreen();
    static int width = fs.getWidth(), height = fs.getHeight();

    public PureSwingPaintingTest() {
        setSize(width, height);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gdl.createPanel();//create the panel
        JPanel panel = gdl.getPanel();
        JButton button = new JButton("Exit");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        getContentPane().add(button, BorderLayout.SOUTH);
        getContentPane().add(panel, BorderLayout.CENTER);
    }

    public static void main(String[] args) {

        try {
            image = ImageIO.read(new File("c:/chess.jpg")).getScaledInstance(width, height, Image.SCALE_SMOOTH);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        gdl = new GameDrawLibrary(width, height, true, true, true, true, true, true) {

            @Override
            void updateGame() {
                //update logic here
            }

            @Override
            void renderGame(Graphics2D g) {
                g.drawImage(image, 0, 0, this);
            }
        };

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {

                if (fs.isSupported()) {
                    fs.setFullScreen(new PureSwingPaintingTest());
                } else {
                    fs.emulateFullScreen(new PureSwingPaintingTest());
                }

                gdl.showPanel();
            }
        });
    }
}

Game JPanel library of sorts:

import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;

abstract class GameDrawLibrary extends JPanel {

    private int width;
    private int height;
    private int scale;
    private boolean isRunning = true;
    private Canvas canvas;
    private BufferStrategy strategy;
    private BufferedImage background;
    private Graphics2D backgroundGraphics;
    private Graphics2D graphics;
    private GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    //graphics quaultiy settings
    private boolean textRenderHintsOn;
    private boolean imageRenderHintsOn;
    private boolean colorRenderHintsOn;
    private boolean interpolationRenderHintsOn;
    private boolean renderHintsOn;
    private RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    private RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    private RenderingHints colorRenderHints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
    private RenderingHints iterpolationRenderHints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    private RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

    public GameDrawLibrary() {
        setIgnoreRepaint(true);
        this.textRenderHintsOn = false;
        this.imageRenderHintsOn = false;
        this.colorRenderHintsOn = false;
        this.interpolationRenderHintsOn = false;
        this.renderHintsOn = false;
    }

    GameDrawLibrary(int width, int height) {
        setIgnoreRepaint(true);
        this.width = width;
        this.height = height;
        this.scale = 1;
        this.textRenderHintsOn = false;
        this.imageRenderHintsOn = false;
        this.colorRenderHintsOn = false;
        this.interpolationRenderHintsOn = false;
        this.renderHintsOn = false;

    }

    GameDrawLibrary(int width, int height, int scale) {
        setIgnoreRepaint(true);
        this.width = width;
        this.height = height;
        this.scale = scale;
        this.textRenderHintsOn = false;
        this.imageRenderHintsOn = false;
        this.colorRenderHintsOn = false;
        this.interpolationRenderHintsOn = false;
        this.renderHintsOn = false;
    }

    public GameDrawLibrary(int width, int height, boolean textRenderHintsOn, boolean imageRenderHintsOn, boolean colorRenderHintsOn, boolean interpolationRenderHintsOn, boolean renderHintsOn, boolean isDoubleBuffered) {
        super(isDoubleBuffered);
        setIgnoreRepaint(true);
        this.width = width;
        this.height = height;
        this.scale = 1;
        this.textRenderHintsOn = textRenderHintsOn;
        this.imageRenderHintsOn = imageRenderHintsOn;
        this.colorRenderHintsOn = colorRenderHintsOn;
        this.interpolationRenderHintsOn = interpolationRenderHintsOn;
        this.renderHintsOn = renderHintsOn;
    }

    public GameDrawLibrary(int width, int height, int scale, boolean textRenderHintsOn, boolean imageRenderHintsOn, boolean colorRenderHintsOn, boolean interpolationRenderHintsOn, boolean renderHintsOn, boolean isDoubleBuffered) {
        super(isDoubleBuffered);
        this.width = width;
        this.height = height;
        this.scale = scale;
        this.textRenderHintsOn = textRenderHintsOn;
        this.imageRenderHintsOn = imageRenderHintsOn;
        this.colorRenderHintsOn = colorRenderHintsOn;
        this.interpolationRenderHintsOn = interpolationRenderHintsOn;
        this.renderHintsOn = renderHintsOn;
    }
    // Screen and buffer stuff

    private Graphics2D getBuffer() {
        if (graphics == null) {
            try {
                graphics = (Graphics2D) strategy.getDrawGraphics();
            } catch (IllegalStateException e) {
                return null;
            }
        }
        return graphics;
    }

    private boolean updateScreen() {
        graphics.dispose();
        graphics = null;
        try {
            strategy.show();
            Toolkit.getDefaultToolkit().sync();
            return (!strategy.contentsLost());

        } catch (NullPointerException | IllegalStateException e) {
            return true;

        }
    }

    // update game logic here
    abstract void updateGame();

    //paint stuff here
    abstract void renderGame(Graphics2D g);

    //create and return the already sized JFrame
    public void createPanel() {
        setSize(getRealWidth(), getRealHeight());
    }

    public void showPanel() {
        // Canvas
        canvas = new Canvas(config);
        canvas.setSize(width * scale, height * scale);
        add(canvas, 0);
        // Background & Buffer
        background = create(width, height, false);
        canvas.createBufferStrategy(2);
        do {
            strategy = canvas.getBufferStrategy();
        } while (strategy == null);

        startThread();//start thread to draw to the canvas
    }

    private void startThread() {
        new Thread(new Runnable() {

            @Override
            public void run() {

                backgroundGraphics = (Graphics2D) background.getGraphics();
                long fpsWait = (long) (1.0 / 30 * 1000);

                main:
                while (isRunning) {
                    long renderStart = System.nanoTime();
                    updateGame();

                    // Update Graphics
                    do {
                        Graphics2D bg = getBuffer();

                        if (isTextRenderHintsOn()) {
                            bg.setRenderingHints(textRenderHints);
                        }
                        if (isImageRenderHintsOn()) {
                            bg.setRenderingHints(imageRenderHints);
                        }
                        if (isColorRenderHintsOn()) {
                            bg.setRenderingHints(colorRenderHints);
                        }
                        if (isInterpolationRenderHintsOn()) {
                            bg.setRenderingHints(iterpolationRenderHints);
                        }
                        if (isRenderHintsOn()) {
                            bg.setRenderingHints(renderHints);
                        }

                        if (!isRunning) {
                            break main;
                        }
                        renderGame(backgroundGraphics); // this calls your draw method
                        // thingy
                        if (scale != 1) {
                            bg.drawImage(background, 0, 0, width * scale, height * scale, 0, 0, width, height, null);
                        } else {
                            bg.drawImage(background, 0, 0, null);
                        }
                        bg.dispose();
                    } while (!updateScreen());

                    // Better do some FPS limiting here
                    long renderTime = (System.nanoTime() - renderStart) / 1000000;
                    try {
                        Thread.sleep(Math.max(0, fpsWait - renderTime));
                    } catch (InterruptedException e) {
                        Thread.interrupted();
                        break;
                    }
                    // renderTime = (System.nanoTime() - renderStart) / 1000000;

                }

            }
        }).start();
    }

    public JPanel getPanel() {
        return this;
    }

    // create a hardware accelerated image
    public final BufferedImage create(final int width, final int height, final boolean alpha) {
        return config.createCompatibleImage(width, height, alpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
    }

    public void setColorRenderHintsOn(boolean colorRenderHintsOn) {
        this.colorRenderHintsOn = colorRenderHintsOn;
    }

    public void setRenderHintsOn(boolean renderHintsOn) {
        this.renderHintsOn = renderHintsOn;
    }

    public void setInterpolationRenderHintsOn(boolean interpolationRenderHintsOn) {
        this.interpolationRenderHintsOn = interpolationRenderHintsOn;
    }

    public void setImageRenderHintsOn(boolean imageRenderHintsOn) {
        this.imageRenderHintsOn = imageRenderHintsOn;
    }

    public void setTextRenderHintsOn(boolean textRenderHintsOn) {
        this.textRenderHintsOn = textRenderHintsOn;
    }

    public boolean isColorRenderHintsOn() {
        return colorRenderHintsOn;
    }

    public boolean isInterpolationRenderHintsOn() {
        return interpolationRenderHintsOn;
    }

    public boolean isTextRenderHintsOn() {
        return textRenderHintsOn;
    }

    public boolean isImageRenderHintsOn() {
        return imageRenderHintsOn;
    }

    public boolean isRenderHintsOn() {
        return renderHintsOn;
    }

    public int getRealHeight() {
        return height * scale;

    }

    public int getRealWidth() {
        return width * scale;
    }

    @Override
    public int getHeight() {
        return height;
    }

    @Override
    public int getWidth() {
        return width;
    }

    public int getScale() {
        return scale;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setScale(int scale) {
        this.scale = scale;
    }
}

Fullscreen class:

import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import javax.swing.JFrame;

/**
 *
 * @author David
 */
public class FullScreen {

    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gs = ge.getDefaultScreenDevice();
    Dimension screenSize;

    public FullScreen() {
        screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    }

    public void setFullScreen(JFrame frame) {
        if (gs.getFullScreenWindow() == null) {

            frame.dispose();
            frame.setUndecorated(true);
            gs.setFullScreenWindow(frame);
            frame.setVisible(true);

        } else {// back to windowed mode
            emulateFullScreen(frame);
        }
    }

    public void emulateFullScreen(JFrame frame) {
        frame.dispose();
        frame.setUndecorated(false);
        gs.setFullScreenWindow(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(screenSize);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public int getWidth() {
        return gs.getDisplayMode().getWidth();

    }

    public int getHeight() {
        return gs.getDisplayMode().getHeight();
    }

    public boolean isSupported() {
        return gs.isFullScreenSupported();
    }
}
\$\endgroup\$
9
  • \$\begingroup\$ I may be wrong, but this seems more of a code-review type question than a specific single answer stackoverflow type question. \$\endgroup\$ – Hovercraft Full Of Eels Aug 8 '12 at 14:18
  • \$\begingroup\$ can you put a picture? \$\endgroup\$ – tuğrul büyükışık Aug 8 '12 at 14:20
  • \$\begingroup\$ @HovercraftFullOfEels Not really? I want to know what other ways the painting quality can be improved? maybe I'm missing something that can make quality better so I put specific code so that others may see what I have already impelemented for quality drawing? \$\endgroup\$ – David Kroukamp Aug 8 '12 at 14:20
  • \$\begingroup\$ a picture could be more readable than lots of code \$\endgroup\$ – tuğrul büyükışık Aug 8 '12 at 14:22
  • \$\begingroup\$ @tuğrulbüyükışık I havent really got anything to compare it to as I havent implemented the drawing etc fully (I just use a simple Image for now) but I'd like to know besides the techniques already used how else can quality be improved \$\endgroup\$ – David Kroukamp Aug 8 '12 at 14:22
6
\$\begingroup\$

It's a rather interesting piece of code, I'm going to enjoy reading it further when I have more time ;)

About the only things that jump out at me is how you are scaling your graphics. I'd suggest having a read through http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html as it discuss getScaledInstance and some alternative algorithms.

That aside, I was told, some years ago, you can fake anti aliasing by scaling a image down by 4. That is, if you want an output of a image at 800*600, you need to start with an image 4 times that size and scaling it down, which will produce a "fake" anti aliasing that does not rely on the hardware to render.

This may no longer hold true, but it might be of some worth to you

\$\endgroup\$
1
  • \$\begingroup\$ +1 Thank you...ah i always forget about the scaling downfalls. And thank you for the tips i might give it a try. \$\endgroup\$ – David Kroukamp Aug 8 '12 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.