// Copyright (c) 2003-2004, FreeHEP
package org.freehep.graphics2d;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.print.PrinterGraphics;
import javax.swing.JPanel;
/**
* This class extends JPanel by adding double buffering. This is intended to be
* used in situations in which redrawing the contents of the panel is expensive.
*
* @author Mark Donszelmann
* @version $Id: freehep-graphics2d/src/main/java/org/freehep/graphics2d/BufferedPanel.java cba39eb5843a 2006/03/20 18:04:28 duns $
*/
public class BufferedPanel extends JPanel implements java.io.Serializable {
private VectorGraphics offScreenGraphics;
private Image offScreenImage;
private Dimension oldDimension = new Dimension();
private Dimension dim = new Dimension();
// FREEHEP-503 Disabled, since in Linux this made the blitting very
// slow.
// private Rectangle clip = new Rectangle();
private boolean printing = false;
private boolean exporting = false;
private boolean repaint = false;
public BufferedPanel() {
this(true);
}
/**
* Creates a new BufferedPanel with a width and height set to zero.
*
* @param opaque transparent panel
*/
public BufferedPanel(boolean opaque) {
// First turn off the Swing double buffering.
super(false);
// Make this either opaque or transparent.
setOpaque(opaque);
}
/**
* Triggers a full "user" repaint. If the "system" wants to repaint it will
* call the paint(Graphics) method directly, rather than scheduling a
* paint(Graphics) through a repaint().
*/
public void repaint() {
super.repaint();
repaint = true;
}
/**
* Triggers a full repaint, since the component is not valid anymore (size
* change, iconized, ...)
*/
public void invalidate() {
super.invalidate();
repaint = true;
}
/**
* Returns if true if paintComponent(VectorGraphics) should be called (was
* triggered by a repaint() or invalidate(), and resets the trigger.
*/
private synchronized boolean shouldRepaint() {
boolean result = repaint;
repaint = false;
return result;
}
/**
* Paint this panel by calling paintComponent(VectorGraphics) if necessary
* and flushing the buffered image to the screen. This method also handles
* printing and exporting separately.
*
* @param g Graphics object
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
// do not paint if params are null
if ((g == null) || (offScreenImage == null))
return;
// decide where we are painting
if (g instanceof PrinterGraphics)
printing = true;
if (g instanceof VectorGraphics)
exporting = true;
if (!isDisplaying()) {
paintComponent(VectorGraphics.create(g));
return;
}
if (shouldRepaint()) {
paintComponent(offScreenGraphics);
}
// copy buffer to screen
//long t0 = System.currentTimeMillis();
/*
* FREEHEP-503 Disabled, since in Linux this made the blitting very
* slow.
*
* Despite what the API documentation says, getClipBounds()
* returns the current dirty region. This can then be used to speedup
* the drawing of the BufferedPanel.
*/
// clip = g.getClipBounds(clip);
// Make sure the clip is not larger than the bounds.
// clip = clip.intersection(getBounds());
// BufferedImage bufferedImage = (BufferedImage)offScreenImage;
// BufferedImage subimage = bufferedImage.getSubimage(clip.x,clip.y,
// clip.width,clip.height); boolean done =
// g.drawImage(subimage,clip.x,clip.y,this);
/* boolean done = */ g.drawImage(offScreenImage, 0, 0, this);
// System.err.println("CopyImage "+done+" took:
// "+(System.currentTimeMillis()-t0)+" ms.");
}
/**
* Returns a pointer to the graphics (VectorGraphics) context of the buffer.
* The user is NOT allowed to call dispose() on this graphics object.
* <P>
* NOTE: this method used to be called getGraphics, however, since the JVM
* paint thread may call getGraphics from paintImmediately and fails to work
* with our VectorGraphics context (the gc is not longer attached to the
* image), we decided to rename the method.
*/
public Graphics getBufferedGraphics() {
return offScreenGraphics;
}
/**
* Allows for custom graphics to be painted. Subclasses should implement
* real drawing here. They can ask isPrinting(), isExporting() or
* isDisplaying() to see where the output goes. If painting is done to a
* display it is done to a buffer which is kept and copied afterwards.
*
* Note that the parameter here is of class VectorGraphics rather than
* Graphics.
*/
public void paintComponent(VectorGraphics vg) {
}
/**
* Resize and move a component.
*/
public void setBounds(int x, int y, int w, int h) {
// Make sure that the parent's method is called first;
// otherwise, the resize never happens and new images are NOT
// made.
super.setBounds(x, y, w, h);
// FREEHEP-503 Disabled, since in Linux this made the blitting very
// slow.
// clip = new Rectangle(x, y, w, h);
// Make the backing image.
makeImage();
}
/**
* Returns true if the drawing is made for a PrinterGraphics context.
*/
public boolean isPrinting() {
return printing;
}
/**
* Returns true if the drawing is made for a VectorGraphics context.
*/
public boolean isExporting() {
return exporting;
}
/**
* Returns true if the drawing is made for a PixelGraphics context, the
* display. True if not Printing and not Exporting.
*/
public boolean isDisplaying() {
return ((!isExporting()) && (!isPrinting()));
}
/**
* Make the buffered image for this panel. Check to see if the size has
* changed before doing anything.
*/
private void makeImage() {
// Get the full size of the panel.
dim = getSize(dim);
// Check that the current size is positive and that the new
// dimension is not equal to the old one.
if (dim.width > 0 && dim.height > 0) {
if (!oldDimension.equals(dim)) {
// allocate an Image, which from before JDK 1.4 was always in
// System Memory,
// and from JDK 1.4 is a Managed Buffered Image in System memory
// or VRAM (Windows)
// or a BufferedImage (getClass always returns BufferedImage)
// Real BufferedImages resides in System Memory.
// Volatile Images reside in VRAM (Windows), or XServer memory.
// Drawing more than ~1000 line segments to system memory (BI)
// and
// doing a copy to the display is faster than drawing them
// to video ram (VI) and using the fast VRAM->display copy.
// For us, System Memory is the choice for both Windows and
// XWindows,
// since we draw more lines that copying.
// However, it may be that the acceleration possible on VRAM my
// up this
// 1000 line limit.
// The image created here is a Managed Buffered Image, which can
// be moved
// from System Memory and copied to VRAM...
// Use the flag -Dsun.java2d.ddoffscreen=false to keep the image
// in
// system memory, or -Dsun.java2d.ddforcevram=true to keep the
// image
// in VRAM. Using none, seems to affect performance in picking
// mode.
// There is no programmatic (java) way of keeping the image in
// system memory, not even if you create a BufferedImage
// explicitly.
if (isOpaque()) {
offScreenImage = super.createImage(dim.width, dim.height);
} else {
offScreenImage = new BufferedImage(dim.width, dim.height,
BufferedImage.TYPE_INT_ARGB);
}
offScreenGraphics = VectorGraphics.create(offScreenImage
.getGraphics());
// Reset the old size of this panel.
oldDimension.setSize(dim);
}
} else {
offScreenImage = null;
offScreenGraphics = null;
}
}
}