スキップしてメイン コンテンツに移動

Render FPS Text on Java Swing Component

In the previous post, I showed helper class for 2D animation on Java Swing component.
If we can measure the frame rate of animation, it is very great, isn't it?
In this post, I will show you that kind of class - rendering FPS text on swing component.
Showing code is fast and snappy :p Here is the code - count frame per 1 sec.
package com.dukesoftware.utils.awt.graphics;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.text.DecimalFormat;

public class FPSText {
    protected static final String FPS = "FPS: ";
    private static final int FONT_SIZE = 24;
    private static final int FONT_STYLE = Font.BOLD;
    private static final String FONT_NAME = "SansSerif";
    
    protected final static Color COLOR = new Color(0.55f,0.55f,0.55f);
    protected double frameCount;
    protected final DecimalFormat format = new DecimalFormat("####.00");
    protected String fpsText;
    protected final int fpsWidth;
    protected long startTime;
    protected Font font;
    protected final Component comp;
    protected int wpos;
    
    public FPSText(Component comp){
        this(comp, new Font(FONT_NAME, FONT_STYLE, FONT_SIZE));
    }
    
    public FPSText(Component comp, Font font){
        this.comp = comp;
        this.font = font;
        fpsText = FPS + format.format(0);
        fpsWidth = font.getSize()*(format.getMaximumFractionDigits()+FPS.length()) + 5;
        setStartTime();
        wpos = comp.getWidth() - fpsWidth;
    }
    
    public void setStartTime(){
        startTime = System.currentTimeMillis();
    }

    public void paint(Graphics g) {
        ++frameCount;
        final long currentTimeMillis = System.currentTimeMillis();
        final long span = currentTimeMillis - startTime;
        if (span > 1000) {
            fpsText = FPS + format.format(frameCount*1000 / span);
            frameCount = 0;
            startTime = currentTimeMillis;
        }
        g.setFont(font);
        g.setColor(COLOR);
        g.drawString(fpsText, comp.getWidth() - fpsWidth, 30);
    }
    
}
But wait! the above FPSText class is sometimes not suitable for rendering heavy animation because measuring time does not become so accurate.
The below is differnet version - counting 100 frame and divide it by time elapsed (In my experience, 100 frame is too much aroud 30 frame is better).
package com.dukesoftware.utils.awt.graphics;

import java.awt.Component;
import java.awt.Graphics;

public class FPSText2 extends FPSText{
    public FPSText2(Component comp){
        super(comp);
    }
    
    public void paint(Graphics g) {
        if (++frameCount >= 100) {
            fpsText = FPS + format.format(100000.0f / (System.currentTimeMillis() - startTime));
            frameCount = 0;
            startTime = System.currentTimeMillis();
        }
        g.setFont(font);
        g.setColor(COLOR);
        g.drawString(fpsText, comp.getWidth() - fpsWidth, 30);
    }
    
}

I embed the FPS text class on BezierAnim class.
After embedding, you can see the fps text on right corner...

package com.dukesoftware.utils.swing.others;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.JComponent;
import javax.swing.Timer;

import com.dukesoftware.utils.awt.graphics.FPSText;
import com.dukesoftware.utils.swing.graphics2d.Model2D;

public class AnimateCanvas extends JComponent implements ComponentListener, ActionListener{
    private int width, height;
    private final Timer animator2D;
    private final Model2D model2D;
    private final FPSText text;

    public AnimateCanvas(Model2D model2D, int width, int height, int fps) {
        this.model2D = model2D;
        this.width = width;
        this.height = height;
        this.animator2D = new Timer(1000/fps, this);
        model2D.init(width, height);
        this.text = new FPSText(this);
        setPreferredSize(new Dimension(width, height));
        addComponentListener(this);
    }
    
    public void stop() {
        animator2D.stop();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        model2D.step(width, height);
        model2D.render(width, height, g2);
        this.text.paint(g2);
        g2.dispose();
    }
    
    public void start() {
        animator2D.start();
    }

    public void componentHidden(ComponentEvent e) {
    }

    public void componentMoved(ComponentEvent e) {
        
    }

    public void componentResized(ComponentEvent e) {
        width = getWidth();
        height = getHeight();
    }

    public void componentShown(ComponentEvent e) {
        
    }

    public void actionPerformed(ActionEvent e) {
        repaint();
    }

}

コメント