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();
            }
         }
      }   
   }   
}

Transferring an Image over the network

Serialization won’t work here because the actual contents of the image (the colors of the pixels) is kept in native code and Serialization only works on Java objects.

The solution is to obtain pixel values and write them into a socket. On the other side, the pixel values must be read and converted into an Image again. Here’s a method that takes an Image and writes its pixel color values into the given OutputStream:

/**
 * Grabs the pixels of the given Image object and returns the array of those
 * pixels. Note that the Image needs to be preloaded in order for this
 * method to work correctly.
 * 
 * @param Image img: The image whose pixels to grab.
 * 
 * @return The values of the pixels encoded in 4 bytes each
 * in aRGB format.
 */

public static int [] grabPixels(Image img){
  int width = img.getWidth(null);
  int height = img.getHeight(null);
  int [] pixels = new int[width*height];
  PixelGrabber grabber = new PixelGrabber(img,0,0,width,height,pixels,0,width);
  try{
    if (!grabber.grabPixels())
      return null;
  } catch (InterruptedException e){
      return null;
    }
  return pixels;
}
 
public static void writeImage(Image img, OutputStream out) throws IOException{
  int [] pixelValues = grabPixels(img);
  ObjectOutputStream objectOut = new ObjectOutputStream(out);  
  objectOut.writeObject(pixelValues); // Arrays are Serializable
}

And here is code that reads the pixel values from the given InputStream, creates and returns an Image object representing the image:

/**
 * Creates an Image object from the given array of pixels.
 * Note that the returned Image is created using an ImageProducer
 * (MemoryImageSource), and such Images are painted much slower than
 * offscreen Images created via Component.createImage(int,int) and you can't
 * draw on these Images as their getGraphics() method will return null. If
 * you need an Image that will be drawn fast and/or you need to paint
 * additional things on the Image, create an offscreen Image using
 * Component.createImage(int,int) and paint the Image returned by this method
 * on that Image.
 * Parameters:
 *   int [] pixels: The pixels array.
 *   int width: The width of the Image.
 *   int height: The height of the Image.
 */
 
public static Image createImage(int [] pixels, int width, int height){
  MemoryImageSource imageSource = new MemoryImageSource(width,height,pixels,
                                                        0,width);
  return Toolkit.getDefaultToolkit().createImage(imageSource);
}
 
 
public static Image readImage(InputStream in) throws IOException{
  ObjectInputStream objectIn = new ObjectInputStream(in);
  try{
    int [] pixelValues = (int [])objectIn.readObject();
  } catch (ClassNotFoundException e){} // This can't happen
    // since the class for (int []) must be present on every JVM
}

Drawing a rectangle with dashed lines in Swing

Main.java:

import java.awt.event.*;
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(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                                       10, new float[] {10}, 0);
      g2d.setStroke(bs);
      g2d.drawRect(20, 50, 80, 80);
 
      bs = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                           10, new float[] {1, 2, 4, 8, 16, 32}, 0);
      g2d.setStroke(bs);
      g2d.drawRect(140, 50, 80, 80);
 
      bs = new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                           5, new float[] {10}, 0);
      g2d.setStroke(bs);
      g2d.drawRect(260, 50, 80, 80);
 
      bs = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                           10, new float[] {1}, 0);
      g2d.setStroke(bs);
      g2d.drawRect(380, 50, 80, 80);
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(480, 160);
      main.setVisible(true);
   }
}

Drawing an underlined 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.UNDERLINE, TextAttribute.UNDERLINE_ON, 0, 6);
     
      g2d.drawString(as.getIterator(), 50, 100);
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 150);
      main.setVisible(true);
   }
}

Clip an image to be rendered with a text in Swing



Main.java:

import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame {
   private BufferedImage image; 
   private boolean clip = false;
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      image = toBufferedImage(new javax.swing.ImageIcon("djkrush.jpg").getImage());
      JButton button = new JButton("Clip to Text");
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            clip = true;
            repaint();
         }
      });
 
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.SOUTH, button);
   }
 
   public void paint(Graphics g) {
      super.paint(g);
 
      Graphics2D g2d = (Graphics2D) g;
 
      if (clip) {
         int w = getSize().width;
         int h = getSize().height;
 
         FontRenderContext frc = g2d.getFontRenderContext();
         Font font = new Font("Helvetica", Font.PLAIN, 12);
         TextLayout tl = new TextLayout("ALICIA", font, frc);
 
         double stringWidth = tl.getBounds().getWidth();
         double stringHeight = tl.getBounds().getHeight();
 
         // determine to what extend the string should be 
         // scaled in order to fill the window
         double scalex = (w - 50) / stringWidth;
         double scaley = (h - 50) / stringHeight;
         AffineTransform ta = AffineTransform.getScaleInstance(scalex, scaley);
 
         // transform the string in a shape
         Shape shape = tl.getOutline(ta);
 
         // position the shape
         double shapeWidth  = shape.getBounds().getWidth();
         double shapeHeight = shape.getBounds().getHeight();
         AffineTransform at = AffineTransform.getTranslateInstance(
                                                w / 2 - shapeWidth / 2, 
                                                h / 2 + shapeHeight / 2);
         shape = at.createTransformedShape(shape);
 
         g2d.clip(shape);
      }
 
      g2d.setColor(Color.red);
      g2d.fillRect(20, 50, 100, 140);
 
      Rectangle2D tr = new Rectangle2D.Double(0, 0, image.getWidth(), image.getHeight());
      TexturePaint tp = new TexturePaint(image, tr);
      g2d.setPaint(tp);
      g2d.fillRect(120, 50, 100, 140);
       
      g2d.setColor(Color.blue);
      g2d.fillRect(220, 50, 100, 140);
 
      g2d.setColor(Color.yellow);
      g2d.fillOval(320, 50, 100, 140);
   }
 
   public 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.setSize(440, 240);
      main.setVisible(true);
   }
}

Image used:

Sharpening an Image in Swing

Use the class ConvolveOp and apply a sharpen 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[] 
                                            { -1, -1, -1,
                                              -1,  9, -1,
                                              -1, -1, -1 });
         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:

Determining what formats are supported by ImageIO

ImageIO is extensible. You can add third party plug-ins for other image formats than the standard ones.

Main.java:

import javax.imageio.*;
 
public class Main
{
   public static void main(String []args) {
      String[] formatNames = ImageIO.getReaderFormatNames();
 
      for (int i=0; i<formatNames.length; i++) {
         System.out.println(formatNames[i]);
      }
   }
}

outputs:

png
jpeg
JPEG
gif
jpg
JPG