Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

JDesktopPane Background Image Artifacts

843805May 7 2006 — edited May 8 2006
Hello,

I created a custom DesktopPaneUI that can paint a background image or gradient in a JDesktopPane. It looks like this: http://dump.areath.com/images/good.png

. . . until you minimize/restore, drag the window partially off the screen, or drag another window over it. Then it might look like this: http://dump.areath.com/images/bad.png Note that from the bottom right of the internal frame to the top left of the JDesktopPane, no corruption occurs.

I've taken it out of the code below, but this occurs even when I force the RepaintManager to repaint the entire component hierarchy. I though this would do the trick, because restoring a window causes it to repaint?

What suggestions does anyone have for keeping the background clean?

Main code, somewhat huge (sorry). It's under GNU, so reuse it if you want:
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.image.BufferedImage;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.DesktopPaneUI;

/**
 * This extension of the standard DesktopPaneUI allows for the display and
 * swapping of scaled, tiled, or actual-sized background images. A background
 * gradient can be used optionally.
 * 
 * @version 1.0.0
 * @since 1.0.0
 */
public class XDesktopPaneUI extends DesktopPaneUI
{
	public static final int SCALED = 12;
	public static final int TILED = 13;
	public static final int ACTUAL = 14;
	public static final int GRADIENT = 15;

	public static final int GRAD_HORIZONTAL = 52;
	public static final int GRAD_VERTICAL = 53;
	public static final int GRAD_DIAG_TL_BR = 54;
	public static final int GRAD_DIAG_TR_BL = 55;
	public static final int GRAD_DIAG_BL_TR = 56;
	public static final int GRAD_DIAG_BR_TL = 57;

	private BufferedImage background;
	private Color gradOne;
	private Color gradTwo;

	private int drawingPolicy;
	private int gradPolicy;
	private boolean preRenderedImg = false;

	/**
	 * Creates a new default XDesktopPaneUI with no background image. The
	 * default drawing policy is <code>XDesktopPaneUI.ACTUAL</code>, and the
	 * default background is <code>Color.white</code>.
	 */
	public XDesktopPaneUI()
	{
		super();
		this.drawingPolicy = ACTUAL;
	}

	/**
	 * Creates a new XDesktopPaneUI with the specified background image, drawing
	 * policy, and animate transition policy.
	 * 
	 * @param bg The BufferedImage to use as the background image
	 * @param policy How to draw the background image
	 */
	public XDesktopPaneUI(BufferedImage bg, int policy)
	{
		super();

		this.background = bg;
		this.drawingPolicy = policy;
	}

	/**
	 * Creates a new XDesktopPaneUI with a background image at the specified
	 * location, drawing policy, and animate transition policy.
	 * 
	 * @param fileName The location of an image to load for use as the
	 *            background image, relative to the ImageLoader class
	 * @param policy How to draw the background image
	 */
	public XDesktopPaneUI(String fileName, int policy)
	{
		super();

		this.background = ImageLoader.loadBufferedImage(fileName);
		this.drawingPolicy = policy;
	}

	/**
	 * Creates a new XDesktopPaneUI with a background gradient generated from
	 * the two Colors <code>c1</code> and <code>c2</code>.
	 * 
	 * @param c1 The first color of the gradient
	 * @param c2 The second color of the gradient
	 * @param gradPolicy The policy for drawing the gradient; detirmines which
	 *            direction the gradient is painted
	 */
	public XDesktopPaneUI(Color c1, Color c2, int gradPolicy)
	{
		super();

		this.background = null;
		this.drawingPolicy = GRADIENT;

		this.gradOne = c1;
		this.gradTwo = c2;
		this.gradPolicy = gradPolicy;
	}

	/**
	 * Changes the background image of the JDesktopPane, with animation if the
	 * <code>animateTrans</code> property is <code>true</code>.
	 * 
	 * @param p The JDesktopPane to change the background of
	 * @param img A BufferedImage to set as the new background image
	 * @param policy How to draw the background image
	 */
	public void setBackgroundImage(JDesktopPane p, BufferedImage img, int policy)
	{
		this.preRenderedImg = false;
		this.background = img;
		this.drawingPolicy = policy;

		p.repaint();
	}

	/**
	 * Changes the background image of the JDesktopPane, with animation if the
	 * <code>animateTrans</code> property is <code>true</code>.
	 * 
	 * @param p The JDesktopPane to change the background of
	 * @param imgPath The location of an image to load for use as the background
	 *            image, relative to the ImageLoader class
	 * @param policy How to draw the background image
	 */
	public void setBackgroundImage(JDesktopPane p, String imgPath, int policy)
	{
		BufferedImage newBg = ImageLoader.loadBufferedImage(imgPath);
		this.preRenderedImg = false;
		this.background = newBg;
		this.drawingPolicy = policy;

		p.repaint();
	}

	/**
	 * Changes the background of the JDesktopPane to a gradient between the two
	 * Colors <code>c1</code> and <code>c2</code> with the specified
	 * gradient policy.
	 * 
	 * @param p The JDesktopPane to change the background of
	 * @param c1 The first color of the gradient
	 * @param c2 The second color of the gradient
	 * @param gradPolicy The policy for drawing the gradient; detirmines which
	 *            direction the gradient is painted
	 */
	public void setGradient(JDesktopPane p, Color c1, Color c2, int gradPolicy)
	{
		this.background = null;
		this.drawingPolicy = GRADIENT;
		this.preRenderedImg = false;

		this.gradOne = c1;
		this.gradTwo = c2;
		this.gradPolicy = gradPolicy;

		p.repaint();
	}

	public static ComponentUI createUI(JComponent c)
	{
		return new XDesktopPaneUI();
	}

	@Override
	public void paint(Graphics g, JComponent c)
	{
		super.paint(g, c);

		Graphics2D g2d = (Graphics2D) g;

		switch (this.drawingPolicy)
		{
			case SCALED:
				paintScaled(g2d, c);
				break;
			case TILED:
				if (this.preRenderedImg)
					paintActual(g2d, c);
				else
					paintTiled(g2d, c);
				break;
			case ACTUAL:
				paintActual(g2d, c);
				break;
			case GRADIENT:
				if (this.preRenderedImg)
					paintActual(g2d, c);
				else
					paintGradient(g2d, c);
			default:
				g2d.setColor(Color.white);
				g2d.drawRect(0, 0, c.getWidth(), c.getHeight());
				break;
		}
	}

	/**
	 * A private helper method that renders the image scaled to the size of the
	 * JComponent <code>c</code>.
	 * 
	 * @param g2d The Graphics2D context to render the component with
	 * @param c The JComponent to render in
	 */
	private void paintScaled(Graphics2D g2d, JComponent c)
	{
		final Container pane = c.getParent();
		final Dimension d = pane.getSize();
		g2d.drawImage(background, 0, 0, (int) d.getWidth(), (int) d.getHeight(), null);
	}

	/**
	 * A private helper method that renders the image in a tiled fashion. If
	 * this is the first call with the current background image, the method
	 * rerenders the image into a new BufferedImage in its tiled form, and sets
	 * that as the background image.
	 * 
	 * @param g2d The Graphics2D context to render the component with
	 * @param c The JComponent to render in
	 */
	private void paintTiled(Graphics2D g2d, JComponent c)
	{
		BufferedImage tiledBg = new BufferedImage(c.getWidth(), c.getHeight(),
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g2dn = tiledBg.createGraphics();

		final Dimension d = c.getSize();
		final int iWidth = background.getWidth();
		final int iHeight = background.getHeight();

		for (int x = 0; x < d.width; x += iWidth)
		{
			for (int y = 0; y < d.height; y += iHeight)
			{
				g2dn.drawImage(background, x, y, null, null);
			}
		}

		this.background = tiledBg;
		this.preRenderedImg = true;

		paintActual(g2d, c);
	}

	/**
	 * A private helper method that renders the image in its actual size.
	 * 
	 * @param g2d The Graphics2D context to render the component with
	 * @param c The JComponent to render in
	 */
	private void paintActual(Graphics2D g2d, JComponent c)
	{
		g2d.drawImage(background, 0, 0, null, null);
	}

	/**
	 * Renders a new gradient on a BufferedImage, set that BufferedImage as the
	 * background, and sets the policy to <code>XDesktopPaneUI.ACTUAL</code>.
	 * 
	 * @param g2d The Graphics2D context to render the component with
	 */
	private void paintGradient(Graphics2D g2d, JComponent c)
	{
		BufferedImage tiledBg = new BufferedImage(c.getWidth(), c.getHeight(),
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g2dn = tiledBg.createGraphics();
		int x1 = 0, y1 = 0, x2 = 0, y2 = 0;

		switch (this.gradPolicy)
		{
			case GRAD_DIAG_TL_BR:
				x1 = 0;
				y1 = 0;
				x2 = c.getWidth();
				y2 = c.getHeight();
				break;
			case GRAD_DIAG_TR_BL:
				x1 = c.getWidth();
				y1 = 0;
				x2 = 0;
				y2 = c.getHeight();
				break;
			case GRAD_DIAG_BL_TR:
				x1 = 0;
				y1 = c.getHeight();
				x2 = c.getWidth();
				y2 = 0;
				break;
			case GRAD_DIAG_BR_TL:
				x1 = c.getWidth();
				y1 = c.getHeight();
				x2 = 0;
				y2 = 0;
				break;
			case GRAD_HORIZONTAL:
				x1 = 0;
				y1 = 0;
				x2 = c.getWidth();
				y2 = 0;
				break;
			case GRAD_VERTICAL:
				x1 = 0;
				y1 = 0;
				x2 = 0;
				y2 = c.getHeight();
			default:
				x1 = 0;
				y1 = 0;
				x2 = 0;
				y2 = c.getHeight();
				break;
		}

		GradientPaint gp = new GradientPaint(x1, y1, gradOne, x2, y2, gradTwo);
		g2dn.setPaint(gp);
		Shape shp = new Rectangle(0, 0, c.getWidth(), c.getHeight());
		g2dn.fill(shp);

		this.background = tiledBg;
		this.preRenderedImg = true;

		paintActual(g2d, c);
	}

	public static void main(String args[])
	{
		JFrame test = new JFrame("Test");
		test.setSize(800, 600);
		test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final JDesktopPane p = new JDesktopPane();
		p.setUI(new XDesktopPaneUI("images/GameUI/mockArt3.jpg", SCALED));
		JInternalFrame jif = new JInternalFrame("If Test", true);
		jif.setSize(100, 100);
		jif.setLocation(250, 250);
		test.getContentPane().add(p);
		p.add(jif);

		jif.setVisible(true);

		test.setVisible(true);

		Timer ts = new Timer();
		ts.schedule(new TimerTask()
		{
			@Override
			public void run()
			{
				((XDesktopPaneUI) p.getUI()).setGradient(p, Color.blue, Color.black,
					GRAD_DIAG_BL_TR);
			}

		}, 5000l);
	}
}
Here's ImageLoader:
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;

import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageInputStream;
import javax.swing.ImageIcon;

/**
 * This static class is a utility for loading images from a file path. It can
 * return an Image object or a BufferedImage object.
 * 
 * @version 1.0.0
 * @since 1.0.0
 */
public class ImageLoader
{
	/**
	 * Reads image data from the <code>fileName</code> parameter and returns
	 * an Image object representing the read data.
	 * 
	 * @param fileName The relative path (to the ImageLoader class) and file
	 *            name from which to read image data
	 * 
	 * @return An Image object representing the image data at
	 *         <code>fileName</code>
	 */
	public static Image loadImage(String fileName)
	{
		Image image = null;

		try
		{
			String path = (ImageLoader.class.getResource(fileName)).getPath();
			path = path.replaceAll("%20", " ");
			final File file = new File(path);
			final FileImageInputStream fiis = new FileImageInputStream(file);
			image = ImageIO.read(fiis);
		}
		catch (MalformedURLException mue)
		{
			// handle error
		}
		catch (IOException ioe)
		{
			// handle error
		}

		return image;
	}

	/**
	 * Calls <code>ImageLoader.loadImage()</code> and returns the result cast
	 * into a BufferedImage object.
	 * 
	 * @param fileName The relative path (to the ImageLoader class) and file
	 *            name from which to read image data
	 * 
	 * @return A BufferedImage object representing the image data at
	 *         <code>fileName</code>
	 */
	public static BufferedImage loadBufferedImage(String fileName)
	{
		final Image img = loadImage(fileName);

		return (BufferedImage) img;
	}

	/**
	 * Creates and returns an ImageIcon object from a <code>fileName</code>
	 * parameter, using the <code>loadImage()</code> method.
	 * 
	 * @param fileName The relative path (to the ImageLoader class) and file
	 *            name from which to read image data
	 * 
	 * @return An ImageIcon object containing the Image loaded from the
	 *         <code>fileName</code> parameter
	 */
	public static ImageIcon getImageIcon(String fileName)
	{
		return new ImageIcon(loadImage(fileName));
	}
}
Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on Jun 5 2006
Added on May 7 2006
2 comments
473 views