Language: Java.
Hi, I need to prevent drawing over the same location of a Graphics2D more than once. For example, if the user draws using a BasicStroke with a round cap and a width of 10 over a certain area, that same area cannot be drawn on a second time.
The reason I want to do this is so that the user can draw (free-hand) translucent colours over an image without drawing over the same stroke (thus increasing the density of the colour and reducing its translucency).
I've tried storing the shapes of all the strokes made by the user (as Area objects that subtract the shape) and then clipping the Graphics2D by the intersection of all those Area objects.
This almost works, but the 'shape' obtained by the clip is not quite the same as the 'shape' drawn by the stroke - it is out by a couple of pixels.
Does anyone have any other ideas that might work?
The concept is relatively simple, you need to have multiple layers onto which you can render...
There are multiple different ways to approach the problem. You could maintain a list of Points and on each paint cycle, render these points to a backing buffer, which you would then draw over the main content using a AlphaComposite.
You could (as this example does) draw directly to the backing buffer and repaint the content, again, using a AlphaComposite to render the higher layer.
You could have any number of layers...
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class PaintOver {
public static void main(String[] args) {
new PaintOver();
}
public PaintOver() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MapPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MapPane extends JPanel {
private BufferedImage background;
private BufferedImage foreground;
public MapPane() {
try {
background = ImageIO.read(getClass().getResource("/TreasureMap.png"));
foreground = new BufferedImage(background.getWidth(), background.getHeight(), BufferedImage.TYPE_INT_ARGB);
} catch (Exception e) {
e.printStackTrace();
}
MouseAdapter mouseHandler = new MouseAdapter() {
private Point startPoint;
#Override
public void mousePressed(MouseEvent e) {
startPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
startPoint = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point endPoint = e.getPoint();
Graphics2D g2d = foreground.createGraphics();
Point offset = getOffset();
Point from = new Point(startPoint);
from.translate(-offset.x, -offset.y);
Point to = new Point(endPoint);
to.translate(-offset.x, -offset.y);
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.draw(new Line2D.Float(from, to));
g2d.dispose();
startPoint = endPoint;
repaint();
}
};
addMouseListener(mouseHandler);
addMouseMotionListener(mouseHandler);
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
protected Point getOffset() {
Point p = new Point();
if (background != null) {
p.x = (getWidth() - background.getWidth()) / 2;
p.y = (getHeight() - background.getHeight()) / 2;
}
return p;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
Graphics2D g2d = (Graphics2D) g.create();
Point offset = getOffset();
g2d.drawImage(background, offset.x, offset.y, this);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.drawImage(foreground, offset.x, offset.y, this);
g2d.dispose();
}
}
}
}
Related
I'm trying to do a simulation program using java swing gui. I have 2 images that are same but one is blurred all the way and other one is normal. And a 3rd image that is just a transparent rectangular frame in .png format. What I want to achieve is that, I will drag the transparent rectangular box over the blurred image and it will reveal the non-blurred image below. How can I achieve this?
P.S: Images are loaded into the program using JLabel onto JLayeredPane and JFrame. Also transparent rectangular box has a mouse listener.
A "overlay" illusion...
Basically this is going to generate a subImage of the un-blurred image based on the current mouse position and view port size, this is then blended with the "overlay effect" and rendered on top of the blurred image, creating the "illusion" of a cutout effect.
The following example will follow the mouse around, exposing a 200x200 pixel area of the image around the mouse cursor.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage blurredImage;
private BufferedImage normalImage;
private BufferedImage spyScopeImage;
private int viewPortSize = 200;
private Point currentLocation;
public TestPane() throws IOException {
blurredImage = ImageIO.read(getClass().getResource("/images/PosterBlurred.png"));
normalImage = ImageIO.read(getClass().getResource("/images/Poster.png"));
spyScopeImage = new BufferedImage(viewPortSize, viewPortSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D masked = spyScopeImage.createGraphics();
Color transparent = new Color(255, 0, 0, 0);
Color fill = Color.RED;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point2D.Double(viewPortSize / 2d, viewPortSize / 2d),
viewPortSize,
new float[]{0f, 0.5f, 1f},
new Color[]{fill, transparent, transparent});
// masked.setComposite(AlphaComposite.DstAtop);
masked.setPaint(rgp);
masked.fill(new Rectangle(0, 0, viewPortSize, viewPortSize));
masked.dispose();
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
currentLocation = e.getPoint();
repaint();
}
#Override
public void mouseEntered(MouseEvent e) {
currentLocation = e.getPoint();
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
currentLocation = null;
repaint();
}
};
addMouseMotionListener(mouseAdapter);
}
#Override
public Dimension getPreferredSize() {
if (blurredImage != null) {
return new Dimension(blurredImage.getWidth(), blurredImage.getHeight());
}
return new Dimension(800, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (blurredImage != null) {
int x = (getWidth() - blurredImage.getWidth()) / 2;
int y = (getHeight() - blurredImage.getHeight()) / 2;
g2d.drawImage(blurredImage, x, y, this);
if (currentLocation != null && normalImage != null) {
int mouseX = currentLocation.x - x;
int mouseY = currentLocation.y - y;
int viewPortOffset = viewPortSize / 2;
int minX = Math.max(0, mouseX - viewPortOffset);
int minY = Math.max(0, mouseY - viewPortOffset);
int maxX = Math.min(normalImage.getWidth(), mouseX + viewPortSize);
int maxY = Math.min(normalImage.getHeight(), mouseY + viewPortSize);
int viewX = minX - viewPortOffset;
int viewY = minY - viewPortOffset;
BufferedImage subimage = normalImage.getSubimage(minX, minY, maxX - minX, maxY - minY);
// Here we're going to "mask" the sub image and the "spy scope" effect together
BufferedImage masked = new BufferedImage(viewPortSize, viewPortSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D mg = masked.createGraphics();
mg.drawImage(subimage, 0, 0, this);
mg.setComposite(AlphaComposite.DstAtop);
mg.drawImage(spyScopeImage, 0, 0, this);
mg.dispose();
g2d.drawImage(masked, x + minX, y + minY, this);
}
}
g2d.dispose();
}
}
}
Alternatively...
You could create a "masked" version of the blurred image with the "scope effect" which exposes the image rendered below it, but agin, this will be expensive, as each time you're creating a new image the size of the original blurred image.
This concept is demonstrated in How to create a transparent shape in the image
package com.ssaurel.swingpaint;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;
/**
* Component for drawing !
*
* #author sylsau
*
*/
public class DrawArea extends JComponent {
// Image in which we're going to draw
private Image image;
// Graphics2D object ==> used to draw on
private Graphics2D g2;
// Mouse coordinates
private int currentX, currentY, oldX, oldY;
public DrawArea() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
// save coord x,y when mouse is pressed
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
// coord x,y when drag mouse
currentX = e.getX();
currentY = e.getY();
if (g2 != null) {
// draw line if g2 context not null
g2.drawLine(oldX, oldY, currentX, currentY);
// refresh draw area to repaint
repaint();
// store current coords x,y as olds x,y
oldX = currentX;
oldY = currentY;
}
}
});
}
protected void paintComponent(Graphics g) {
if (image == null) {
// image to draw null ==> we create
image = createImage(getSize().width, getSize().height);
g2 = (Graphics2D) image.getGraphics();
// enable antialiasing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// clear draw area
clear();
}
g.drawImage(image, 0, 0, null);
}
// now we create exposed methods
public void clear() {
g2.setPaint(Color.white);
// draw white on entire draw area to clear
g2.fillRect(0, 0, getSize().width, getSize().height);
g2.setPaint(Color.black);
repaint();
}
public void red() {
// apply red color on g2 context
g2.setPaint(Color.red);
}
public void black() {
g2.setPaint(Color.black);
}
public void magenta() {
g2.setPaint(Color.magenta);
}
public void green() {
g2.setPaint(Color.green);
}
public void blue() {
g2.setPaint(Color.blue);
}
}
package com.ssaurel.swingpaint;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SwingPaint {
JButton clearBtn, markerBtn, blackBtn, blueBtn, greenBtn, redBtn, magentaBtn;
DrawArea drawArea;
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clearBtn) {
drawArea.clear();
} else if (e.getSource() == blackBtn) {
drawArea.black();
} else if (e.getSource() == blueBtn) {
drawArea.blue();
} else if (e.getSource() == greenBtn) {
drawArea.green();
} else if (e.getSource() == markerBtn) {
drawArea.black();
} else if (e.getSource() == redBtn) {
drawArea.red();
} else if (e.getSource() == magentaBtn) {
drawArea.magenta();
}
}
};
public static void main(String[] args) {
new SwingPaint().show();
}
public void show() {
// create main frame
JFrame frame = new JFrame("Swing Paint");
Container content = frame.getContentPane();
// set layout on content pane
content.setLayout(new BorderLayout());
// create draw area
drawArea = new DrawArea();
// add to content pane
content.add(drawArea, BorderLayout.CENTER);
// create controls to apply colors and call clear feature
JPanel controls = new JPanel();
clearBtn = new JButton("Clear");
clearBtn.addActionListener(actionListener);
blackBtn = new JButton("Black");
blackBtn.addActionListener(actionListener);
blueBtn = new JButton("Blue");
blueBtn.addActionListener(actionListener);
greenBtn = new JButton("Green");
greenBtn.addActionListener(actionListener);
redBtn = new JButton("Red");
redBtn.addActionListener(actionListener);
magentaBtn = new JButton("Magenta");
magentaBtn.addActionListener(actionListener);
markerBtn = new JButton("Marker");
markerBtn.addActionListener(actionListener);
// add to panel
controls.add(markerBtn);
controls.add(greenBtn);
controls.add(blueBtn);
controls.add(blackBtn);
controls.add(redBtn);
controls.add(magentaBtn);
controls.add(clearBtn);
// add to content pane
content.add(controls, BorderLayout.NORTH);
frame.setSize(900, 720);
// can close frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// show the swing paint result
frame.setVisible(true);
}
}
the code here explains a drawing tool running on a java language using netbeans. what I would like is that the "Marker" to be thicker than default. It's thin to draw this way. well at least for me. anyways, I am lost on how to make it thicker.
Now that you have edited the post to explain that you want your lines to be drawn thicker (which is not the same as 'bold' - bold applies only to fonts) you need to look up Java "stroke styles".
Here is the official filling and stroking tutorial. There are plenty of others.
(Is not a good answer anymore because the op changed his post!)
You need to change your font.
So you need to call on your swing component
.setFont().deriveFont(Font.BOLD));
Or go with
.setFont(new Font("serif", Font.BOLD, 16))
To set a new font
I am trying to simply rotate an image in a for loop like so:
class MyCanvas extends JComponent {
AffineTransform identity = new AffineTransform();
Image arrow;
Double angle = -180.0;
public void spin() {
angle += 10.0;
for(int i = 0; i < 10; i++) {
repaint();
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
arrow = Toolkit.getDefaultToolkit().getImage("red-arrow-right-th.png");
// Rotate + translate
AffineTransform trans = new AffineTransform();
trans.setTransform(identity);
trans.translate(getWidth()/2, getHeight()/2);
trans.rotate(Math.toRadians(angle));
System.out.println(trans);
g2.drawImage(arrow, trans, this);
g2.finalize();
}
}
However when I run call spin() in main, it appears to apply only a single rotation, whilst still printing out the loop correctly. What am I overlooking something?
I've transformed your code using the recommendation of MadProgrammer:
Don't override paint, override paintComponent.
Call super.paint before performing any custom painting,
Never call finalize on anything and especially not on objects you didn't create yourself.
Use a Swing Timer
Note the following
A qualified this is used to access the ImageRotationView instance from the ActionListener inner class.
AffineTransform.getRotateInstance returns a transform that rotates coordinates around an anchor point.
Speed can be optimized but it works correctly like this.
This class works as a standalone application
A file named dice.png should be present in the base directory.
.
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class ImageRotationFrame {
public static void main(String[] args) {
new ImageRotationFrame();
}
public ImageRotationFrame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Testing");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ImageRotationComponent());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private class ImageRotationComponent extends JComponent {
Image arrow;
double angle = 0.0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
angle += 0.4;
AffineTransform trans = AffineTransform.getRotateInstance(angle, getWidth() / 2, getHeight() / 2);
((Graphics2D) g).drawImage(arrow, trans, this);
}
public ImageRotationComponent() {
try {
arrow = ImageIO.read(new File("dice.png"));
} catch (IOException e) {
e.printStackTrace();
}
int delay = 500; //milliseconds
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
ImageRotationComponent.this.repaint();
}
};
new Timer(delay, taskPerformer).start();
}
}
}
I'm trying to make a paint program, and this class is the main area where you drag your mouse to paint. The problem is the clip has to be rectangular, so any other lines within that rectangle of the clip (the clip gets bigger the faster you move) will get covered by the new clip, however the new clip isn't all needed.
My ideas of solutions are:
To somehow set the clip to a line (but I think that clip would have to be set in the repaint method, not the setClip() in paint component)
To save the image currently on the paint component and set it to the backgroud
Possibly set the ocupancy of the clip lower in the areas without the line?
Thank you for looking at it, here is the code (with some parts left out for simpler reading) and if you know a solution I would love to hear it. Thanks!
public class Canvas extends JPanel implements MouseMotionListener, MouseListener{
int sizeX, sizeY;
String title;
int[] backColor = new int[3];
int brushSize=20;
Point currentP = new Point();
Point pastP = new Point();
Point paintP = new Point();
int diffX, diffY;
boolean initialize=true;
boolean initClip=true;
Canvas(){
backColor[0] = newProject.colorA;
backColor[1] = newProject.colorB;
backColor[2] = newProject.colorC;
if(backColor[0]>=255){
backColor[0]=255;
}
if(backColor[1]>=255){
backColor[1]=255;
}
if(backColor[2]>=255){
backColor[2]=255;
}
sizeX = newProject.sizeX;
sizeY = newProject.sizeY;
//System.out.println(sizeX + " " + sizeY);
setSize(sizeX,sizeY);
setBackground(Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(brushSize));
if(initialize){
g.setColor(new Color(backColor[0], backColor[1], backColor[2]));
g.fillRect(0, 0, sizeX, sizeY);
g.setColor(Color.red);
g.drawRect(0,0,50,50);
System.out.println("Initialize");
}
else{
g2.drawLine(currentP.x, currentP.y, pastP.x,pastP.y);
}
//System.out.println("Paint");
}
#Override
public void mouseDragged(MouseEvent e) {
if(initClip) //if mouse has been released since last dragged
currentP = e.getPoint(); //This causes PastP and CurrentP to be equal
initClip=false; //since pastP is set equal to CurrentP afterward
pastP = currentP;
currentP = e.getPoint();
diffX=Math.abs(currentP.x-pastP.x); //find the differences to find how big of
diffY=Math.abs(currentP.y-pastP.y); //a clip it needs
if(diffX==0){ //if no movement, set it to brush size so the
diffX=brushSize; //clip shows up
}
if(diffY==0){
diffY=brushSize;
}
initialize=false;
if(currentP.x-pastP.x>0){ //figures out which direction it moved
paintP.x=pastP.x; //sets the clip variable to the correct corner
}
else{
paintP.x=currentP.x;
}
if(currentP.y-pastP.y>0){
paintP.y=pastP.y;
}
else{
paintP.y=currentP.y;
}
System.out.println(paintP);
repaint(paintP.x, paintP.y, diffX, diffY); //repaint with point PaintP and the
//difference it moved
}
#Override
public void mouseReleased(MouseEvent arg0) {
initClip=true;
}
I'm not sure why you would bother. Each time paintComponent is called by the paint system, you are expected to repaint the entire component anyway.
Instead, simply paint what you need to paint, then paint the selection on top of it...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawSelection {
public static void main(String[] args) {
new DrawSelection();
}
public DrawSelection() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
private Rectangle clipRect;
public TestPane() {
try {
background = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter ma = new MouseAdapter() {
private Point cp;
#Override
public void mousePressed(MouseEvent e) {
cp = e.getPoint();
clipRect = null;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
cp = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
int x = Math.min(p.x, cp.x);
int y = Math.min(p.y, cp.y);
int width = Math.max(p.x, cp.x) - x;
int height = Math.max(p.y, cp.y) - y;
clipRect = new Rectangle(x, y, width, height);
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
}
if (clipRect != null) {
g2d.setColor(UIManager.getColor("List.selectionBackground"));
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(clipRect);
}
g2d.dispose();
}
}
}
If you want to optimise the paint process, why not draw the parts of the image to a backing buffer and simply paint the buffer, then paint the selection or other dynamic parts on top of it within the paintComponent?
I have searched here but did not get my answer (though I feel it should be here).
I want to perform some activity(but I don't know how much time it will take to complete) and while this task is being run, I want to show a rotating wheel to the user with a message "Processing...please wait". Once the activity gets completed,rotating wheel should also disappear.
How to implement this feature?
As I seem to recall, the GlassPane in Swing can be used to:
Intercept user input.
Display an image/animation
Someone already implemented this: http://www.curious-creature.org/2005/02/15/wait-with-style-in-swing/
There is a download link for the source code at the top of the page.
Also look at: http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
It explains the GlassPane in some more detail.
A lot will come down to what it is you want to achieve and how much customization and work your want to go to.
You could just load a gif with ImageIcon and apply it to a JLabel to achieve the effect you're after. This example demonstrates the use of custom painting and a javax.swing.Timer to animate the wait cycle.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Arc2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Waiting {
public static void main(String[] args) {
new Waiting();
}
public Waiting() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer paintTimer;
private float cycle;
private boolean invert = false;
public TestPane() {
paintTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cycle += 0.05f;
if (cycle > 1f) {
cycle = 0f;
invert = !invert;
}
repaint();
}
});
paintTimer.setRepeats(true);
setRuning(true);
}
public void setRuning(boolean running) {
if (running) {
paintTimer.start();
} else {
paintTimer.stop();
}
}
public boolean isRunning() {
return paintTimer.isRunning();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(40, 40);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (isRunning()) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int width = getWidth() - 1;
int height = getHeight() - 1;
int radius = Math.min(width, height);
int x = (width - radius) / 2;
int y = (height - radius) / 2;
int start = 0;
int extent = Math.round(cycle * 360f);
if (invert) {
start = extent;
extent = 360 - extent;
}
g2d.setColor(Color.RED);
g2d.fill(new Arc2D.Float(x, y, radius, radius, start, extent, Arc2D.PIE));
g2d.setColor(Color.YELLOW);
g2d.draw(new Arc2D.Float(x, y, radius, radius, start, extent, Arc2D.PIE));
g2d.dispose();
}
}
}
}
In order for Swing to continue painting, you will need to create a background thread of some kind to perform the work outside of the Event Dispatching Thread until you are finished...
For example
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
// Done some work...
}
#Override
protected void done() {
// stop the animation...
}
};
For more details, check out Concurrency in Swing for more details