| You can implement the BufferedImageOp 
              interface to create your own custom filters. In addition to the 
              filter() method, there are four other 
              methods in the interface that must be implemented. The following 
              RotateOp example illustrates the basics 
              of creating a filter.   The filter() method offers the option 
              of using an existing BufferedImage 
              object, passed via the second argument, to receive the output of 
              the filter. If this reference is null, a BufferedImage 
              must be created and it must possess the same dimensions as the source 
              image, similar raster and color model.  The createCompatibleDestImage() method 
              does the job of making a suitable destination image. For the BufferedImage 
              constructor it uses a color model either passed as an argument or 
              from the source. It also gets a raster suitable for this color model 
              and it checks to see if the alpha transparency factor pre-multiplies 
              the color components.   The getBound2Ds() method returns the 
              bounds object obtained from the source image. The getPoint2D() 
              method, which asks for the point in the destination image that corresponds 
              to the given point in the source image, just returns the same point 
              as in the source image since in this filter the dimensions are unchanged. 
              There are no RenderingHints 
              provided for displaying the filter output image.  The RotateColorsApplet program shown 
              below uses the custom filter called RotateOp, 
              which shifts the RGB color components each time the image is repainted 
              on the RotateImagePanel, 
              a subclass of JPanel.   
              
                 
                  | RotateColorsApplet+ RotateImagePanel 
                      + RotateOp
 Resources: liftoff.jpg 
                       
 |   
                  | import javax.swing.*; import java.awt.*;
 import java.awt.image.*;
 
 /**
 * Demonstration of the RotateOp filter. Most of 
                    the work is done on the
 * RotateImagePanel, which is displayed on this applet. 
                    The applet is
 * Runnable and periodically signals to the RotateImagePanel 
                    to repaint
 * itself with the image filtered for a different color.
 **/
 public class RotateColorsApplet extends JApplet
 implements Runnable
 {
 // Will use thread reference as a flag
 Thread fThread;
 int fDeltaT = 1000; //msecs of sleep time
 
 // Need a reference to the panel for the
 // thread loop.
 RotateImagePanel fRotateImagePanel;
 
 
 public void init () {
 Container content_pane = getContentPane 
                    ();
 
 // Create an instance of DrawingPanel
 fRotateImagePanel = new RotateImagePanel 
                    ();
 
 // Add the DrawingPanel to the contentPane.
 content_pane.add (fRotateImagePanel);
 } // init
 
 /**  Create the image on the panel and 
                    then start the thread.**/
 public void start () {
 // Tell the panel to create the image 
                    source.
 fRotateImagePanel.init (this);
 if (fThread == null) {
 fThread = 
                    new Thread (this);
 fThread.start 
                    ();
 }
 } // start
 
 /** Stop the thread web page unloaded by setting 
                    loop flag in run().**/
 public void stop ()  {
 fThread = null;
 }
 
 /** Loop the image display so that it rotates 
                    the image each frame.**/
 public void run () {
 while (fThread != null) {
 try {
 Thread.sleep 
                    (fDeltaT);
 } catch (InterruptedException 
                    e) { }
 // Repaint filtered image.
 repaint ();
 }
 } // run
 } // class RotateColorsApplet
 
 
 |   
                  | import javax.swing.*; import java.awt.*;
 import java.awt.image.*;
 
 /** Load an image, apply the rotate filter to it, and then 
                    display it. **/
 class RotateImagePanel extends JPanel
 {
 
 BufferedImage fBufferedImage;
 int fWidth, fHeight;
 RotateOp fRotate;
 
 void init (JApplet applet) {
 // Get the image
 Image img = applet.getImage (applet.getCodeBase(), 
                    "liftoff.jpg");
 
 // and use a MediaTracker to load 
                    it before converting it to
 // a BufferedImage.
 try {
 
 MediaTracker tracker 
                    = new MediaTracker (this);
 tracker.addImage (img,0);
 tracker.waitForID ( 0 
                    );
 } catch ( InterruptedException e) 
                    { }
 
 int fWidth = img.getWidth (this);
 int fHeight= img.getHeight (this);
 
 fBufferedImage =
 new BufferedImage (fWidth, 
                    fHeight, BufferedImage.TYPE_INT_RGB);
 Graphics2D g2 = fBufferedImage.createGraphics 
                    ();
 g2.drawImage (img,0 ,0, null);
 
 // Create the color rotation filter 
                    object.
 fRotate = new RotateOp ();
 
 } // init
 
 /** Before drawing the image, apply the filter.**/
 public void paintComponent (Graphics g) {
 fRotate.filter (fBufferedImage, fBufferedImage);
 g.drawImage (fBufferedImage, 0, 0, 
                    this );
 
 }
 } // RotateImagePanel
 
 |   
                  |  import javax.swing.*;import java.awt.*;
 import java.awt.image.*;
 import java.awt.geom.*;
 
 /** Demonstrate a custom filter by shift the color components. 
                      **/
 public class RotateOp implements BufferedImageOp 
                      {
 
 public final BufferedImage filter (BufferedImage 
                      source_img,
 BufferedImage dest_img) {
 
 // If no destination image provided, 
                      make one of same form as source
 if (dest_img == null)
 dest_img = createCompatibleDestImage 
                      (source_img, null);
 
 int width = source_img.getWidth 
                      ();
 int height= source_img.getHeight 
                      ();
 
 for (int y=0; y < height; y++) {
 for (int 
                      x=0; x < width; x++) {
 int 
                      pixel = source_img.getRGB (x,y);
 
 // 
                      Get the component colors
 int 
                      red   = (pixel >> 16) & 0xff;
 int 
                      green = (pixel >> 8)  & 0xff;
 int 
                      blue  =  pixel        & 
                      0xff;
 
 // 
                      Rotate the values
 int 
                      tmp = blue;
 blue 
                      = green;
 green 
                      = red;
 red 
                      = tmp;
 
 // 
                      Put new value into corresponding pixel of destination image;
 pixel 
                      =   (255 << 24) |  (red << 16) |  (green 
                      << 8) | blue;
 dest_img.setRGB 
                      (x,y,pixel);
 }
 }
 return dest_img;
 } // filter
 
 /**
 *  Create a destination image 
                      if needed. Must be same width as source
 *  and will by default use 
                      the same color model. Otherwise, it will use
 *  the one passed to it.
 **/
 public BufferedImage createCompatibleDestImage 
                      (BufferedImage source_img,
 ColorModel dest_color_model) {
 // If no color model passed, use 
                      the same as in source
 if (dest_color_model == null)
 dest_color_model 
                      = source_img.getColorModel ();
 
 int width = source_img.getWidth 
                      ();
 int height= source_img.getHeight 
                      ();
 
 // Create a new image with this 
                      color model & raster. Check if the
 // color components already multiplied 
                      by the alpha factor.
 return new BufferedImage (
 dest_color_model,
 dest_color_model.createCompatibleWritableRaster 
                      (width,height),
 dest_color_model.isAlphaPremultiplied 
                      (),
 null);
 } // createCompatibleDestImage
 
 /** Use the source image for the destination 
                      bounds size. **/
 public final Rectangle2D getBounds2D (BufferedImage 
                      source_img) {
 return source_img.getRaster ().getBounds 
                      ();
 } // getBounds2D
 
 /** The point in the source corresponds to same 
                      point
 * in the destination.
 **/
 public final Point2D getPoint2D (Point2D source_point,
 Point2D dest_point) {
 
 if (dest_point == null) dest_point 
                      = new Point2D.Float ();
 dest_point.setLocation (source_point.getX 
                      (), source_point.getY ());
 return dest_point;
 } // getPoint2D
 
 /** This filter doesn't provide any rendering 
                      hints. **/
 public final RenderingHints getRenderingHints 
                      () {
 return null;
 }
 
 }// class RotateOp
 |  
 See Knudsen (1999) and the other references for more details about 
              creating custom filters. References & Web Resources  
             Latest update: March 8, 2006  |