Using skins in a Java GUI

JFrame inherits from Frame, a heavyweight component and associated with its native screen resource (peer). If the underlying operating system does not support non-rectangular shapes, a skin is not possible.
The Robot class, (come with 1.3) allows you to capture screen rectangles (method createScreenCapture). A possibility is to use JWindow and set the background image with the data you get from the Robot class. Then add a MouseMotionListener, intercept the mouseDragged event and update the background of your JWindow.
You may want to look at the following sites:

http://www.enteract.com/~grieves/ which does some fancy things with the JInternalFrame
http://www.L2FProd.com/ and http://www.lookandfeel.com SkinRegion, Java irregular windows

Dispatching an event to a Component

You can do so directly by calling the method dispatchEvent(AWTEvent event) on the Component. The following example dispatches the events to a button programmatically (as if they would come from the user clicking the mouse).

Main.java:

mport java.awt.event.*;
import java.awt.*;
 
public class Main extends Frame
{
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      Button button1 = new Button("Click me!");
      final Button button2 = new Button("Don't bother me...");
 
      setLayout(new FlowLayout(FlowLayout.LEFT)); 
      add(button1);
      add(button2);
      setSize(200, 200);
 
      button1.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            button2.dispatchEvent(new MouseEvent(button2,
                                                 MouseEvent.MOUSE_PRESSED,
                                                 System.currentTimeMillis(),
                                                 0, 5, 5, 1, false));
 
            try {
               Thread.sleep(100);
            }
            catch(InterruptedException e) { } 
 
            button2.dispatchEvent(new MouseEvent(button2,
                                                 MouseEvent.MOUSE_RELEASED,
                                                 System.currentTimeMillis(),
                                                 0, 5, 5, 1, false));
           
         }
      });
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setVisible(true);
   }
}

Creating a JComboBox that doesn’t close when I click one of its JCheckBox items

Main.java:

import javax.swing.*;
import java.awt.*;
import java.util.*;
 
public class Main extends JFrame
{
   public Main() {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
      getContentPane().setLayout(new FlowLayout());
       
      Vector v = new Vector();
      v.add("Europe");
      v.add(new JCheckBox("Brussels", false));
      v.add(new JCheckBox("Paris", false));
      v.add(new JCheckBox("London", false));
      v.add("United States");
      v.add(new JCheckBox("New York", false));
      v.add(new JCheckBox("Detroit", false));
      v.add(new JCheckBox("San Francisco", false));
 
      getContentPane().add(new JComboCheckBox(v));
   }
    
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 300);
      main.setVisible(true);
   }
}

JComboCheckBox.java:

import javax.swing.*;
import java.awt.event.*;
 
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.MetalComboBoxUI;
 
import com.sun.java.swing.plaf.motif.MotifComboBoxUI;
import com.sun.java.swing.plaf.windows.WindowsComboBoxUI;
 
import java.awt.*;
import java.util.*;
 
public class JComboCheckBox extends JComboBox 
{
   public JComboCheckBox() { 
      init(); 
   }
    
   public JComboCheckBox(JCheckBox[] items) { 
      super(items); 
      init(); 
   }
    
   public JComboCheckBox(Vector items) { 
      super(items); 
      init(); 
   }
    
   public JComboCheckBox(ComboBoxModel aModel) { 
      super(aModel); 
      init(); 
   }
    
   public void updateUI() {
      ComboBoxUI cui = (ComboBoxUI) UIManager.getUI(this);
      if (cui instanceof MetalComboBoxUI) {
         cui = new MetalNoCloseComboBoxUI();
      } else if (cui instanceof MotifComboBoxUI) {
         cui = new MotifNoCloseComboBoxUI();       
      } else if (cui instanceof WindowsComboBoxUI) {
         cui = new WindowsNoCloseComboBoxUI();
      }
       
      setUI(cui);
   }
    
   class MetalNoCloseComboBoxUI extends MetalComboBoxUI {
      protected ComboPopup createPopup() {
         return new NoCloseBasicComboPopup(comboBox);
      }
   }
 
   class WindowsNoCloseComboBoxUI extends WindowsComboBoxUI {
      protected ComboPopup createPopup() {
         return new NoCloseBasicComboPopup(comboBox);
      }
   }
 
   class MotifNoCloseComboBoxUI extends MotifComboBoxUI {
      protected ComboPopup createPopup() {
         return new NoCloseBasicComboPopup(comboBox);
      }
   }
    
   private void init() {
      setRenderer(new ComboBoxRenderer());
      addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) { 
            itemSelected(); 
         }
      });
   }
 
   private void itemSelected() {
      if (getSelectedItem() instanceof JCheckBox) {
         JCheckBox jcb = (JCheckBox)getSelectedItem();
         jcb.setSelected(!jcb.isSelected());
         repaint();
      }
   }
 
   class ComboBoxRenderer implements ListCellRenderer {
      private JLabel label;
      
      public ComboBoxRenderer() { 
         setOpaque(true); 
      }
      
      public Component getListCellRendererComponent(JList list, Object value, int index, 
                                                    boolean isSelected, boolean cellHasFocus) {
         if (value instanceof JCheckBox) {
            list.repaint();
         }
         if (value instanceof Component) {
            Component c = (Component)value;
            if (isSelected) {
               c.setBackground(list.getSelectionBackground());
               c.setForeground(list.getSelectionForeground());
            } else {
               c.setBackground(list.getBackground());
               c.setForeground(list.getForeground());
            }
            
            return c;
         } else {
            if (label==null) {
               label = new JLabel(value.toString());
            }
            else {
               label.setText(value.toString()); 
            }
            
            return label;
         }
      }
   }
 
   class NoCloseBasicComboPopup extends BasicComboPopup
   {
      public NoCloseBasicComboPopup(JComboBox combo) {
         super(combo);         
      }
   
      protected MouseListener createListMouseListener() {
         return new ListMouseHandler();
      }
   
      protected class ListMouseHandler extends MouseAdapter {
         public void mousePressed(MouseEvent e) {
         }
      
         public void mouseReleased(MouseEvent anEvent) {
            comboBox.setSelectedIndex(list.getSelectedIndex());
            Object o = list.getSelectedValue();
            if (! (o instanceof JCheckBox)) {
               hide();
            }
         }
      }   
   }   
}

Converting an int array into an image

How can a int array be converted into an Image?
Well, that depends on the format in which the image is stored inside the int array. I will assume that each pixel is stored into a single int in aRGB format (1st byte – alpha, 2nd byte – red, 3rd byte – green, 4th byte – blue), and that the array contains the pixel values row by row (meaning that first the array contains the upper most row of the image, then the 2nd row, then the 3rd etc.) If you have it stored in a different format, convert to this one first.
Here’s code that does the trick:

/**
 * Creates an Image object from the given array of pixels.
 *
 * @param pixels The pixels array.
 * @param width The width of the Image.
 * @param height The height of the Image.
 *
 * @return An Image object representing the image stored
 * in the given int array.
 */

public static Image createImage(int [] pixels, int width, int height){
  MemoryImageSource imageSource = new MemoryImageSource(width,height,pixels,
                                                        0,width);
  return Toolkit.getDefaultToolkit().createImage(imageSource);
}

How can an image be displayed as fast as possible
Well, obviously we are drawing images using one of the various Graphics.drawImage() methods, so apparently there’s nothing we can do to speed up the drawing… The tricky part here is to know that images created with an ImageProducer such as ones we’ve created in the first question (and ones you usually create with the getImage() methods) draw much slower than images created with the Component.createImage(width,height) method. So how do we obtain such a fast drawing image? Simple – let’s draw the slow drawing image on the fast drawing image once and then draw the fast drawing image on the screen every time.
Here’s code that does this:


/**
 * Creates a new offscreen Image that will contain the same
 * data as the given image. The given Image must be preloaded.
 *
 * @param img The source image.
 * @param component Any onscreen component (one that has a peer).
 *
 * @return An offscreen copy of the given image.
 */
 
public static Image toOffscreenImage(Image img, Component comp){
  int width = img.getWidth(null);
  int height = img.getHeight(null);
  Image offScreenImage = comp.createImage(width,height);
  offScreenImage.getGraphics().drawImage(img,0,0,null);
  return offScreenImage;
}

It is important that the Component you give to the above method is onscreen (has a peer), meaning it’s inside a toplevel container (Window, Frame, Dialog) whose setVisible(true) method has been called.

Changing the outline thickness of a shape in Swing

Main.java:

import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame {
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
   }
 
   public void paint(Graphics g) {
      Graphics2D g2d = (Graphics2D) g;
 
      BasicStroke bs = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                                       10, null, 0);
      g2d.setStroke(bs);
      QuadCurve2D.Float qc = new QuadCurve2D.Float(20, 50, 100, 50, 100, 170);
      g2d.draw(qc);
 
      bs = new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                           10, null, 0);
      g2d.setStroke(bs);
      Ellipse2D.Float e = new Ellipse2D.Float(140, 50, 80, 120);
      g2d.draw(e);
 
      bs = new BasicStroke(10, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                           10, null, 0);
      g2d.setStroke(bs);
      g2d.drawRect(260, 50, 80, 120);
 
      GradientPaint gp = new GradientPaint(380, 50, Color.red, 460, 170, Color.blue);
      g2d.setPaint(gp);
      bs = new BasicStroke(15, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                           10, null, 0);
      g2d.setStroke(bs);
      g2d.drawRect(380, 50, 80, 120);
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(480, 200);
      main.setVisible(true);
   }
}

Drawing a strikethrough string in Swing

Use the class AttributedString as shown in following example.

Main.java:

import java.awt.event.*;
import java.awt.font.*;
import javax.swing.*;
import java.text.*;
import java.awt.*;
 
public class Main extends JFrame {
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
   }
 
   public void paint(Graphics g) {
      Graphics2D g2d = (Graphics2D) g;
 
      AttributedString as = new AttributedString("Easter Island");
      as.addAttribute(TextAttribute.FONT, new Font("Helvetica", Font.PLAIN, 36));
      as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 7, 13);
     
      g2d.drawString(as.getIterator(), 50, 100);
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 150);
      main.setVisible(true);
   }
}

Converting a color image to a black and white one in Swing

Use the ColorConvertOp class. Its filter() method does a pixel-by-pixel color conversion of the original image.
Following example captures the screen, converts it to gray-scale and displays it in a window:

import java.awt.event.*;
import java.awt.image.*;
import java.awt.color.*;
import javax.swing.*;
import java.awt.*;
  
public class Main extends JFrame
{
   public Main() throws Exception {
      Robot robot = new Robot();
      int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
      int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
 
      // capture the whole screen
      BufferedImage image = robot.createScreenCapture(new Rectangle(0, 0, width, height));
  
      // convert to black and white
      BufferedImage bwimage = convert2bw(image);
 
      // add the image inside a JLabel component to the window
      getContentPane().add(new JLabel(new ImageIcon(bwimage)));
      pack();
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }
 
   // converts an image to a black and white one
   public BufferedImage convert2bw(BufferedImage image) {
      ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
      ColorConvertOp ccop = new ColorConvertOp(cs, null);

      return ccop.filter(image, null);
   }
  
   public static void main(String args[]) throws Exception {
      Main main = new Main();
      main.show();
   }
}

Blurring an Image in Swing

Use the class ConvolveOp and apply a blur filter.

Main.java:

import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame {
   private boolean firstTime = true;
   private BufferedImage sourceBi;
   private BufferedImage destBi;
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      }); 
   } 
 
   public void paint(Graphics graphics) {
      Graphics2D g2d = (Graphics2D) graphics;
  
      if (firstTime) {
         Image img = new javax.swing.ImageIcon("djkrush.jpg").getImage(); 
         sourceBi = toBufferedImage(img);
         destBi = new BufferedImage(sourceBi.getWidth(), sourceBi.getHeight(), 
                                    BufferedImage.TYPE_INT_RGB);
         setSize(sourceBi.getWidth(), sourceBi.getHeight()*2);
 
         Kernel kernel = new Kernel(3, 3, new float[] 
                                            {  1f/9f, 1f/9f, 1f/9f,
                                               1f/9f, 1f/9f, 1f/9f,
                                               1f/9f, 1f/9f, 1f/9f });
         ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
         cOp.filter(sourceBi, destBi);
 
         firstTime = false;
      }
 
      g2d.drawImage(sourceBi, 0, 0, this);
      g2d.drawImage(destBi, 0, sourceBi.getHeight(), this);
   }
 
   public static BufferedImage toBufferedImage(Image image) {
      BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), 
                                           BufferedImage.TYPE_INT_RGB); 
 
      // copy the original image
      Graphics g = bi.createGraphics();
    
      g.drawImage(image, 0, 0, null);
      g.dispose();
 
      return bi;
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setVisible(true);
      main.setSize(300, 150);
   }
}

Image used:

To learn more about image processing,
check out this expensive but excellent book:

Reading a specific region of an image file with the ImageIO APIs

With the class ImageReadParam, you have control over the decoding of the image and the region. Specify the region with a Rectangle instance and pass this to the ImageReadParam instance with setSourceRegion. Then, when you execute BufferedImage bi = reader.read(imageindex, param), the resulting BufferedImage will only contain that region. (imageindex specifies the number of the image in the image file, possible in some image formats).

It is especially useful for handling large images.

In the following example, a large image is selected as the ImageInputStream. Every 10ms, a region as small as the size of the JFrame is read, and only that region. At every step, a new window region is selected; it moves about horizontally and vertically.

Main.java:

import javax.imageio.stream.*;
import javax.swing.event.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.imageio.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.io.*;
  
public class Main extends JFrame implements Runnable
{
   BufferedImage background;
   ImageReader reader;
   ImageReadParam param;
   int imageWidth, imageHeight;
   int currentx, currenty, dx, dy;
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      setResizable(false);
   }
   
   public void initImageReading() throws Exception {
      Iterator readers = ImageIO.getImageReadersByFormatName("jpg");
      reader = (ImageReader)readers.next();
 
      ImageInputStream iis = ImageIO.createImageInputStream(new File("autumn16.jpg"));
      reader.setInput(iis, true);
      param = reader.getDefaultReadParam();
 
      imageWidth = reader.getWidth(0);
      imageHeight = reader.getHeight(0);
 
      Rectangle rect = new Rectangle(0, 0, getWidth(), getHeight()); 
      param.setSourceRegion(rect);
 
      currentx = imageWidth/2;
      currenty = imageHeight/2;
      background = reader.read(0, param);
 
      dx = getWidth() / 2;
      dy = getHeight() / 2;
   }
 
   public void run() {
      while (true) {
         try {
            Thread.sleep(10);
         }
         catch(InterruptedException e) { }
 
         if (currentx+dx > imageWidth-getWidth() || currentx+dx <= 0) {
            dx *= -1;
            currenty += dy;
            if (currenty+dy > imageHeight-getHeight() || currenty+dy <= 0) {
               dy *= -1;
            }
            currenty += dy;
         }
         currentx += dx;
 
         Rectangle rect = new Rectangle(currentx, currenty, getWidth(), getHeight()); 
         param.setSourceRegion(rect);
 
         try {
            background = reader.read(0, param);
         }
         catch(IOException e) {
            e.printStackTrace();
         }
 
         repaint();
      }
   }
  
   public void paint(Graphics g) {
      if (background != null)
         g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
   }
 
   public static void main(String []args) throws Exception {
      Main main = new Main();
      main.setSize(100, 100);
      main.setVisible(true);
 
      main.initImageReading();
      new Thread(main).start();
   }
}