11

I would like to know how I would handle pixel operations best in Java. I'm using swing and drawing a rectangle of 1 by 1 pixel is terribly slow. I need to get 60 fps at least, without using too much resources. Would blitting to an image first mean this could be archieved succesfully? Or is it a bad idea in general to do this with Java and do I need to stick to C or another alternative?

I'm in the beginning of writing a raycaster and since openCL, which I'm using, has a wrapper for Java, I prefer working in Java.

user229044
  • 232,980
  • 40
  • 330
  • 338
RobotRock
  • 4,211
  • 6
  • 46
  • 86
  • 1
    I'd suggest reading up on the concept of Intermediate Images. Here's an example - http://www.java2s.com/Code/Java/Advanced-Graphics/IntermediateImages.htm – mre Jun 25 '11 at 15:45
  • I think the technique Intermidiate Images is along the lines I described; blitting an image. However I'm not familiar with the speedresults of this in Java, which is what I want to know. – RobotRock Jun 25 '11 at 16:04
  • This technique wont get 60fps results in Java, still way too slow. – RobotRock Jun 25 '11 at 22:24
  • I'm not sure how these comments here go together with the chosen answer below. The _Intermediate Image_ reference suggested by @mre deploys `BufferedImage` just like the answer by @Devon_C_Miller does. And it's really hard to imagine that you can't draw a 1 x 1 px rect with 60fps. – class stacker May 05 '15 at 09:36

2 Answers2

5

Adding to @camickr's suggestion:

Create a BufferedImage (BI), wrap it in a IconImage, set it as the icon for a JLabel. Paint changes onto the BI and call JLabel's repaint() to flush those. changes to the screen. Caching the partial image in a BI lessens the amount of work for the paint routine. It just needs to blit the image, not render the image. Use SwingWorkers to deploy background threads to run the calculations and pass the results back to the EDT for painting.

As long as you're talking about a static image, that will work fine. If you are considering something more like a video game (some fixed images and other moving images) take a look at VolatileImage. There is a good description here: http://gpwiki.org/index.php/Java:Tutorials:VolatileImage

Update:

The following gives me a bit over 80 fps:

public class Demo extends javax.swing.JPanel {
    private Image src = null;
    public Demo() {
        new Worker().execute();
    }
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (src != null) g.drawImage(src, 0, 0, this);
    }
    private class Worker extends SwingWorker<Void, Image>{
        private final Color[] colors = { Color.red, Color.green, Color.blue };
        protected void process(List<Image> chunks){
            for (Image bufferedImage : chunks){
                src = bufferedImage;
                repaint();
            }
        }
        protected Void doInBackground() throws Exception{
            int frames = 0;
            int[] mem = new int[1024 * 768];
            long start = System.currentTimeMillis();
            long end = start + 15000;
            long last = start;
            while (last < end){
                int col = colors[frames % colors.length].getRGB();
                for (int y = 0; y < 768; y++)
                    for (int x = 0; x < 1024; x++)
                        mem[x + y * 1024] = col;
                Image img = createImage(new MemoryImageSource(1024, 768, mem, 0, 1024));
                BufferedImage bi = new BufferedImage(1024, 768, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2 = bi.createGraphics();
                g2.drawImage(img, 0, 0, null);
                g2.dispose();
                publish(bi);
                last = System.currentTimeMillis();
                frames++;
            }
            System.err.println("Frames = " + frames + ", fps = " + ((double) frames / (last - start) * 1000));
            return null;
        }
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run(){
                JFrame jf = new JFrame();
                jf.getContentPane().add(new Demo(), BorderLayout.CENTER);
                jf.setSize(1024, 768);
                jf.setVisible(true);
            }
        });
    }
}
Devon_C_Miller
  • 16,248
  • 3
  • 45
  • 71
  • I get 10fps filling the screen with pixels on a 1024x768 image. Any idea how to speed this up? I'm using the technique like you said and running a while true loop for the rendering. – RobotRock Jun 27 '11 at 13:01
  • What kind of performance do you get if you just render to the BI and don't actually paint it to the screen? Typically the bottlenecks in ray tracing are more in the calculations than the screen updates. Try just outputting System.currentTimeMillis() each time you complete a screen's worth of calculations. – Devon_C_Miller Jun 27 '11 at 22:11
  • I'm drawing noncalculated pixels, so just plain filling the screen pixel by pixel. At 320x240 I get 100+ fps, at 1024x768 it drops to 10. If I leave out repaint(), it wont change the results. – RobotRock Jun 28 '11 at 13:32
  • Awesome! Thank you VERY much. Dont know yet what the major difference is; it'll probably be the use of a SwingWorker, I guess. – RobotRock Jul 02 '11 at 18:17
  • A note: mem[i * 768 + j] = col needs to be mem[i + j * 1024] = col I think and isnt the fps to be calculated in process? – RobotRock Jul 02 '11 at 19:51
  • 2
    Exchange the order of the x and y loops to make this much faster (better locality). – Ben Voigt Jul 03 '11 at 01:40
  • Good catch. I updated the indices to be x and y and corrected the array indexing. The biggest speed up is from the use of MemoryImageSource. The writes into the array are all primitive operations, so there is no function call overhead. It replaces over 780K function calls with 5 calls. – Devon_C_Miller Jul 03 '11 at 01:47
  • Thanks @Ben Voight. I've updated the code. Reversing the order allows the intermediate result of y*1024 to be cached across the inner loop. – Devon_C_Miller Jul 03 '11 at 01:52
  • @Devon: Not only can that subexpression be optimized, the memory access pattern is much better. Now it runs `mem[0], mem[1], mem[2]` (which together require only one transfer from RAM to cache) where before it was `mem[0], mem[1024], mem[2048]` (which each require a separate cache load) – Ben Voigt Jul 03 '11 at 02:30
  • For the record: I get `Frames = 7187, fps = 479.10139324045065` running the above code on Windows 10 Pro (64 Bit), jre1.8.0_66, Xeon E3-1230 v3 @ 3.30GHz and 16 GB memory. – Jack Dec 29 '15 at 18:44
4

Use a BufferedImage and the setRGB(...) method. Then you draw the entire image in your paint routine.

camickr
  • 321,443
  • 19
  • 166
  • 288