0

I have a JScrollPane displaying (as its viewport view) MyPanel, a subclass of JPanel.

MyPanel implements custom painting by overloading paintComponent. The total size of the displayable content of MyPanel is generally quite wide (meaning 50x to 200x wider than the size of the JScrollPane viewport) and using a Timer, I scroll horizontally to view different sections of the underlying MyPanel. I also allow using the scroll bar thumb to manually seek to a specific area of MyPanel.

In my paintComponent implementation I am currently finding the portion of MyPanel that is currently visible in the view port using JViewport#getVisibleRect, and just painting that portion each time the view port position is changed.

This works fine - but I end up repainting a significant percentage of the visible portion of MyPanel over and over as the timed scrolling only moves the view port 1/50 of the view port width at a time. Also, I generally end up scrolling through the entire horizontal extent of MyPanel, so I have to paint it all at least once anyway.

That leads me to think about painting the entire contents of MyPanel just once (to a BufferedImage?) and then letting JScrollPane (or JViewport) handle clipping and blitting only the needed area of the BufferedImage.

Intuitively this seems to me to be the most efficient way of handling this, and something that would be relatively common.

As I investigate the Swing tutorials and other sources, I learn that Swing is already double buffered. If I try to force this on my own brute-force, independent of Swing functionality, it sounds like I'll end up with triple-buffering.

I haven't found the recipe (if it exists) to exploit JScrollPane to do this for me.

Is there an example available, or some direction as to how to do this (if possible)?

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
ags
  • 719
  • 7
  • 23
  • 1
    `JViewPort` is already double buffered (over the default `JComponent` double buffering). Have you looked at the `Graphics#getClip` value to see what it is actually painting? The other thing you could do is maintain some kind of reference to the view area you last painted and compare it to the new one. If you haven't already done so, I would take a look through [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html) – MadProgrammer Jan 24 '13 at 00:45
  • Additionally, if `MyPanel` is nothing by a static component (ie it has no components on to which the user can interact), painting it a `BufferedImage` is isn't an unrealistic prospect. The only issue I might be concerned with is the potential size of the `BufferedImage` and the available memory. But that may not be a concern for you in the case. – MadProgrammer Jan 24 '13 at 01:09
  • @MadProgrammer There are many interactions with MyPanel (key bindings, mouse input). Does that mean BufferedImage is not an option? – ags Jan 24 '13 at 01:59
  • Are you using any `JComponents` (like buttons or text fields)? Or are you managing the mouse and key events your self? – MadProgrammer Jan 24 '13 at 02:02
  • @MadProgrammer I have read the reference above 'Painting in AWT and Swing' and have extracted all the content I can from it (good suggestion though). It was there that I learned of the default double-buffering. Basically what I would like to do is render the entire content (it is static) *somewhere* once, and have Swing handle selecting the visible area and mapping it to the screen. Is that even reasonable? – ags Jan 24 '13 at 02:03
  • @MadProgrammer I have no other components contained in MyPanel. I am managing all the interaction (mouse and key input) myself. – ags Jan 24 '13 at 02:04
  • That should be fine. I would simple use `MyPanel` as a painting surface to paint that buffer onto in that case... – MadProgrammer Jan 24 '13 at 02:04
  • @MadProgrammer Can you point me to any example of that? I'm not clear on how to "connect" the BufferedImage to the Viewport/JScrollPane. – ags Jan 24 '13 at 02:06
  • Paint directly to `MyPanel` within the `paintComponent` method itself. Faster to render a image then lots of individual paint commands... – MadProgrammer Jan 24 '13 at 02:09
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23266/discussion-between-ags-and-madprogrammer) – ags Jan 24 '13 at 02:35

1 Answers1

0

Swing automatically paints only the smallest necessary area of components for you. repaint(4 args) is only useful when the component is partially changed and you don't want the entire visible area to be repainted. And in your practical, it has the same effect as repaint(no-args).

The auto-clipped area is already small enough for visibility concerns as you described in your question. And you can configure it in your program.

Also, you don't need to worry about the scrolling -- JScrollPane invokes repaint of its children automatically.

You can easily experiment on these:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Test extends JFrame {

    private Random rnd = new Random();
    private Color c = Color.WHITE;

    public Test () {
        final JPanel pnl = new JPanel() {

            @Override
            public void paintComponent (Graphics g) {
                super.paintComponent(g);
                g.setColor(c);
                g.fillRect(0, 0, getWidth(), getHeight());
                Rectangle r = g.getClipBounds();
                System.out.println(r.width + ", " + r.height);
            }
        };
        pnl.setPreferredSize(new Dimension(10000, 10000));

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 400);
        setLocationRelativeTo(null);

        add(new JScrollPane(pnl));

        setVisible(true);

        new Thread() {

            @Override
            public void run () {
                while (true) {
                    c = new Color(rnd.nextInt(0xffffff));
                    pnl.repaint();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {}
                }
            }
        }.start();
    }

    public static void main (String args[]) {
        new Test();
    }
}
shuangwhywhy
  • 5,475
  • 2
  • 18
  • 28