Java/Advanced Graphics/Image

Материал из Java эксперт
Версия от 09:48, 1 июня 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Содержание

2D Image Draw

   <source lang="java">
 

import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import javax.swing.JFrame; public class Main extends Canvas {

 public Main() {
   setBackground(Color.white);
 }
 public void paint(Graphics g) {
   try {
     Graphics2D g2D;
     g2D = (Graphics2D) g;
     g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
     String fileName = "a.jpg";
     Image img = getToolkit().getImage(fileName);
     AffineTransform aTran = new AffineTransform();
     aTran.translate(50.0f, 20.0f);
     g2D.transform(aTran);
     g2D.drawImage(img, new AffineTransform(), this);
     g2D.setColor(Color.red);
     g2D.drawRect(1, 1, 171, 228);
     FontRenderContext frc = g2D.getFontRenderContext();
     Font font1 = new Font("Courier", Font.BOLD, 8);
     String str1 = new String("Copyright");
     TextLayout tl = new TextLayout(str1, font1, frc);
     g2D.setColor(Color.white);
     tl.draw(g2D, 125, 228);
   } catch (Exception e) {
   }
 }
 public static void main(String s[]) {
   JFrame frame1 = new JFrame("2D Images ");
   frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame1.getContentPane().add("Center", new Main());
   frame1.pack();
   frame1.setSize(new Dimension(300, 300));
   frame1.setVisible(true);
 }

}


 </source>
   
  
 
  



A 3x3 kernel that blurs an image.

   <source lang="java">

import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   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 });
   BufferedImageOp op = new ConvolveOp(kernel);
   bufferedImage = op.filter(bufferedImage, null);
 }

}

 </source>
   
  
 
  



A 3x3 kernel that embosses an image.

   <source lang="java">

import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   Kernel kernel = new Kernel(3, 3, new float[] { -2, 0, 0, 0, 1, 0, 0, 0, 2 });
   BufferedImageOp op = new ConvolveOp(kernel);
   bufferedImage = op.filter(bufferedImage, null);
 }

}

 </source>
   
  
 
  



A 3x3 kernel that sharpens an image.

   <source lang="java">

import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   Kernel kernel = new Kernel(3, 3, new float[] { -1, -1, -1, -1, 9, -1, -1,
       -1, -1 });
   BufferedImageOp op = new ConvolveOp(kernel);
   bufferedImage = op.filter(bufferedImage, null);
 }

}

 </source>
   
  
 
  



A reflected image: effect makes an illusion as if the image was reflected in water

   <source lang="java">
 

import java.awt.AlphaComposite; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; public class ImageReflection extends JComponent {

 public void paintComponent(Graphics g) {
   try {
     BufferedImage image = ImageIO.read(new File("yourImage.jpg"));
     Graphics2D g2d = (Graphics2D) g;
     int width = getWidth();
     int height = getHeight();
     int imageWidth = image.getWidth();
     int imageHeight = image.getHeight();
     int gap = 20;
     float opacity = 0.4f;
     float fadeHeight = 0.3f;
     g2d.translate((width - imageWidth) / 2, height / 2 - imageHeight);
     g2d.drawRenderedImage(image, null);
     g2d.translate(0, 2 * imageHeight + gap);
     g2d.scale(1, -1);
     BufferedImage reflection = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
     Graphics2D rg = reflection.createGraphics();
     rg.drawRenderedImage(image, null);
     rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
     rg.setPaint(new GradientPaint(0, imageHeight * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f),
         0, imageHeight, new Color(0.0f, 0.0f, 0.0f, opacity)));
     rg.fillRect(0, 0, imageWidth, imageHeight);
     rg.dispose();
     g2d.drawRenderedImage(reflection, null);
   } catch (Exception e) {
   }
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame("Reflection");
   ImageReflection r = new ImageReflection();
   frame.add(r);
   frame.setSize(300, 300);
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Bloom Demo

   <source lang="java">
 


/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruposite; import java.awt.rupositeContext; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.GridLayout; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.Raster; import java.awt.image.RasterFormatException; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

* @author Romain Guy <romain.guy@mac.ru>
*/

public class BloomDemo extends JFrame {

   private BloomViewer viewer;
   private JScrollPane scroller;
   public BloomDemo() {
       super("Bloom Demo");
       add(buildBloomViewer());
       add(buildControls(), BorderLayout.SOUTH);
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       pack();
       setSize(640, 480);
       setLocationRelativeTo(null);
   }
   
   private JComponent buildControls() {
       JPanel controls = new JPanel(new GridLayout(3, 1));
       
       JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
       panel.add(new JLabel("Bloom: 0.0"));
       JSlider slider;
       panel.add(slider = new JSlider(0, 300, 70));
       slider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               float threshold = ((JSlider) e.getSource()).getValue() / 100.0f;
               viewer.setThreshold(threshold);
           }
       });
       panel.add(new JLabel("3.0"));
       controls.add(panel);
       
       panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
       panel.add(new JLabel("Smooth: 1"));
       panel.add(slider = new JSlider(10, 100, 40));
       slider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               float smoothness = ((JSlider) e.getSource()).getValue() / 10.0f;
               viewer.setSmoothness(smoothness);
           }
       });
       panel.add(new JLabel("10"));
       controls.add(panel);
       
       panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
       JButton button;
       panel.add(button = new JButton("Open Image..."));
       button.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               JFileChooser chooser = new JFileChooser();
               if (chooser.showOpenDialog(BloomDemo.this) == JFileChooser.APPROVE_OPTION) {
                   viewer.loadImage(chooser.getSelectedFile());
                   scroller.revalidate();
               }
           }
       });
       controls.add(panel);
       
       return controls;
   }
   private JComponent buildBloomViewer() {
       viewer = new BloomViewer("A.png");
       scroller = new JScrollPane(viewer);
       scroller.setBorder(null);
       scroller.getViewport().setBackground(Color.BLACK);
       return scroller;
   }
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new BloomDemo().setVisible(true);
           }
       });
   }

} /*

* $Id: AbstractFilter.java,v 1.1 2007/02/14 00:56:19 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

Provides an abstract implementation of the BufferedImageOp * interface. This class can be used to created new image filters based * on BufferedImageOp.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

abstract class AbstractFilter implements BufferedImageOp {

   public abstract BufferedImage filter(BufferedImage src, BufferedImage dest);
   /**
    * {@inheritDoc}
    */
   public Rectangle2D getBounds2D(BufferedImage src) {
       return new Rectangle(0, 0, src.getWidth(), src.getHeight());
   }
   /**
    * {@inheritDoc}
    */
   public BufferedImage createCompatibleDestImage(BufferedImage src,
                                                  ColorModel destCM) {
       if (destCM == null) {
           destCM = src.getColorModel();
       }
       return new BufferedImage(destCM,
                                destCM.createCompatibleWritableRaster(
                                        src.getWidth(), src.getHeight()),
                                destCM.isAlphaPremultiplied(), null);
   }
   /**
    * {@inheritDoc}
    */
   public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
       return (Point2D) srcPt.clone();
   }
   /**
    * {@inheritDoc}
    */
   public RenderingHints getRenderingHints() {
       return null;
   }

} /*

* $Id: BlendComposite.java,v 1.3 2007/03/16 15:00:37 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

A blend composite defines the rule according to which a drawing primitive * (known as the source) is mixed with existing graphics (know as the * destination.)

*

BlendComposite is an implementation of the * {@link java.awt.ruposite} interface and must therefore be set as a state on * a {@link java.awt.Graphics2D} surface.

*

Please refer to {@link java.awt.Graphics2D#setComposite(java.awt.ruposite)} * for more information on how to use this class with a graphics surface.

*

Blending Modes

*

This class offers a certain number of blending modes, or compositing * rules. These rules are inspired from graphics editing software packages, * like Adobe Photoshop or The GIMP.

*

Given the wide variety of implemented blending modes and the difficulty * to describe them with words, please refer to those tools to visually see * the result of these blending modes.

*

Opacity

*

Each blending mode has an associated opacity, defined as a float value * between 0.0 and 1.0. Changing the opacity controls the force with which the * compositing operation is applied. For instance, a composite with an opacity * of 0.0 will not draw the source onto the destination. With an opacity of * 1.0, the source will be fully drawn onto the destination, according to the * selected blending mode rule.

*

The opacity, or alpha value, is used by the composite instance to mutiply * the alpha value of each pixel of the source when being composited over the * destination.

*

Creating a Blend Composite

*

Blend composites can be created in various manners:

*
    *
  • Use one of the pre-defined instance. Example: * BlendComposite.Average.
  • *
  • Derive one of the pre-defined instances by calling * {@link #derive(float)} or {@link #derive(BlendingMode)}. Deriving allows * you to change either the opacity or the blending mode. Example: * BlendComposite.Average.derive(0.5f).
  • *
  • Use a factory method: {@link #getInstance(BlendingMode)} or * {@link #getInstance(BlendingMode, float)}.
  • *
*

Implementation Caveat

*

TThe blending mode SoftLight has not been implemented yet.

*
* @see java.awt.Graphics2D
* @see java.awt.ruposite
* @see java.awt.AlphaComposite
* @author Romain Guy <romain.guy@mac.ru>
*/

final class BlendComposite implements Composite {

   /**
*

A blending mode defines the compositing rule of a * {@link BlendComposite}.

    *
    * @author Romain Guy <romain.guy@mac.ru>
    */
   public enum BlendingMode {
       AVERAGE,
       MULTIPLY,
       SCREEN,
       DARKEN,
       LIGHTEN,
       OVERLAY,
       HARD_LIGHT,
       SOFT_LIGHT,
       DIFFERENCE,
       NEGATION,
       EXCLUSION,
       COLOR_DODGE,
       INVERSE_COLOR_DODGE,
       SOFT_DODGE,
       COLOR_BURN,
       INVERSE_COLOR_BURN,
       SOFT_BURN,
       REFLECT,
       GLOW,
       FREEZE,
       HEAT,
       ADD,
       SUBTRACT,
       STAMP,
       RED,
       GREEN,
       BLUE,
       HUE,
       SATURATION,
       COLOR,
       LUMINOSITY
   }
   public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE);
   public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
   public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
   public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
   public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
   public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
   public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
   public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT);
   public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
   public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION);
   public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION);
   public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE);
   public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE);
   public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE);
   public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN);
   public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN);
   public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN);
   public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT);
   public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW);
   public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE);
   public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT);
   public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
   public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
   public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP);
   public static final BlendComposite Red = new BlendComposite(BlendingMode.RED);
   public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN);
   public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE);
   public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE);
   public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION);
   public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR);
   public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY);
   private final float alpha;
   private final BlendingMode mode;
   private BlendComposite(BlendingMode mode) {
       this(mode, 1.0f);
   }
   private BlendComposite(BlendingMode mode, float alpha) {
       this.mode = mode;
       if (alpha < 0.0f || alpha > 1.0f) {
           throw new IllegalArgumentException(
                   "alpha must be comprised between 0.0f and 1.0f");
       }
       this.alpha = alpha;
   }
   /**
*

Creates a new composite based on the blending mode passed * as a parameter. A default opacity of 1.0 is applied.

    *
    * @param mode the blending mode defining the compositing rule
    * @return a new BlendComposite based on the selected blending
    *   mode, with an opacity of 1.0
    */
   public static BlendComposite getInstance(BlendingMode mode) {
       return new BlendComposite(mode);
   }
   /**
*

Creates a new composite based on the blending mode and opacity passed * as parameters. The opacity must be a value between 0.0 and 1.0.

    *
    * @param mode the blending mode defining the compositing rule
    * @param alpha the constant alpha to be multiplied with the alpha of the
    *   source. alpha must be a floating point between 0.0 and 1.0.
    * @throws IllegalArgumentException if the opacity is less than 0.0 or
    *   greater than 1.0
    * @return a new BlendComposite based on the selected blending
    *   mode and opacity
    */
   public static BlendComposite getInstance(BlendingMode mode, float alpha) {
       return new BlendComposite(mode, alpha);
   }
   /**
*

Returns a BlendComposite object that uses the specified * blending mode and this object"s alpha value. If the newly specified * blending mode is the same as this object"s, this object is returned.

    *
    * @param mode the blending mode defining the compositing rule
    * @return a BlendComposite object derived from this object,
    *   that uses the specified blending mode
    */
   public BlendComposite derive(BlendingMode mode) {
       return this.mode == mode ? this : new BlendComposite(mode, getAlpha());
   }
   /**
*

Returns a BlendComposite object that uses the specified * opacity, or alpha, and this object"s blending mode. If the newly specified * opacity is the same as this object"s, this object is returned.

    *
    * @param alpha the constant alpha to be multiplied with the alpha of the
    *   source. alpha must be a floating point between 0.0 and 1.0.
    * @throws IllegalArgumentException if the opacity is less than 0.0 or
    *   greater than 1.0
    * @return a BlendComposite object derived from this object,
    *   that uses the specified blending mode
    */
   public BlendComposite derive(float alpha) {
       return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha);
   }
   /**
*

Returns the opacity of this composite. If no opacity has been defined, * 1.0 is returned.

    *
    * @return the alpha value, or opacity, of this object
    */
   public float getAlpha() {
       return alpha;
   }
   /**
*

Returns the blending mode of this composite.

    *
    * @return the blending mode used by this object
    */
   public BlendingMode getMode() {
       return mode;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public int hashCode() {
       return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public boolean equals(Object obj) {
       if (!(obj instanceof BlendComposite)) {
           return false;
       }
       BlendComposite bc = (BlendComposite) obj;
       return mode == bc.mode && alpha == bc.alpha;
   }
   private static boolean checkComponentsOrder(ColorModel cm) {
       if (cm instanceof DirectColorModel &&
               cm.getTransferType() == DataBuffer.TYPE_INT) {
           DirectColorModel directCM = (DirectColorModel) cm;
           
           return directCM.getRedMask() == 0x00FF0000 &&
                  directCM.getGreenMask() == 0x0000FF00 &&
                  directCM.getBlueMask() == 0x000000FF &&
                  (directCM.getNumComponents() != 4 ||
                   directCM.getAlphaMask() == 0xFF000000);
       }
       
       return false;
   }
   
   /**
    * {@inheritDoc}
    */
   public CompositeContext createContext(ColorModel srcColorModel,
                                         ColorModel dstColorModel,
                                         RenderingHints hints) {
       if (!checkComponentsOrder(srcColorModel) ||
               !checkComponentsOrder(dstColorModel)) {
           throw new RasterFormatException("Incompatible color models");
       }
       
       return new BlendingContext(this);
   }
   private static final class BlendingContext implements CompositeContext {
       private final Blender blender;
       private final BlendComposite composite;
       private BlendingContext(BlendComposite composite) {
           this.ruposite = composite;
           this.blender = Blender.getBlenderFor(composite);
       }
       public void dispose() {
       }
       public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
           int width = Math.min(src.getWidth(), dstIn.getWidth());
           int height = Math.min(src.getHeight(), dstIn.getHeight());
           float alpha = composite.getAlpha();
           int[] result = new int[4];
           int[] srcPixel = new int[4];
           int[] dstPixel = new int[4];
           int[] srcPixels = new int[width];
           int[] dstPixels = new int[width];
           for (int y = 0; y < height; y++) {
               src.getDataElements(0, y, width, 1, srcPixels);
               dstIn.getDataElements(0, y, width, 1, dstPixels);
               for (int x = 0; x < width; x++) {
                   // pixels are stored as INT_ARGB
                   // our arrays are [R, G, B, A]
                   int pixel = srcPixels[x];
                   srcPixel[0] = (pixel >> 16) & 0xFF;
                   srcPixel[1] = (pixel >>  8) & 0xFF;
                   srcPixel[2] = (pixel      ) & 0xFF;
                   srcPixel[3] = (pixel >> 24) & 0xFF;
                   pixel = dstPixels[x];
                   dstPixel[0] = (pixel >> 16) & 0xFF;
                   dstPixel[1] = (pixel >>  8) & 0xFF;
                   dstPixel[2] = (pixel      ) & 0xFF;
                   dstPixel[3] = (pixel >> 24) & 0xFF;
                   blender.blend(srcPixel, dstPixel, result);
                   // mixes the result with the opacity
                   dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
                                  ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
                                  ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
                                   (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
               }
               dstOut.setDataElements(0, y, width, 1, dstPixels);
           }
       }
   }
   private static abstract class Blender {
       public abstract void blend(int[] src, int[] dst, int[] result);
       public static Blender getBlenderFor(BlendComposite composite) {
           switch (composite.getMode()) {
               case ADD:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = Math.min(255, src[0] + dst[0]);
                           result[1] = Math.min(255, src[1] + dst[1]);
                           result[2] = Math.min(255, src[2] + dst[2]);
                           result[3] = Math.min(255, src[3] + dst[3]);
                       }
                   };
               case AVERAGE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = (src[0] + dst[0]) >> 1;
                           result[1] = (src[1] + dst[1]) >> 1;
                           result[2] = (src[2] + dst[2]) >> 1;
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case BLUE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0];
                           result[1] = src[1];
                           result[2] = dst[2];
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case COLOR:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           float[] srcHSL = new float[3];
                           ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
                           float[] dstHSL = new float[3];
                           ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
                           ColorUtilities.HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case COLOR_BURN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = src[0] == 0 ? 0 :
                               Math.max(0, 255 - (((255 - dst[0]) << 8) / src[0]));
                           result[1] = src[1] == 0 ? 0 :
                               Math.max(0, 255 - (((255 - dst[1]) << 8) / src[1]));
                           result[2] = src[2] == 0 ? 0 :
                               Math.max(0, 255 - (((255 - dst[2]) << 8) / src[2]));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case COLOR_DODGE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = src[0] == 255 ? 255 :
                               Math.min((dst[0] << 8) / (255 - src[0]), 255);
                           result[1] = src[1] == 255 ? 255 :
                               Math.min((dst[1] << 8) / (255 - src[1]), 255);
                           result[2] = src[2] == 255 ? 255 :
                               Math.min((dst[2] << 8) / (255 - src[2]), 255);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case DARKEN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = Math.min(src[0], dst[0]);
                           result[1] = Math.min(src[1], dst[1]);
                           result[2] = Math.min(src[2], dst[2]);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case DIFFERENCE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = Math.abs(dst[0] - src[0]);
                           result[1] = Math.abs(dst[1] - src[1]);
                           result[2] = Math.abs(dst[2] - src[2]);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case EXCLUSION:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] + src[0] - (dst[0] * src[0] >> 7);
                           result[1] = dst[1] + src[1] - (dst[1] * src[1] >> 7);
                           result[2] = dst[2] + src[2] - (dst[2] * src[2] >> 7);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case FREEZE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = src[0] == 0 ? 0 :
                               Math.max(0, 255 - (255 - dst[0]) * (255 - dst[0]) / src[0]);
                           result[1] = src[1] == 0 ? 0 :
                               Math.max(0, 255 - (255 - dst[1]) * (255 - dst[1]) / src[1]);
                           result[2] = src[2] == 0 ? 0 :
                               Math.max(0, 255 - (255 - dst[2]) * (255 - dst[2]) / src[2]);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case GLOW:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] == 255 ? 255 :
                               Math.min(255, src[0] * src[0] / (255 - dst[0]));
                           result[1] = dst[1] == 255 ? 255 :
                               Math.min(255, src[1] * src[1] / (255 - dst[1]));
                           result[2] = dst[2] == 255 ? 255 :
                               Math.min(255, src[2] * src[2] / (255 - dst[2]));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case GREEN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0];
                           result[1] = dst[1];
                           result[2] = src[2];
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case HARD_LIGHT:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = src[0] < 128 ? dst[0] * src[0] >> 7 :
                               255 - ((255 - src[0]) * (255 - dst[0]) >> 7);
                           result[1] = src[1] < 128 ? dst[1] * src[1] >> 7 :
                               255 - ((255 - src[1]) * (255 - dst[1]) >> 7);
                           result[2] = src[2] < 128 ? dst[2] * src[2] >> 7 :
                               255 - ((255 - src[2]) * (255 - dst[2]) >> 7);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case HEAT:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] == 0 ? 0 :
                               Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]);
                           result[1] = dst[1] == 0 ? 0 :
                               Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]);
                           result[2] = dst[2] == 0 ? 0 :
                               Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case HUE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           float[] srcHSL = new float[3];
                           ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
                           float[] dstHSL = new float[3];
                           ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
                           ColorUtilities.HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case INVERSE_COLOR_BURN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] == 0 ? 0 :
                               Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0]));
                           result[1] = dst[1] == 0 ? 0 :
                               Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1]));
                           result[2] = dst[2] == 0 ? 0 :
                               Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2]));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case INVERSE_COLOR_DODGE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] == 255 ? 255 :
                               Math.min((src[0] << 8) / (255 - dst[0]), 255);
                           result[1] = dst[1] == 255 ? 255 :
                               Math.min((src[1] << 8) / (255 - dst[1]), 255);
                           result[2] = dst[2] == 255 ? 255 :
                               Math.min((src[2] << 8) / (255 - dst[2]), 255);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case LIGHTEN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = Math.max(src[0], dst[0]);
                           result[1] = Math.max(src[1], dst[1]);
                           result[2] = Math.max(src[2], dst[2]);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case LUMINOSITY:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           float[] srcHSL = new float[3];
                           ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
                           float[] dstHSL = new float[3];
                           ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
                           ColorUtilities.HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case MULTIPLY:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = (src[0] * dst[0]) >> 8;
                           result[1] = (src[1] * dst[1]) >> 8;
                           result[2] = (src[2] * dst[2]) >> 8;
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case NEGATION:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = 255 - Math.abs(255 - dst[0] - src[0]);
                           result[1] = 255 - Math.abs(255 - dst[1] - src[1]);
                           result[2] = 255 - Math.abs(255 - dst[2] - src[2]);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case OVERLAY:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] < 128 ? dst[0] * src[0] >> 7 :
                               255 - ((255 - dst[0]) * (255 - src[0]) >> 7);
                           result[1] = dst[1] < 128 ? dst[1] * src[1] >> 7 :
                               255 - ((255 - dst[1]) * (255 - src[1]) >> 7);
                           result[2] = dst[2] < 128 ? dst[2] * src[2] >> 7 :
                               255 - ((255 - dst[2]) * (255 - src[2]) >> 7);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case RED:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = src[0];
                           result[1] = dst[1];
                           result[2] = dst[2];
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case REFLECT:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = src[0] == 255 ? 255 :
                               Math.min(255, dst[0] * dst[0] / (255 - src[0]));
                           result[1] = src[1] == 255 ? 255 :
                               Math.min(255, dst[1] * dst[1] / (255 - src[1]));
                           result[2] = src[2] == 255 ? 255 :
                               Math.min(255, dst[2] * dst[2] / (255 - src[2]));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case SATURATION:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           float[] srcHSL = new float[3];
                           ColorUtilities.RGBtoHSL(src[0], src[1], src[2], srcHSL);
                           float[] dstHSL = new float[3];
                           ColorUtilities.RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
                           ColorUtilities.HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case SCREEN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
                           result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
                           result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case SOFT_BURN:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] + src[0] < 256 ?
                               (dst[0] == 255 ? 255 :
                                Math.min(255, (src[0] << 7) / (255 - dst[0]))) :
                                                                               Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0]));
                           result[1] = dst[1] + src[1] < 256 ?
                               (dst[1] == 255 ? 255 :
                                Math.min(255, (src[1] << 7) / (255 - dst[1]))) :
                                                                               Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1]));
                           result[2] = dst[2] + src[2] < 256 ?
                               (dst[2] == 255 ? 255 :
                                Math.min(255, (src[2] << 7) / (255 - dst[2]))) :
                                                                               Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2]));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case SOFT_DODGE:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = dst[0] + src[0] < 256 ?
                               (src[0] == 255 ? 255 :
                                Math.min(255, (dst[0] << 7) / (255 - src[0]))) :
                                   Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0]));
                           result[1] = dst[1] + src[1] < 256 ?
                               (src[1] == 255 ? 255 :
                                Math.min(255, (dst[1] << 7) / (255 - src[1]))) :
                                   Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1]));
                           result[2] = dst[2] + src[2] < 256 ?
                               (src[2] == 255 ? 255 :
                                Math.min(255, (dst[2] << 7) / (255 - src[2]))) :
                                   Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2]));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case SOFT_LIGHT:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           int mRed = src[0] * dst[0] / 255;
                           int mGreen = src[1] * dst[1] / 255;
                           int mBlue = src[2] * dst[2] / 255;
                           result[0] = mRed + src[0] * (255 - ((255 - src[0]) * (255 - dst[0]) / 255) - mRed) / 255;
                           result[1] = mGreen + src[1] * (255 - ((255 - src[1]) * (255 - dst[1]) / 255) - mGreen) / 255;
                           result[2] = mBlue + src[2] * (255 - ((255 - src[2]) * (255 - dst[2]) / 255) - mBlue) / 255;
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case STAMP:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256));
                           result[1] = Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256));
                           result[2] = Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256));
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
               case SUBTRACT:
                   return new Blender() {
                       @Override
                       public void blend(int[] src, int[] dst, int[] result) {
                           result[0] = Math.max(0, src[0] + dst[0] - 256);
                           result[1] = Math.max(0, src[1] + dst[1] - 256);
                           result[2] = Math.max(0, src[2] + dst[2] - 256);
                           result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
                       }
                   };
           }
           throw new IllegalArgumentException("Blender not implemented for " +
                                              composite.getMode().name());
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* @author Romain Guy <romain.guy@mac.ru>
*/

class BloomViewer extends JComponent {

   private BrightPassFilter brightPassFilter = new BrightPassFilter();
   private BufferedImage image = null;
   private float smoothness = 4.0f;
   private BufferedImage bloom = null;
   public BloomViewer(String fileName) {
       try {
           image = GraphicsUtilities.loadCompatibleImage(getClass().getResource(fileName));
       }  catch (IOException e) {
           e.printStackTrace();
       }
   }
   @Override
   public Dimension getPreferredSize() {
       return new Dimension(image.getWidth(), image.getHeight());
   }
   @Override
   protected void paintComponent(Graphics g) {
       if (bloom == null) {
           BufferedImage result = image;
           if (smoothness > 1.0f) {
               result = GraphicsUtilities.createThumbnailFast(image, 
                           (int) (image.getWidth() / smoothness));
           }
           BufferedImage brightPass = brightPassFilter.filter(result, null);
           GaussianBlurFilter gaussianBlurFilter = new GaussianBlurFilter(5);
           bloom = GraphicsUtilities.createCompatibleImage(image);
           Graphics2D g2 = bloom.createGraphics();
           
           g2.drawImage(image, 0, 0, null);
           g2.setComposite(BlendComposite.Add);
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(gaussianBlurFilter.filter(brightPass, null),
                   0, 0, image.getWidth(), image.getHeight(), null);
           
           for (int i = 0; i < 3; i++) {
               brightPass = GraphicsUtilities.createThumbnailFast(brightPass,
                           brightPass.getWidth() / 2);
               g2.drawImage(gaussianBlurFilter.filter(brightPass, null),
                       0, 0, image.getWidth(), image.getHeight(), null);
           }
           g2.dispose();
       }
       int x = (getWidth() - bloom.getWidth()) / 2;
       int y = (getHeight() - bloom.getHeight()) / 2;
       g.drawImage(bloom, x, y, null);
   }
   public void setThreshold(float threshold) {
       brightPassFilter = new BrightPassFilter(threshold);
       bloom = null;
       repaint();
   }
   public void setSmoothness(float smoothness) {
       this.smoothness = smoothness;
       bloom = null;
       repaint();
   }
   public void loadImage(File file) {
       try {
           this.image = GraphicsUtilities.loadCompatibleImage(file.toURI().toURL());
           bloom = null;
           repaint();
       }  catch (MalformedURLException ex) {
           ex.printStackTrace();
       } catch (IOException ex) {
           ex.printStackTrace();
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* @author Romain Guy
*/

class BrightPassFilter extends AbstractFilter {

   private float brightnessThreshold;
   public BrightPassFilter() {
       this(0.7f);
   }
   public BrightPassFilter(float brightnessThreshold) {
       this.brightnessThreshold = brightnessThreshold;
   }
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       if (dst == null) {
           DirectColorModel directCM = new DirectColorModel(32,
                   0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
           dst = createCompatibleDestImage(src, directCM);
       }
       int width = src.getWidth();
       int height = src.getHeight();
       int[] pixels = new int[width * height];
       GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels);
       brightPass(pixels, width, height);
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels);
       return dst;
   }
   private void brightPass(int[] pixels, int width, int height) {
       int threshold = (int) (brightnessThreshold * 255);
       
       int r;
       int g;
       int b;
       int luminance;
       int[] luminanceData = new int[3 * 256];
       
       for (int i = 0; i < luminanceData.length; i += 3) {
           luminanceData[i    ] = (int) (i * 0.2125f);
           luminanceData[i + 1] = (int) (i * 0.7154f);
           luminanceData[i + 2] = (int) (i * 0.0721f);
       }
       
       int index = 0;
       for (int y = 0; y < height; y++) {
           for (int x = 0; x < width; x++) {
               int pixel = pixels[index];
               // unpack the pixel"s components
               r = pixel >> 16 & 0xFF;
               g = pixel >> 8  & 0xFF;
               b = pixel       & 0xFF;
               
               // compute the luminance
               luminance = luminanceData[r * 3] + luminanceData[g * 3 + 1] +
                       luminanceData[b * 3 + 2];
               
               // apply the treshold to select the brightest pixels
               luminance = Math.max(0, luminance - threshold);
               
               int sign = (int) Math.signum(luminance);
               // pack the components in a single pixel
               pixels[index] = 0xFF000000 | (r * sign) << 16 |
                       (g * sign) << 8 | (b * sign);
               index++;
           }
       }
   }

} /*

* $Id: ColorUtilities.java,v 1.1 2007/02/14 00:56:18 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

ColorUtilities contains a set of tools to perform * common color operations easily.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class ColorUtilities {

   private ColorUtilities() {
   }
   /**
*

Returns the HSL (Hue/Saturation/Luminance) equivalent of a given * RGB color. All three HSL components are between 0.0 and 1.0.

    *
    * @param color the RGB color to convert
    * @return a new array of 3 floats corresponding to the HSL components
    */
   public static float[] RGBtoHSL(Color color) {
       return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), null);
   }
   /**
*

Returns the HSL (Hue/Saturation/Luminance) equivalent of a given * RGB color. All three HSL components are between 0.0 and 1.0.

    *
    * @param color the RGB color to convert
    * @param hsl a pre-allocated array of floats; can be null
    * @return hsl if non-null, a new array of 3 floats otherwise
    * @throws IllegalArgumentException if hsl has a length lower
    *   than 3
    */
   public static float[] RGBtoHSL(Color color, float[] hsl) {
       return RGBtoHSL(color.getRed(), color.getGreen(), color.getBlue(), hsl);
   }
   /**
*

Returns the HSL (Hue/Saturation/Luminance) equivalent of a given * RGB color. All three HSL components are between 0.0 and 1.0.

    *
    * @param r the red component, between 0 and 255
    * @param g the green component, between 0 and 255
    * @param b the blue component, between 0 and 255
    * @return a new array of 3 floats corresponding to the HSL components
    */
   public static float[] RGBtoHSL(int r, int g, int b) {
       return RGBtoHSL(r, g, b, null);
   }
   /**
*

Returns the HSL (Hue/Saturation/Luminance) equivalent of a given * RGB color. All three HSL components are floats between 0.0 and 1.0.

    *
    * @param r the red component, between 0 and 255
    * @param g the green component, between 0 and 255
    * @param b the blue component, between 0 and 255
    * @param hsl a pre-allocated array of floats; can be null
    * @return hsl if non-null, a new array of 3 floats otherwise
    * @throws IllegalArgumentException if hsl has a length lower
    *   than 3
    */
   public static float[] RGBtoHSL(int r, int g, int b, float[] hsl) {
       if (hsl == null) {
           hsl = new float[3];
       } else if (hsl.length < 3) {
           throw new IllegalArgumentException("hsl array must have a length of" +
                                              " at least 3");
       }
       if (r < 0) r = 0;
       else if (r > 255) r = 255;
       if (g < 0) g = 0;
       else if (g > 255) g = 255;
       if (b < 0) b = 0;
       else if (b > 255) b = 255;
       float var_R = (r / 255f);
       float var_G = (g / 255f);
       float var_B = (b / 255f);
       float var_Min;
       float var_Max;
       float del_Max;
       if (var_R > var_G) {
           var_Min = var_G;
           var_Max = var_R;
       } else {
           var_Min = var_R;
           var_Max = var_G;
       }
       if (var_B > var_Max) {
           var_Max = var_B;
       }
       if (var_B < var_Min) {
           var_Min = var_B;
       }
       del_Max = var_Max - var_Min;
       float H, S, L;
       L = (var_Max + var_Min) / 2f;
       if (del_Max - 0.01f <= 0.0f) {
           H = 0;
           S = 0;
       } else {
           if (L < 0.5f) {
               S = del_Max / (var_Max + var_Min);
           } else {
               S = del_Max / (2 - var_Max - var_Min);
           }
           float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max;
           float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max;
           float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max;
           if (var_R == var_Max) {
               H = del_B - del_G;
           } else if (var_G == var_Max) {
               H = (1 / 3f) + del_R - del_B;
           } else {
               H = (2 / 3f) + del_G - del_R;
           }
           if (H < 0) {
               H += 1;
           }
           if (H > 1) {
               H -= 1;
           }
       }
       hsl[0] = H;
       hsl[1] = S;
       hsl[2] = L;
       return hsl;
   }
   /**
*

Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance) * color.

    *
    * @param h the hue component, between 0.0 and 1.0
    * @param s the saturation component, between 0.0 and 1.0
    * @param l the luminance component, between 0.0 and 1.0
    * @return a new Color object equivalent to the HSL components
    */
   public static Color HSLtoRGB(float h, float s, float l) {
       int[] rgb = HSLtoRGB(h, s, l, null);
       return new Color(rgb[0], rgb[1], rgb[2]);
   }
   /**
*

Returns the RGB equivalent of a given HSL (Hue/Saturation/Luminance) * color. All three RGB components are integers between 0 and 255.

    *
    * @param h the hue component, between 0.0 and 1.0
    * @param s the saturation component, between 0.0 and 1.0
    * @param l the luminance component, between 0.0 and 1.0
    * @param rgb a pre-allocated array of ints; can be null
    * @return rgb if non-null, a new array of 3 ints otherwise
    * @throws IllegalArgumentException if rgb has a length lower
    *   than 3
    */
   public static int[] HSLtoRGB(float h, float s, float l, int[] rgb) {
       if (rgb == null) {
           rgb = new int[3];
       } else if (rgb.length < 3) {
           throw new IllegalArgumentException("rgb array must have a length of" +
                                              " at least 3");
       }
       if (h < 0) h = 0.0f;
       else if (h > 1.0f) h = 1.0f;
       if (s < 0) s = 0.0f;
       else if (s > 1.0f) s = 1.0f;
       if (l < 0) l = 0.0f;
       else if (l > 1.0f) l = 1.0f;
       int R, G, B;
       if (s - 0.01f <= 0.0f) {
           R = (int) (l * 255.0f);
           G = (int) (l * 255.0f);
           B = (int) (l * 255.0f);
       } else {
           float var_1, var_2;
           if (l < 0.5f) {
               var_2 = l * (1 + s);
           } else {
               var_2 = (l + s) - (s * l);
           }
           var_1 = 2 * l - var_2;
           R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f)));
           G = (int) (255.0f * hue2RGB(var_1, var_2, h));
           B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f)));
       }
       rgb[0] = R;
       rgb[1] = G;
       rgb[2] = B;
       return rgb;
   }
   private static float hue2RGB(float v1, float v2, float vH) {
       if (vH < 0.0f) {
           vH += 1.0f;
       }
       if (vH > 1.0f) {
           vH -= 1.0f;
       }
       if ((6.0f * vH) < 1.0f) {
           return (v1 + (v2 - v1) * 6.0f * vH);
       }
       if ((2.0f * vH) < 1.0f) {
           return (v2);
       }
       if ((3.0f * vH) < 2.0f) {
           return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
       }
       return (v1);
   }

} /*

* $Id: GaussianBlurFilter.java,v 1.2 2007/02/19 15:53:42 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class GaussianBlurFilter extends AbstractFilter {

   private final int radius;
   /**
*

Creates a new blur filter with a default radius of 3.

    */
   public GaussianBlurFilter() {
       this(3);
   }
   /**
*

Creates a new blur filter with the specified radius. If the radius * is lower than 0, a radius of 0.1 will be used automatically.

    *
    * @param radius the radius, in pixels, of the blur
    */
   public GaussianBlurFilter(int radius) {
       if (radius < 1) {
           radius = 1;
       }
       this.radius = radius;
   }
   /**
*

Returns the radius used by this filter, in pixels.

    *
    * @return the radius of the blur
    */
   public float getRadius() {
       return radius;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       float[] kernel = createGaussianKernel(radius);
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       // horizontal pass
       blur(srcPixels, dstPixels, width, height, kernel, radius);
       // vertical pass
       //noinspection SuspiciousNameCombination
       blur(dstPixels, srcPixels, height, width, kernel, radius);
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }
   /**
*

Blurs the source pixels into the destination pixels. The force of * the blur is specified by the radius which must be greater than 0.

*

The source and destination pixels arrays are expected to be in the * INT_ARGB format.

*

After this method is executed, dstPixels contains a transposed and * filtered copy of srcPixels.

    *
    * @param srcPixels the source pixels
    * @param dstPixels the destination pixels
    * @param width the width of the source picture
    * @param height the height of the source picture
    * @param kernel the kernel of the blur effect
    * @param radius the radius of the blur effect
    */
   static void blur(int[] srcPixels, int[] dstPixels,
                    int width, int height,
                    float[] kernel, int radius) {
       float a;
       float r;
       float g;
       float b;
       int ca;
       int cr;
       int cg;
       int cb;
       for (int y = 0; y < height; y++) {
           int index = y;
           int offset = y * width;
           for (int x = 0; x < width; x++) {
               a = r = g = b = 0.0f;
               for (int i = -radius; i <= radius; i++) {
                   int subOffset = x + i;
                   if (subOffset < 0 || subOffset >= width) {
                       subOffset = (x + width) % width;
                   }
                   int pixel = srcPixels[offset + subOffset];
                   float blurFactor = kernel[radius + i];
                   a += blurFactor * ((pixel >> 24) & 0xFF);
                   r += blurFactor * ((pixel >> 16) & 0xFF);
                   g += blurFactor * ((pixel >>  8) & 0xFF);
                   b += blurFactor * ((pixel      ) & 0xFF);
               }
               ca = (int) (a + 0.5f);
               cr = (int) (r + 0.5f);
               cg = (int) (g + 0.5f);
               cb = (int) (b + 0.5f);
               dstPixels[index] = ((ca > 255 ? 255 : ca) << 24) |
                                  ((cr > 255 ? 255 : cr) << 16) |
                                  ((cg > 255 ? 255 : cg) <<  8) |
                                   (cb > 255 ? 255 : cb);
               index += height;
           }
       }
   }
   static float[] createGaussianKernel(int radius) {
       if (radius < 1) {
           throw new IllegalArgumentException("Radius must be >= 1");
       }
       float[] data = new float[radius * 2 + 1];
       float sigma = radius / 3.0f;
       float twoSigmaSquare = 2.0f * sigma * sigma;
       float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
       float total = 0.0f;
       for (int i = -radius; i <= radius; i++) {
           float distance = i * i;
           int index = i + radius;
           data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
           total += data[index];
       }
       for (int i = 0; i < data.length; i++) {
           data[i] /= total;
       }
       return data;
   }

} /*

* $Id: GraphicsUtilities.java,v 1.1 2007/02/14 00:56:19 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       BufferedImage temp = null;
       Graphics2D g2 = null;
       int previousWidth = width;
       int previousHeight = height;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           if (temp == null) {
               temp = createCompatibleImage(image, width, height);
               g2 = temp.createGraphics();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           }
           g2.drawImage(thumb, 0, 0, width, height,
                        0, 0, previousWidth, previousHeight, null);
           previousWidth = width;
           previousHeight = height;
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       g2.dispose();
       if (width != thumb.getWidth() || height != thumb.getHeight()) {
           temp = createCompatibleImage(image, width, height);
           g2 = temp.createGraphics();
           g2.drawImage(thumb, 0, 0, null);
           g2.dispose();
           thumb = temp;
       }
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       BufferedImage temp = null;
       Graphics2D g2 = null;
       int previousWidth = width;
       int previousHeight = height;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           if (temp == null) {
               temp = createCompatibleImage(image, width, height);
               g2 = temp.createGraphics();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           }
           g2.drawImage(thumb, 0, 0, width, height,
                        0, 0, previousWidth, previousHeight, null);
           previousWidth = width;
           previousHeight = height;
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       g2.dispose();
       if (width != thumb.getWidth() || height != thumb.getHeight()) {
           temp = createCompatibleImage(image, width, height);
           g2 = temp.createGraphics();
           g2.drawImage(thumb, 0, 0, null);
           g2.dispose();
           thumb = temp;
       }
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

}


 </source>
   
  
 
  



Blur an Image

   <source lang="java">
 

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruposite; import java.awt.Cursor; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.LinearGradientPaint; import java.awt.Paint; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; import java.util.List; import java.util.ResourceBundle; import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import org.jdesktop.animation.timing.Cycle; import org.jdesktop.animation.timing.Envelope; import org.jdesktop.animation.timing.TimingController; import org.jdesktop.animation.timing.interpolation.ObjectModifier; import org.jdesktop.animation.timing.interpolation.PropertyRange; public class Blur {

   private static MainFrame mainFrame;
   private static ResourceBundle bundle;
   private Blur() {
   }
   public static MainFrame getMainFrame() {
       return mainFrame;
   }
   public static void main(String[] args) {
       try {
           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (UnsupportedLookAndFeelException e) {
           e.printStackTrace();
       }
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               mainFrame = new MainFrame();
               mainFrame.setVisible(true);
           }
       });
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class MainFrame extends JFrame {

   private StepLabel stepLabel;
   private BufferedImage image;
   private ProgressGlassPane waitPanel;
   private TimingController timer;
   private SizeStepPanel sizeStep;
   private DragAndDropStepPanel dragAndDropStep;
   private DoneStepPanel doneStep;
   private String fileName;
   public MainFrame() {
       super("");
       setContentPane(new GradientPanel());
       setGlassPane(waitPanel = new ProgressGlassPane());
       buildContentPane();
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setSize(640, 480);
       setResizable(false);
       setLocationRelativeTo(null);
   }
   private void buildContentPane() {
       buildStepLabel();
       add(stepLabel, BorderLayout.SOUTH);
       showDragAndDropStep();
   }
   private void buildStepLabel() {
       stepLabel = new StepLabel();
       stepLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 36, 0));
   }
   public void showDragAndDropStep() {
       if (image != null) {
           image.flush();
           image = null;
       }
       if (sizeStep != null) {
           remove(sizeStep);
           sizeStep = null;
       } else if (doneStep != null) {
           remove(doneStep);
           doneStep = null;
       }
       dragAndDropStep = new DragAndDropStepPanel();
       dragAndDropStep.setTransferHandler(new ImageTransferHandler());
       add(dragAndDropStep);
       setStepInfo(1, "Drop an image in this window");
       hideWaitGlassPane();
   }
   private void setStepInfo(int step, String message) {
       stepLabel.setStep(step);
       stepLabel.setText(message);
   }
   public void setImage(BufferedImage image, String fileName) {
       this.fileName = fileName;
       this.image = image;
       showSizeStep();
   }
   public BufferedImage getImage() {
       return image;
   }
   private void showSizeStep() {
       sizeStep = new SizeStepPanel();
       remove(dragAndDropStep);
       dragAndDropStep = null;
       add(sizeStep);
       setStepInfo(2, "Choose Title");
   }
   public void showWaitGlassPane() {
       Cycle cycle = new Cycle(2500, 33);
       Envelope envelope = new Envelope(TimingController.INFINITE, 0,
                                        Envelope.RepeatBehavior.REVERSE,
                                        Envelope.EndBehavior.HOLD);
       PropertyRange fadeRange = PropertyRange.createPropertyRangeInt("progress", 0, 100); // NON-NLS
       timer = new TimingController(cycle, envelope,
                                    new ObjectModifier(waitPanel, fadeRange));
       waitPanel.setProgress(0);
       waitPanel.setVisible(true);
       timer.start();
   }
   public void hideWaitGlassPane() {
       if (timer != null && timer.isRunning()) {
           timer.stop();
       }
       waitPanel.setVisible(false);
   }
   public void showDoneStep() {
       doneStep = new DoneStepPanel();
       remove(sizeStep);
       sizeStep = null;
       add(doneStep);
       setStepInfo(3, "Picture saved");
       hideWaitGlassPane();
   }
   public String getFileName() {
       return fileName;
   }

} /*

* $Id: AbstractFilter.java,v 1.1 2007/01/15 16:12:02 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

Provides an abstract implementation of the BufferedImageOp * interface. This class can be used to created new image filters based * on BufferedImageOp.

*
* @author Romain Guy <romain.guy@mac.ru>
*/
abstract class AbstractFilter implements BufferedImageOp {
   public abstract BufferedImage filter(BufferedImage src, BufferedImage dest);
   /**
    * {@inheritDoc}
    */
   public Rectangle2D getBounds2D(BufferedImage src) {
       return new Rectangle(0, 0, src.getWidth(), src.getHeight());
   }
   /**
    * {@inheritDoc}
    */
   public BufferedImage createCompatibleDestImage(BufferedImage src,
                                                  ColorModel destCM) {
       if (destCM == null) {
           destCM = src.getColorModel();
       }
       return new BufferedImage(destCM,
                                destCM.createCompatibleWritableRaster(
                                        src.getWidth(), src.getHeight()),
                                destCM.isAlphaPremultiplied(), null);
   }
   /**
    * {@inheritDoc}
    */
   public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
       return (Point2D) srcPt.clone();
   }
   /**
    * {@inheritDoc}
    */
   public RenderingHints getRenderingHints() {
       return null;
   }

} /*

* $Id: ColorTintFilter.java,v 1.1 2007/01/15 16:12:02 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

A color mixer filter can be used to mix a solid color to an image. The * result is an image tinted by the specified color. The force of the effect * can be controlled with the mixValue, a number between 0.0 and * 1.0 that can be seen as the percentage of the mix (0.0 does not affect the * source image and 1.0 replaces all the pixels by the solid color).

*

The color of the pixels in the resulting image is computed as follows:

*
 * cR = cS * (1 - mixValue) + cM * mixValue
 * 
*

Definition of the parameters:

*
    *
  • cR: color of the resulting pixel
  • *
  • cS: color of the source pixel
  • *
  • cM: the solid color to mix with the source image
  • *
  • mixValue: strength of the mix, a value between 0.0 and 1.0
  • *
*
* @author Romain Guy <romain.guy@mac.ru>
*/

class ColorTintFilter extends AbstractFilter {

   private final Color mixColor;
   private final float mixValue;
   /**
*

Creates a new color mixer filter. The specified color will be used * to tint the source image, with a mixing strength defined by * mixValue.

    *
    * @param mixColor the solid color to mix with the source image
    * @param mixValue the strength of the mix, between 0.0 and 1.0; if the
    *   specified value lies outside this range, it is clamped
    * @throws IllegalArgumentException if mixColor is null
    */
   public ColorTintFilter(Color mixColor, float mixValue) {
       if (mixColor == null) {
           throw new IllegalArgumentException("mixColor cannot be null");
       }
       this.mixColor = mixColor;
       if (mixValue < 0.0f) {
           mixValue = 0.0f;
       } else if (mixValue > 1.0f) {
           mixValue = 1.0f;
       }
       this.mixValue = mixValue;
   }
   /**
*

Returns the mix value of this filter.

    *
    * @return the mix value, between 0.0 and 1.0
    */
   public float getMixValue() {
       return mixValue;
   }
   /**
*

Returns the solid mix color of this filter.

    *
    * @return the solid color used for mixing
    */
   public Color getMixColor() {
       return mixColor;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int width = src.getWidth();
       int height = src.getHeight();
       int[] pixels = new int[width * height];
       GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels);
       mixColor(pixels);
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels);
       return dst;
   }
   private void mixColor(int[] pixels) {
       int mix_a = mixColor.getAlpha();
       int mix_r = mixColor.getRed();
       int mix_g = mixColor.getBlue();
       int mix_b = mixColor.getGreen();
       for (int i = 0; i < pixels.length; i++) {
           int argb = pixels[i];
           int a = (argb >> 24) & 0xFF;
           int r = (argb >> 16) & 0xFF;
           int g = (argb >>  8) & 0xFF;
           int b = (argb      ) & 0xFF;
           a = (int) (a * (1.0f - mixValue) + mix_a * mixValue);
           r = (int) (r * (1.0f - mixValue) + mix_r * mixValue);
           g = (int) (g * (1.0f - mixValue) + mix_g * mixValue);
           b = (int) (b * (1.0f - mixValue) + mix_b * mixValue);
           pixels[i] = a << 24 | r << 16 | g << 8 | b;
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class DoneStepPanel extends JPanel {

   public DoneStepPanel() {
       setLayout(new GridBagLayout());
       setBackground(Color.BLACK);
       setOpaque(false);
       JLabel label = new JLabel("Done");
       label.setFont(new Font("Helvetica", Font.BOLD, 64)); // NON-NLS
       label.setForeground(Color.WHITE);
       add(label, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
                                         GridBagConstraints.CENTER,
                                         GridBagConstraints.NONE,
                                         new Insets(0, 0, 0, 0), 0, 0));
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class DragAndDropStepPanel extends JComponent {

   private BufferedImage dropHere;
   public DragAndDropStepPanel() {
       setBackground(Color.BLACK);
       setOpaque(false);
       loadSupportImage();
   }
   private void loadSupportImage() {
       try {
           dropHere = GraphicsUtilities.loadCompatibleImage(
                   getClass().getResource("drop-here.png")); // NON-NLS
           //dropHere = Reflection.createReflection(dropHere);
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
   @Override
   protected void paintComponent(Graphics g) {
       //Rectangle clip = g.getClipBounds();
       //g.setColor(getBackground());
       //g.fillRect(clip.x, clip.y, clip.width, clip.height);
       int x = (getWidth() - dropHere.getWidth()) / 2;
       int y = (getHeight() - dropHere.getHeight()) / 2;
       g.drawImage(dropHere, x, y, null);
   }

} /*

* $Id: FastBlurFilter.java,v 1.1 2007/01/15 16:12:02 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


/**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class FastBlurFilter extends AbstractFilter {

   private final int radius;
   public FastBlurFilter() {
       this(3);
   }
   public FastBlurFilter(int radius) {
       if (radius < 1) {
           radius = 1;
       }
       this.radius = radius;
   }
   public int getRadius() {
       return radius;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       // horizontal pass
       blur(srcPixels, dstPixels, width, height, radius);
       // vertical pass
       blur(dstPixels, srcPixels, height, width, radius);
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }
   private static void blur(int[] srcPixels, int[] dstPixels,
                            int width, int height, int radius) {
       int windowSize = radius * 2 + 1;
       int sumAlpha;
       int sumRed;
       int sumGreen;
       int sumBlue;
       int srcIndex = 0;
       int dstIndex;
       int pixel;
       for (int y = 0; y < height; y++) {
           sumAlpha = sumRed = sumGreen = sumBlue = 0;
           dstIndex = y;
           pixel = srcPixels[srcIndex];
           sumAlpha += (radius + 1) * ((pixel >> 24) & 0xFF);
           sumRed   += (radius + 1) * ((pixel >> 16) & 0xFF);
           sumGreen += (radius + 1) * ((pixel >>  8) & 0xFF);
           sumBlue  += (radius + 1) * ( pixel        & 0xFF);
           for (int i = 1; i <= radius; i++) {
               pixel = srcPixels[srcIndex + (i <= width - 1 ? i : width - 1)];
               sumAlpha += (pixel >> 24) & 0xFF;
               sumRed   += (pixel >> 16) & 0xFF;
               sumGreen += (pixel >>  8) & 0xFF;
               sumBlue  +=  pixel        & 0xFF;
           }
           for  (int x = 0; x < width; x++) {
               dstPixels[dstIndex] = sumAlpha / windowSize << 24 |
                                     sumRed   / windowSize << 16 |
                                     sumGreen / windowSize <<  8 |
                                     sumBlue  / windowSize;
               dstIndex += height;
               int nextPixelIndex = x + radius + 1;
               if (nextPixelIndex >= width) {
                   nextPixelIndex = width - 1;
               }
               int previousPixelIndex = x - radius;
               if (previousPixelIndex < 0) {
                   previousPixelIndex = 0;
               }
               int nextPixel = srcPixels[srcIndex + nextPixelIndex];
               int previousPixel = srcPixels[srcIndex + previousPixelIndex];
               sumAlpha += (nextPixel     >> 24) & 0xFF;
               sumAlpha -= (previousPixel >> 24) & 0xFF;
               sumRed += (nextPixel     >> 16) & 0xFF;
               sumRed -= (previousPixel >> 16) & 0xFF;
               sumGreen += (nextPixel     >> 8) & 0xFF;
               sumGreen -= (previousPixel >> 8) & 0xFF;
               sumBlue += nextPixel & 0xFF;
               sumBlue -= previousPixel & 0xFF;
           }
           srcIndex += width;
       }
   }

} class GaussianBlurFilter extends AbstractFilter {

   private final int radius;
   /**
*

Creates a new blur filter with a default radius of 3.

    */
   public GaussianBlurFilter() {
       this(3);
   }
   /**
*

Creates a new blur filter with the specified radius. If the radius * is lower than 0, a radius of 0.1 will be used automatically.

    *
    * @param radius the radius, in pixels, of the blur
    */
   public GaussianBlurFilter(int radius) {
       if (radius < 1) {
           radius = 1;
       }
       this.radius = radius;
   }
   /**
*

Returns the radius used by this filter, in pixels.

    *
    * @return the radius of the blur
    */
   public int getRadius() {
       return radius;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       float[] kernel = createGaussianKernel(radius);
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       // horizontal pass
       blur(srcPixels, dstPixels, width, height, kernel, radius);
       // vertical pass
       //noinspection SuspiciousNameCombination
       blur(dstPixels, srcPixels, height, width, kernel, radius);
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }
   /**
*

Blurs the source pixels into the destination pixels. The force of * the blur is specified by the radius which must be greater than 0.

*

The source and destination pixels arrays are expected to be in the * INT_ARGB format.

*

After this method is executed, dstPixels contains a transposed and * filtered copy of srcPixels.

    *
    * @param srcPixels the source pixels
    * @param dstPixels the destination pixels
    * @param width the width of the source picture
    * @param height the height of the source picture
    * @param kernel the kernel of the blur effect
    * @param radius the radius of the blur effect
    */
   static void blur(int[] srcPixels, int[] dstPixels,
                    int width, int height,
                    float[] kernel, int radius) {
       float a;
       float r;
       float g;
       float b;
       int ca;
       int cr;
       int cg;
       int cb;
       for (int y = 0; y < height; y++) {
           int index = y;
           int offset = y * width;
           for (int x = 0; x < width; x++) {
               a = r = g = b = 0.0f;
               for (int i = -radius; i <= radius; i++) {
                   int subOffset = x + i;
                   if (subOffset < 0 || subOffset >= width) {
                       subOffset = (x + width) % width;
                   }
                   int pixel = srcPixels[offset + subOffset];
                   float blurFactor = kernel[radius + i];
                   a += blurFactor * ((pixel >> 24) & 0xFF);
                   r += blurFactor * ((pixel >> 16) & 0xFF);
                   g += blurFactor * ((pixel >>  8) & 0xFF);
                   b += blurFactor * ((pixel      ) & 0xFF);
               }
               ca = (int) (a + 0.5f);
               cr = (int) (r + 0.5f);
               cg = (int) (g + 0.5f);
               cb = (int) (b + 0.5f);
               dstPixels[index] = ((ca > 255 ? 255 : ca) << 24) |
                                  ((cr > 255 ? 255 : cr) << 16) |
                                  ((cg > 255 ? 255 : cg) <<  8) |
                                   (cb > 255 ? 255 : cb);
               index += height;
           }
       }
   }
   static float[] createGaussianKernel(int radius) {
       if (radius < 1) {
           throw new IllegalArgumentException("Radius must be >= 1");
       }
       float[] data = new float[radius * 2 + 1];
       float sigma = radius / 3.0f;
       float twoSigmaSquare = 2.0f * sigma * sigma;
       float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
       float total = 0.0f;
       for (int i = -radius; i <= radius; i++) {
           float distance = i * i;
           int index = i + radius;
           data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
           total += data[index];
       }
       for (int i = 0; i < data.length; i++) {
           data[i] /= total;
       }
       return data;
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class ImageTransferHandler extends TransferHandler {

   @Override
   public boolean canImport(TransferSupport support) {
       if (!support.isDrop()) {
           return false;
       }
       if (!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
           return false;
       }
       boolean copySupported = (COPY & support.getSourceDropActions()) == COPY;
       if (copySupported) {
           support.setDropAction(COPY);
           return true;
       }
       return false;
   }
   @Override
   public boolean importData(TransferSupport support) {
       if (!support.isDrop()) {
           return false;
       }
       if (!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
           return false;
       }
       Transferable t = support.getTransferable();
       try {
           Object data =
                   t.getTransferData(DataFlavor.javaFileListFlavor);
           final File file = ((List<File>) data).get(0);
           Thread loader = new Thread(new Runnable() {
               public void run() {
                   //Application.getMainFrame().showWaitGlassPane();
                   try {
                       BufferedImage image = GraphicsUtilities
                               .loadCompatibleImage(file.toURI().toURL());
                       Application.getMainFrame().setImage(image, file.getName());
                   } catch (IOException e) {
                       e.printStackTrace();
                       Application.getMainFrame().hideWaitGlassPane();
                   }
               }
           });
           loader.start();
       } catch (UnsupportedFlavorException e) {
           return false;
       } catch (MalformedURLException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
       return true;
   }

}

final class Reflection {

   private Reflection() {
   }
   public static BufferedImage createReflection(BufferedImage image) {
       BufferedImage mask = createGradientMask(image.getWidth(),
                                               image.getHeight());
       return createReflectedPicture(image, mask);
   }
   public static BufferedImage createReflectedPicture(BufferedImage avatar,
                                                      BufferedImage alphaMask) {
       int avatarWidth = avatar.getWidth() + 6;
       int avatarHeight = avatar.getHeight();
       BufferedImage buffer = createReflection(avatar,
                                               avatarWidth, avatarHeight);
       applyAlphaMask(buffer, alphaMask, avatarHeight);
       return buffer;/*.getSubimage(0, 0, avatarWidth, avatarHeight * 3 / 2)*/
   }
   private static void applyAlphaMask(BufferedImage buffer,
                                      BufferedImage alphaMask,
                                      int avatarHeight) {
       Graphics2D g2 = buffer.createGraphics();
       g2.setComposite(AlphaComposite.DstOut);
       g2.drawImage(alphaMask, null, 0, avatarHeight);
       g2.dispose();
   }
   private static BufferedImage createReflection(BufferedImage avatar,
                                                 int avatarWidth,
                                                 int avatarHeight) {
       BufferedImage buffer = new BufferedImage(avatarWidth, avatarHeight * 5 / 3,
                                                BufferedImage.TYPE_INT_ARGB);
       Graphics2D g = buffer.createGraphics();
       g.drawImage(avatar, null, null);
       g.translate(0, avatarHeight * 2);
       g.scale(1.0, -1.0);
       FastBlurFilter filter = new FastBlurFilter(3);
       g.drawImage(avatar, filter, 0, 0);
       g.dispose();
       return buffer;
   }
   public static BufferedImage createGradientMask(int avatarWidth,
                                                  int avatarHeight) {
       return createGradientMask(avatarWidth, avatarHeight, 0.7f, 1.0f);
   }
   public static BufferedImage createGradientMask(int avatarWidth,
                                                  int avatarHeight,
                                                  float opacityStart,
                                                  float opacityEnd) {
       BufferedImage gradient = new BufferedImage(avatarWidth, avatarHeight,
                                                  BufferedImage.TYPE_INT_ARGB);
       Graphics2D g = gradient.createGraphics();
       GradientPaint painter = new GradientPaint(0.0f, 0.0f,
                                                 new Color(1.0f, 1.0f, 1.0f, opacityStart),
                                                 0.0f, avatarHeight / 2.0f,
                                                 new Color(1.0f, 1.0f, 1.0f, opacityEnd));
       g.setPaint(painter);
       g.fill(new Rectangle2D.Double(0, 0, avatarWidth, avatarHeight));
       g.dispose();
       return gradient;
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class GradientPanel extends JPanel {

   GradientPanel() {
       super(new BorderLayout());
   }
   @Override
   protected void paintComponent(Graphics g) {
       Graphics2D g2 = (Graphics2D) g;
       Rectangle clip = g2.getClipBounds();
       Paint paint = g2.getPaint();
       g2.setPaint(new GradientPaint(0.0f, getHeight() * 0.22f, new Color(0x202737),
                                     0.0f, getHeight() * 0.9f, Color.BLACK));
       g2.fillRect(clip.x, clip.y, clip.width, clip.height);
       g2.setPaint(paint);
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class ProgressGlassPane extends JComponent {

   private static final int BAR_WIDTH = 200;
   private static final int BAR_HEIGHT = 10;
   private static final Color TEXT_COLOR = new Color(0xFFFFFF);
   private static final float[] GRADIENT_FRACTIONS = new float[] {
       0.0f, 0.499f, 0.5f, 1.0f
   };
   private static final Color[] GRADIENT_COLORS = new Color[] {
       Color.GRAY, Color.DARK_GRAY, Color.BLACK, Color.GRAY
   };
   private static final Color GRADIENT_COLOR2 = Color.WHITE;
   private static final Color GRADIENT_COLOR1 = Color.GRAY;
   private String message = "Please wait...";
   private int progress = 0;
   private BufferedImage backDrop = null;
   public ProgressGlassPane() {
       setBackground(Color.WHITE);
       setFont(new Font("Helvetica", Font.BOLD, 16)); // NON-NLS
       setOpaque(true);
   }
   public int getProgress() {
       return progress;
   }
   @Override
   public void setVisible(boolean visible) {
       if (visible) {
           MainFrame mainFrame = Application.getMainFrame();
           backDrop = GraphicsUtilities.createCompatibleImage(mainFrame.getRootPane().getWidth(),
                                                              mainFrame.getRootPane().getHeight());
           Graphics2D g2 = backDrop.createGraphics();
           mainFrame.getRootPane().paint(g2);
           g2.dispose();
           backDrop = GraphicsUtilities.createThumbnail(backDrop,
                                                        mainFrame.getRootPane().getWidth() / 2);
           backDrop = new ColorTintFilter(Color.BLACK, 0.10f).filter(backDrop, null);
           backDrop = new GaussianBlurFilter(12).filter(backDrop, null);
       } else {
           if (backDrop != null) {
               backDrop.flush();
           }
           backDrop = null;
       }
       super.setVisible(visible);
   }
   public void setProgress(int progress) {
       int oldProgress = this.progress;
       this.progress = progress;
       if (progress > oldProgress) {
           // computes the damaged area
           FontMetrics metrics = getGraphics().getFontMetrics(getFont());
           int w = (int) (BAR_WIDTH * ((float) oldProgress / 100.0f));
           int x = w + (getWidth() - BAR_WIDTH) / 2;
           int y = (getHeight() - BAR_HEIGHT) / 2;
           y += metrics.getDescent() / 2 + 2;
           w = (int) (BAR_WIDTH * ((float) progress / 100.0f)) - w;
           int h = BAR_HEIGHT;
           repaint(x, y, w, h);
       } else {
           FontMetrics metrics = getGraphics().getFontMetrics(getFont());
           int w = (int) (BAR_WIDTH * ((float) oldProgress / 100.0f));
           int x = w + (getWidth() - BAR_WIDTH) / 2;
           int y = (getHeight() - BAR_HEIGHT) / 2;
           y += metrics.getDescent() / 2 + 2;
           w = (int) (BAR_WIDTH * ((float) progress / 100.0f)) + w;
           int h = BAR_HEIGHT;
           repaint(x, y, w, h);
       }
   }
   @Override
   protected void paintComponent(Graphics g) {
       // enables anti-aliasing
       Graphics2D g2 = (Graphics2D) g;
       g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       MainFrame mainFrame = Application.getMainFrame();
       int width = mainFrame.getRootPane().getWidth();
       int height = mainFrame.getRootPane().getHeight();
       g2.drawImage(backDrop, 0, 0, width, height, null);
       // sets a 65% translucent composite
       AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.75f);
       Composite composite = g2.getComposite();
       // centers the progress bar on screen
       FontMetrics metrics = g.getFontMetrics();
       int x = (getWidth() - BAR_WIDTH) / 2;
       int y = (getHeight() - BAR_HEIGHT - metrics.getDescent()) / 2;
       g2.setComposite(alpha);
       g2.setColor(Color.BLACK);
       g2.fillRoundRect(x - 15, y - metrics.getAscent() - 7,
                        BAR_WIDTH + 30, BAR_HEIGHT + 22 + metrics.getAscent(),
                        20, 20);
       g2.setComposite(composite);
       // draws the text
       g2.setColor(TEXT_COLOR);
       g2.drawString(message, x, y);
       // goes to the position of the progress bar
       y += metrics.getDescent() + 2;
       // computes the size of the progress indicator
       int w = (int) (BAR_WIDTH * ((float) progress / 100.0f));
       int h = BAR_HEIGHT;
       // draws the content of the progress bar
       Paint paint = g2.getPaint();
       g2.setComposite(alpha);
       // bar"s background
       Paint gradient = new GradientPaint(x, y, GRADIENT_COLOR1,
                                          x, y + h, GRADIENT_COLOR2);
       g2.setPaint(gradient);
       g2.fillRect(x, y, BAR_WIDTH, BAR_HEIGHT);
       // actual progress
       gradient = new LinearGradientPaint(x, y, x, y + h,
                                          GRADIENT_FRACTIONS, GRADIENT_COLORS);
       g2.setPaint(gradient);
       g2.fillRect(x, y, w, h);
       g2.setPaint(paint);
       // draws the progress bar border
       g2.setColor(Color.DARK_GRAY);
       g2.drawRect(x, y, BAR_WIDTH, BAR_HEIGHT);
       g2.setComposite(composite);

   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class StepLabel extends JLabel {

   private int step;
   private boolean rebuildIcon;
   public StepLabel() {
       setBackground(Color.BLACK);
       setForeground(Color.WHITE);
       setHorizontalAlignment(CENTER);
       setIconTextGap(12);
       //setOpaque(true);
       setFont(new Font("Helvetica", Font.PLAIN, 24));
       setStep(1);
       setText(" ");
   }
   public void setStep(int step) {
       this.step = step;
       this.rebuildIcon = true;
       repaint();
   }
   public int getStep() {
       return step;
   }
   @Override
   protected void paintComponent(Graphics g) {
       if (rebuildIcon) {
           FontMetrics fontMetrics = g.getFontMetrics();
           int height = (int) (fontMetrics.getHeight() / 1.5 + 2);
           BufferedImage image = GraphicsUtilities.createTranslucentCompatibleImage(height, height);
           Graphics2D g2 = image.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                               RenderingHints.VALUE_ANTIALIAS_ON);
           g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                               RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
           g2.setFont(getFont().deriveFont(Font.BOLD, 20));
           fontMetrics = g2.getFontMetrics();
           String s = String.valueOf(step);
           int x = (height - fontMetrics.stringWidth(s)) / 2;
           int y = height - fontMetrics.getAscent() / 8;
           //g2.setColor(Color.WHITE);
           Paint paint = g2.getPaint();
           g2.setPaint(new GradientPaint(0.0f, 0.0f, Color.WHITE,
                                         0.0f, height, Color.GRAY));
           g2.fillOval(0, 0, height, height);
           g2.setPaint(paint);
           //g2.setColor(Color.BLACK);
           g2.setComposite(AlphaComposite.Clear);
           g2.drawString(s, x, y);
           g2.dispose();
           setIcon(new ImageIcon(image));
           rebuildIcon = false;
       }
       super.paintComponent(g);
   }

} /*

* $Id: GraphicsUtilities.java,v 1.1 2007/01/15 16:12:03 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private static final GraphicsConfiguration CONFIGURATION =
           GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   private GraphicsUtilities() {
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createTranslucentCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createTranslucentCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return CONFIGURATION.createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createTranslucentCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return CONFIGURATION.createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createTranslucentCompatibleImage(int width,
                                                                int height) {
       return CONFIGURATION.createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createTranslucentCompatibleImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createTranslucentCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(CONFIGURATION.getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage = CONFIGURATION.createCompatibleImage(
               image.getWidth(), image.getHeight(), image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class SizeStepPanel extends JPanel {

   private BufferedImage small;
   private BufferedImage medium;
   private BufferedImage large;
   private boolean isLoaded;
   private int smallHeight;
   private int mediumHeight;
   private int largeHeight;
   private Rectangle largeImageBounds;
   private Rectangle mediumImageBounds;
   private Rectangle smallImageBounds;
   public SizeStepPanel() {
       setBackground(Color.BLACK);
       setOpaque(false);
       createThumbnails();
       addMouseMotionListener(new CursorChanger());
       addMouseListener(new SizeSelector());
   }
   private void createThumbnails() {
       Thread loader = new Thread(new Runnable() {
           public void run() {
               final MainFrame mainFrame = Application.getMainFrame();
               BufferedImage image = mainFrame.getImage();
               small = GraphicsUtilities.createThumbnail(image, 90);
               smallHeight = small.getHeight();
               small = Reflection.createReflection(small);
               medium = GraphicsUtilities.createThumbnail(image, 160);
               mediumHeight = medium.getHeight();
               medium = Reflection.createReflection(medium);
               large = GraphicsUtilities.createThumbnail(image, 240);
               largeHeight = large.getHeight();
               large = Reflection.createReflection(large);
               SwingUtilities.invokeLater(new Runnable() {
                   public void run() {
                       mainFrame.hideWaitGlassPane();
                   }
               });
               isLoaded = true;
               repaint();
           }
       });
       loader.start();
   }
   
   public void dispose() {
       if (large != null) {
           large.flush();
           large = null;
       }
       if (medium != null) {
           medium.flush();
           medium = null;
       }
       if (small != null) {
           small.flush();
           small = null;
       }
   }
   @Override
   protected void paintComponent(Graphics g) {
       super.paintComponent(g);
       if (!isLoaded) {
           return;
       }
       int totalWidth = 24 * 2;
       totalWidth += large.getWidth() + medium.getWidth() + small.getWidth();
       int x = (getWidth() - totalWidth) / 2;
       int y = (getHeight() - large.getHeight()) / 2;
       y += 42;
       g.drawImage(large, x, y, null);
       if (largeImageBounds == null) {
           largeImageBounds = new Rectangle(x, y, large.getWidth(), largeHeight);
       }
       x += large.getWidth() + 24;
       y += largeHeight - mediumHeight;
       g.drawImage(medium, x, y, null);
       if (mediumImageBounds == null) {
           mediumImageBounds = new Rectangle(x, y, medium.getWidth(), mediumHeight);
       }
       x += medium.getWidth() + 24;
       y += mediumHeight - smallHeight;
       g.drawImage(small, x, y, null);
       if (smallImageBounds == null) {
           smallImageBounds = new Rectangle(x, y, small.getWidth(), smallHeight);
       }
   }
   private static void saveImage(final BufferedImage image, final File file) {
       Thread writer = new Thread(new Runnable() {
           public void run() {
               try {
                   ImageIO.write(image, "JPEG", file); // NON-NLS
               } catch (IOException e) {
                   e.printStackTrace();
               }
               SwingUtilities.invokeLater(new Runnable() {
                   public void run() {
                       Application.getMainFrame().showDoneStep();
                   }
               });
           }
       });
       writer.start();
   }
   private class SizeSelector extends MouseAdapter {
       @Override
       public void mouseClicked(MouseEvent e) {
           Point point = e.getPoint();
           for (final Rectangle r : new Rectangle[] { largeImageBounds, mediumImageBounds, smallImageBounds }) {
               if (r != null && r.contains(point)) {
                   Application.getMainFrame().showWaitGlassPane();
                   Thread sizer = new Thread(new Runnable() {
                       public void run() {
                           int width;
                           if (r == largeImageBounds) {
                               width = 1024;
                           } else if (r == mediumImageBounds) {
                               width = 800;
                           } else {
                               width = 640;
                           }
                           final BufferedImage toSave =
                                   GraphicsUtilities.createThumbnail(
                                           Application.getMainFrame().getImage(), width);
                           SwingUtilities.invokeLater(new Runnable() {
                               public void run() {
                                   JFileChooser chooser = new JFileChooser();
                                   chooser.setSelectedFile(new File(
                                           "x",Application
                                                                        .getMainFrame().getFileName()
                                                                    )
                                                           );
                                   dispose();
                                   if (chooser.showSaveDialog(Application.getMainFrame()) == JFileChooser.APPROVE_OPTION) {
                                       File selectedFile = chooser.getSelectedFile();
                                       if (!selectedFile.getPath().toLowerCase().endsWith(".jpg")) { // NON-NLS
                                           selectedFile = new File(selectedFile.getPath() + ".jpg"); // NON-NLS
                                       }
                                       saveImage(toSave, selectedFile);
                                   } else {
                                       Application.getMainFrame().showDragAndDropStep();
                                   }
                               }
                           });
                       }
                   });
                   sizer.start();
               }
           }
       }
   }
   private class CursorChanger extends MouseAdapter {
       @Override
       public void mouseMoved(MouseEvent e) {
           Point point = e.getPoint();
           if ((largeImageBounds != null && largeImageBounds.contains(point)) ||
               (mediumImageBounds != null && mediumImageBounds.contains(point)) ||
               (smallImageBounds != null && smallImageBounds.contains(point))) {
               setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
           } else if (!getCursor().equals(Cursor.getDefaultCursor())) {
               setCursor(Cursor.getDefaultCursor());
           }
       }
   }

}

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//package org.progx.artemis; //import java.util.ResourceBundle; //import javax.swing.SwingUtilities; //import javax.swing.UIManager; //import javax.swing.UnsupportedLookAndFeelException; //import org.progx.artemis.ui.MainFrame; class Application {

   private static MainFrame mainFrame = new MainFrame();
   private static ResourceBundle bundle;
   private Application() {
   }
   public static MainFrame getMainFrame() {
       mainFrame.setVisible(true);
       
       return mainFrame;
   }
   public static void main(String[] args) {
   }
   public static synchronized ResourceBundle getResourceBundle() {
       if (bundle == null) {
           bundle = ResourceBundle.getBundle("org.progx.artemis.messages"); // NON-NLS
       }
       return bundle;
   }

}



 </source>
   
  
 
  



Blur our image: Blur means an unfocused image

   <source lang="java">
 

import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; public class BlurredImage extends JPanel {

 public void paint(Graphics g) {
   try {
     BufferedImage myImage = ImageIO.read(this.getClass().getResource("redrock.png"));
     BufferedImage filteredImage = new BufferedImage(myImage.getWidth(null), myImage
         .getHeight(null), BufferedImage.TYPE_BYTE_GRAY);
     Graphics g1 = filteredImage.getGraphics();
     g1.drawImage(myImage, 400, 200, null);
     float[] blurKernel = { 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f };
     BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel));
     myImage = blur.filter(myImage, null);
     g1.dispose();
     Graphics2D g2d = (Graphics2D) g;
     g2d.drawImage(myImage, null, 3, 3);
   } catch (Exception e) {
   }
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame("Blurred Image");
   frame.add(new BlurredImage());
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setSize(400,400);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Box Blur Demo

   <source lang="java">
 

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.io.IOException; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

* @author Romain Guy <romain.guy@mac.ru>
*/

public class BoxBlurDemo extends JFrame {

   private BlurTestPanel blurTestPanel;
   private JSlider radiusSlider;
   private JCheckBox fasterBlurCheck;
   public BoxBlurDemo() {
       super("Box Blur");
       blurTestPanel = new BlurTestPanel();
       add(blurTestPanel);
       radiusSlider = new JSlider(1, 10, 1);
       radiusSlider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               blurTestPanel.setRadius(radiusSlider.getValue());
           }
       });
       
       fasterBlurCheck = new JCheckBox("2-steps blur");
       fasterBlurCheck.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               blurTestPanel.setFastBlur(fasterBlurCheck.isSelected());
           }
       });
       JPanel controls = new JPanel(new FlowLayout(FlowLayout.LEFT));
       controls.add(new JLabel("Radius: 1px"));
       controls.add(radiusSlider);
       controls.add(new JLabel("10px"));
       
       controls.add(Box.createHorizontalStrut(12));
       controls.add(fasterBlurCheck);
       add(controls, BorderLayout.SOUTH);
       pack();
       setLocationRelativeTo(null);
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   private static class BlurTestPanel extends JPanel {
       private BufferedImage image = null;
       private BufferedImage imageA;
       private int radius = 1;
       private boolean fasterBlur = false;
       public BlurTestPanel() {
           try {
               imageA = GraphicsUtilities.loadCompatibleImage(getClass().getResource("A.jpg"));
           } catch (IOException e) {
               e.printStackTrace();
           }
           setOpaque(false);
       }
       @Override
       public Dimension getPreferredSize() {
           return new Dimension(imageA.getWidth(), imageA.getHeight());
       }
       @Override
       protected void paintComponent(Graphics g) {
           if (image == null) {
               image = new BufferedImage(imageA.getWidth() + 2 * radius,
                                         imageA.getHeight() + 2 * radius,
                                         BufferedImage.TYPE_INT_ARGB);
               Graphics2D g2 = image.createGraphics();
               g2.drawImage(imageA, radius, radius, null);
               g2.dispose();
               long start = System.nanoTime();
               if (!fasterBlur) {
                   image = getBlurFilter(radius).filter(image, null);
               } else {
                   image = getBlurFilter(radius, 0).filter(image, null);
                   image = getBlurFilter(0, radius).filter(image, null);
               }
               long delay = System.nanoTime() - start;
               System.out.println("time = " + (delay / 1000.0f / 1000.0f) + "ms");
           }
           int x = (getWidth() - image.getWidth()) / 2;
           int y = (getHeight() - image.getHeight()) / 2;
           g.drawImage(image, x, y, null);
       }
       public void setRadius(int radius) {
           this.radius = radius;
           image = null;
           repaint();
       }
       private void setFastBlur(boolean fasterBlur) {
           this.fasterBlur = fasterBlur;
           image = null;
           repaint();
       }
   }
   
   public static ConvolveOp getBlurFilter(int horizontalRadius,
           int verticalRadius) {
       int width = horizontalRadius * 2 + 1;
       int height = verticalRadius * 2 + 1;
       float weight = 1.0f / (width * height);
       float[] data = new float[width * height];
       
       for (int i = 0; i < data.length; i++) {
           data[i] = weight;
       }
       
       Kernel kernel = new Kernel(width, height, data);
       return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
   }
   
   public static ConvolveOp getBlurFilter(int radius) {
       if (radius < 1) {
           throw new IllegalArgumentException("Radius must be >= 1");
       }
       
       int size = radius * 2 + 1;
       float weight = 1.0f / (size * size);
       float[] data = new float[size * size];
       
       for (int i = 0; i < data.length; i++) {
           data[i] = weight;
       }
       
       Kernel kernel = new Kernel(size, size, data);
       return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
   }
   public static void main(String... args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new BoxBlurDemo().setVisible(true);
           }
       });
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

}


 </source>
   
  
 
  



Brighten the image by 30%

   <source lang="java">

import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   float scaleFactor = 1.3f;
   RescaleOp op = new RescaleOp(scaleFactor, 0, null);
   bufferedImage = op.filter(bufferedImage, null);
 }

}

 </source>
   
  
 
  



Brightness Increase Demo

   <source lang="java">
 


import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class BrightnessIncreaseDemo extends JFrame {

   private JLabel textLabel;
   private JLabel imageLabel;
   
   private BufferedImage image;
   public BrightnessIncreaseDemo() {
       super("Brightness Increase");
       
       getContentPane().setLayout(new FlowLayout());
       
       textLabel = new JLabel("Hover Me");
       textLabel.setForeground(new Color(0, 0, 120));
       textLabel.addMouseListener(new MouseAdapter() {
           @Override
           public void mouseEntered(MouseEvent e) {
              increaseTextBrightness((JComponent) e.getSource());
           } 
           @Override
           public void mouseExited(MouseEvent e) {
              decreaseTextBrightness((JComponent) e.getSource());
           }
       });
       add(textLabel);
       try {
           image = GraphicsUtilities.loadCompatibleImage(getClass().getResource("A.png"));
       } catch (IOException ex) {
           ex.printStackTrace();
       }
       
       imageLabel = new JLabel(new ImageIcon(image));
       imageLabel.addMouseListener(new MouseAdapter() {
           @Override
           public void mouseEntered(MouseEvent e) {
              increaseImageBrightness((JLabel) e.getSource(), image);
           } 
           @Override
           public void mouseExited(MouseEvent e) {
              decreaseImageBrightness((JLabel) e.getSource(), image);
           }
       });
       add(imageLabel);
       
       pack();
       setLocationRelativeTo(null);
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   
   public static void increaseImageBrightness(JLabel c, BufferedImage image) {
       float[] factors = new float[] {
           1.4f, 1.4f, 1.4f, 1.4f
       };
       float[] offsets = new float[] {
           0.0f, 0.0f, 0.0f, 0.0f
       };
       RescaleOp op = new RescaleOp(factors, offsets, null);
       BufferedImage brighter = op.filter(image, null);
       c.setIcon(new ImageIcon(brighter));
   }
   
   public static void decreaseImageBrightness(JLabel c, BufferedImage image) {
       c.setIcon(new ImageIcon(image));
   }
   
   public static void increaseTextBrightness(JComponent c) {
       Color color = c.getForeground();
       c.putClientProperty("mouseover_brightness", color);
       float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
       
       hsb[2] = Math.min(1.0f, hsb[2] * 2.0f);
       c.setForeground(Color.getHSBColor(hsb[0], hsb[1], hsb[2]));
   }
   
   public static void decreaseTextBrightness(JComponent c) {
       Color color = (Color) c.getClientProperty("mouseover_brightness");
       c.setForeground(color);
   }
   
   public static void main(String... args) {
       try {
           UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
       } catch (UnsupportedLookAndFeelException ex) {
           ex.printStackTrace();
       } catch (ClassNotFoundException ex) {
           ex.printStackTrace();
       } catch (IllegalAccessException ex) {
           ex.printStackTrace();
       } catch (InstantiationException ex) {
           ex.printStackTrace();
       }
       
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new BrightnessIncreaseDemo().setVisible(true);
           }
       });
   }

}

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

}


 </source>
   
  
 
  



Converting a Buffered Image (BufferedImage) from an Image

   <source lang="java">
 

import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_3BYTE_BGR);
   Image img = Toolkit.getDefaultToolkit().createImage(
       bufferedImage.getSource());
 }

}


 </source>
   
  
 
  



Create a grayscale image with Java 2D tools

   <source lang="java">
 

import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; public class GrayImage extends JPanel {

 public GrayImage() {
 }
 public void paint(Graphics g) {
   Image myImage = new ImageIcon("yourImage.png").getImage();
   BufferedImage bufferedImage = new BufferedImage(myImage.getHeight(this), myImage.getWidth(this), BufferedImage.TYPE_BYTE_GRAY);
   Graphics gi = bufferedImage.getGraphics();
   gi.drawImage(myImage, 0, 0, null);
   gi.dispose();
   
   Graphics2D g2d = (Graphics2D) g;
   g2d.drawImage(bufferedImage, null, 0, 0);
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame();
   frame.add(new GrayImage());
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setSize(400,400);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Create an image that does not support transparency

   <source lang="java">
 

import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Transparency; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; public class BasicShapes extends JPanel {

 public void paint(Graphics g) {
   Graphics2D g2d = (Graphics2D) g;
   int width = 100;
   int height = 100;
   BufferedImage bimage = g2d.getDeviceConfiguration().createCompatibleImage(
       width, height, Transparency.OPAQUE);
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame("Basic Shapes");
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.add(new BasicShapes());
   frame.setSize(350, 250);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Create an image that supports arbitrary levels of transparency

   <source lang="java">
 

import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Transparency; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; public class BasicShapes extends JPanel {

 public void paint(Graphics g) {
   Graphics2D g2d = (Graphics2D) g;
   int width = 100;
   int height = 100;
   BufferedImage bimage = g2d.getDeviceConfiguration().createCompatibleImage(
       width, height, Transparency.TRANSLUCENT);
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame("Basic Shapes");
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.add(new BasicShapes());
   frame.setSize(350, 250);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Create an image that supports transparent pixels

   <source lang="java">
 

import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Transparency; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; public class BasicShapes extends JPanel {

 public void paint(Graphics g) {
   Graphics2D g2d = (Graphics2D) g;
   int width = 100;
   int height = 100;
   BufferedImage bimage = g2d.getDeviceConfiguration().createCompatibleImage(
       width, height, Transparency.BITMASK);
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame("Basic Shapes");
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.add(new BasicShapes());
   frame.setSize(350, 250);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Creating a Buffered Image from an Image

   <source lang="java">
 

import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage img = toBufferedImage(new ImageIcon("a.png").getImage());
 }
 public static BufferedImage toBufferedImage(Image image) {
   image = new ImageIcon(image).getImage();
   BufferedImage bimage = new BufferedImage(image.getWidth(null), image
       .getHeight(null), BufferedImage.TYPE_INT_ARGB);
   Graphics g = bimage.createGraphics();
   g.drawImage(image, 0, 0, null);
   g.dispose();
   return bimage;
 }

}


 </source>
   
  
 
  



Creating a buffered image using Component.createImage().

   <source lang="java">
 

//use only if the component is visible on the screen. //It returns buffered images that do not support transparent pixels. import java.awt.image.BufferedImage; import javax.swing.JFrame; public class Main {

 public static void main(String[] argv) throws Exception {
   JFrame f = new JFrame();
   f.setVisible(true);
   int width = 300;
   int height = 500;
   BufferedImage bimage = (BufferedImage) f.createImage(width, height);
   if (bimage == null) {
     System.out.println("component is not visible on the screen");
   }
 }

}


 </source>
   
  
 
  



Creating a Image Zoomer using Graphics2D

   <source lang="java">
 

import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import javax.swing.JPanel; class ImagePanel extends JPanel {

 private double zoom = 1.0;
 private double percentage;
 private Image image;
 public ImagePanel(Image image, double zoomPercentage) {
   this.image = image;
   percentage = zoomPercentage / 100;
 }
 public void paintComponent(Graphics grp) {
   Graphics2D g2D = (Graphics2D) grp;
   g2D.scale(zoom, zoom);
   g2D.drawImage(image, 0, 0, this);
 }
 public void setZoomPercentage(int zoomPercentage) {
   percentage = ((double) zoomPercentage) / 100;
 }
 public void originalSize() {
   zoom = 1;
 }
 public void zoomIn() {
   zoom += percentage;
 }
 public void zoomOut() {
   zoom -= percentage;
   if (zoom < percentage) {
     if (percentage > 1.0) {
       zoom = 1.0;
     } else {
       zoomIn();
     }
   }
 }

}


 </source>
   
  
 
  



Darken the image by 10%

   <source lang="java">

import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   RescaleOp op = new RescaleOp(.9f, 0, null);
   bufferedImage = op.filter(bufferedImage, null);
 }

}

 </source>
   
  
 
  



Drawing on a Buffered Image

   <source lang="java">
 

import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; public class BasicShapes {

 public static void main(String[] args) {
   BufferedImage bimage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   Graphics2D g2d = bimage.createGraphics();
   g2d.setColor(Color.red);
   g2d.fill(new Ellipse2D.Float(0, 0, 200, 100));
   g2d.dispose();
 }

}


 </source>
   
  
 
  



Embossing a Buffered Image

   <source lang="java">

import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   Kernel kernel = new Kernel(3, 3, new float[] { -2, 0, 0, 0, 1, 0, 0, 0, 2 });
   BufferedImageOp op = new ConvolveOp(kernel);
   bufferedImage = op.filter(bufferedImage, null);
 }

}

 </source>
   
  
 
  



Enlarge Image With Animation

   <source lang="java">
 

/* Steven Garrity <sgarrity@silverorange.ru> Lapo Calamandrei <calamandrei@gmail.ru> Ryan Collier <rcollier@novell.ru> Rodney Dawes <dobey@novell.ru> Andreas Nilsson <nisses.mail@home.se> Tuomas Kuosmanen <tigert@tigert.ru> Garrett LeSage <garrett@novell.ru> Jakub Steiner <jimmac@novell.ru> Creative Commons Attribution-ShareAlike 2.5 License Agreement CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions

  1. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  2. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  3. "Licensor" means the individual or entity that offers the Work under the terms of this License.
  4. "Original Author" means the individual or entity who created the Work.
  5. "Work" means the copyrightable work of authorship offered under the terms of this License.
  6. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  7. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  1. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  2. to create and reproduce Derivative Works;
  3. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  4. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  5.
     For the avoidance of doubt, where the work is a musical composition:
        1. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
        2. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
  6. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).

The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved. 4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  1. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients" exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  2. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients" exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  3. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor"s copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination

  1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  1. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  2. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.

Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons" then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. Creative Commons may be contacted at http://creativecommons.org/.

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruponent; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class SpringDemo extends JFrame {

   private JList list;
   private SpringGlassPane glassPane;
   public SpringDemo() {
       super("Spring Demo");
       
       setupGlassPane();
       
       add(Box.createVerticalStrut(16), BorderLayout.NORTH);
       add(Box.createHorizontalStrut(16), BorderLayout.WEST);
       add(buildList());
       add(Box.createHorizontalStrut(16), BorderLayout.EAST);
       add(Box.createVerticalStrut(16), BorderLayout.SOUTH);
       
       pack();
       setLocationRelativeTo(null);
       setResizable(false);
       setDefaultCloseOperation(EXIT_ON_CLOSE);
   }
   
   private void setupGlassPane() {
       glassPane = new SpringGlassPane();
       setGlassPane(glassPane);
       glassPane.setVisible(true);
   }
   
   private JComponent buildList() {
       Application[] elements = new Application[] {
           new Application("Address Book", "x-office-address-book.png"),
           new Application("Calendar",     "x-office-calendar.png"),
           new Application("Presentation", "x-office-presentation.png"),
           new Application("Spreadsheet",  "x-office-spreadsheet.png"),
       };
       
       list = new JList(elements);
       list.setCellRenderer(new ApplicationListCellRenderer());
       list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
       list.setVisibleRowCount(2);
       list.setBorder(BorderFactory.createEtchedBorder());
       list.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    int index = list.getSelectedIndex();
                    
                    Rectangle bounds = list.getCellBounds(index, index);
                    Point location = new Point(bounds.x, bounds.y);
                    location = SwingUtilities.convertPoint(list, location, glassPane);
                    location.y -= 13;
                    bounds.setLocation(location);
                    
                    glassPane.showSpring(bounds,
                            ((Application) list.getSelectedValue()).icon.getImage());
                }
            }
        });
       
       JPanel panel = new JPanel(new GridBagLayout());
       panel.add(new JLabel("Launcher"),
               new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
                   GridBagConstraints.LINE_START, GridBagConstraints.NONE,
                   new Insets(0, 0, 0, 0), 0, 0));
       panel.add(list, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
               GridBagConstraints.CENTER, GridBagConstraints.NONE,
               new Insets(0, 0, 0, 0), 0, 0));
       panel.add(new JLabel("Double-click an icon to launch the program"),
               new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0,
                   GridBagConstraints.LINE_START, GridBagConstraints.NONE,
                   new Insets(0, 0, 0, 0), 0, 0));
       
       return panel;
   }
   
   public static class SpringGlassPane extends JComponent {
       private static final float MAGNIFY_FACTOR = 1.5f;
       
       private Rectangle bounds;
       private Image image;
       
       private float zoom = 0.0f;
       @Override
       protected void paintComponent(Graphics g) {
           if (image != null && bounds != null) {
               int width = image.getWidth(this);
               width += (int) (image.getWidth(this) * MAGNIFY_FACTOR * getZoom());
               
               int height = image.getHeight(this);
               height += (int) (image.getHeight(this) * MAGNIFY_FACTOR * getZoom());
               
               int x = (bounds.width - width) / 2;
               int y = (bounds.height - height) / 2;
               Graphics2D g2 = (Graphics2D) g.create();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                       RenderingHints.VALUE_INTERPOLATION_BILINEAR);
               
               g2.setComposite(AlphaComposite.SrcOver.derive(1.0f - getZoom()));
               g2.drawImage(image, x + bounds.x, y + bounds.y,
                       width, height, null);
           }
       }
       public void showSpring(Rectangle bounds, Image image) {
           this.bounds = bounds;
           this.image = image;
           
           Animator animator = PropertySetter.createAnimator(250, this,
                   "zoom", 0.0f, 1.0f);
           animator.setAcceleration(0.2f);
           animator.setDeceleration(0.4f);
           animator.start();
           
           repaint();
       }
       public float getZoom() {
           return zoom;
       }
       public void setZoom(float zoom) {
           this.zoom = zoom;
           repaint();
       }
   }
   
   private static class ApplicationListCellRenderer extends DefaultListCellRenderer {
       public Component getListCellRendererComponent(JList list, Object value,
               int index, boolean isSelected, boolean cellHasFocus) {
           JLabel c;
           c = (JLabel) super.getListCellRendererComponent(list, value,
                   index, isSelected, cellHasFocus);
           
           Application element = (Application) value;
           c.setBorder(BorderFactory.createEmptyBorder(32, 32, 32, 32));
           c.setFont(c.getFont().deriveFont(18.0f).deriveFont(Font.BOLD));
           c.setText(element.label);
           c.setIcon(element.icon);
           c.setHorizontalTextPosition(JLabel.CENTER);
           c.setVerticalTextPosition(JLabel.BOTTOM);
           if (isSelected) {
               c.setBackground(new Color(0, 0, 200, 20));
           }
           
           return c;
       }   
   }
   
   private static class Application {
       public ImageIcon icon;
       public String label;
       
       public Application(String label, String icon) {
           this.icon = new ImageIcon(getClass().getResource(icon));
           this.label = label;
       }
   }
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new SpringDemo().setVisible(true);
           }
       });
   }

}



 </source>
   
  
 
  



Enlarging an image by pixel replication

   <source lang="java">

import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
 }
 public static BufferedImage enlarge(BufferedImage image, int n) {
   int w = n * image.getWidth();
   int h = n * image.getHeight();
   BufferedImage enlargedImage = new BufferedImage(w, h, image.getType());
   for (int y = 0; y < h; ++y){
     for (int x = 0; x < w; ++x){
       enlargedImage.setRGB(x, y, image.getRGB(x / n, y / n));
     }
   }
   return enlargedImage;
 }

}

 </source>
   
  
 
  



Extend RGBImageFilter to create AlphaFilter class

   <source lang="java">

import java.awt.image.RGBImageFilter; class AlphaFilter extends RGBImageFilter {

 int alphaLevel;
 public AlphaFilter(int alpha) {
   alphaLevel = alpha;
   canFilterIndexColorModel = true;
 }
 public int filterRGB(int x, int y, int rgb) {
   int alpha = (rgb >> 24) & 0xff;
   alpha = (alpha * alphaLevel) / 255;
   return ((rgb & 0x00ffffff) | (alpha << 24));
 }

}

 </source>
   
  
 
  



Extend RGBImageFilter to create ColorFilter class

   <source lang="java">

import java.awt.image.RGBImageFilter; class ColorFilter extends RGBImageFilter {

 boolean red, green, blue;
 public ColorFilter(boolean r, boolean g, boolean b) {
   red = r;
   green = g;
   blue = b;
   canFilterIndexColorModel = true;
 }
 public int filterRGB(int x, int y, int rgb) {
   int r = red ? 0 : ((rgb >> 16) & 0xff);
   int g = green ? 0 : ((rgb >> 8) & 0xff);
   int b = blue ? 0 : ((rgb >> 0) & 0xff);
   return (rgb & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
 }

}

 </source>
   
  
 
  



Flipping a Buffered Image

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
   tx.translate(0, -bufferedImage.getHeight(null));
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Flip the image horizontally

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
   tx.translate(-bufferedImage.getWidth(null), 0);
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Flip the image vertically and horizontally, equivalent to rotating the image 180 degrees

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = AffineTransform.getScaleInstance(-1, -1);
   tx.translate(-bufferedImage.getWidth(null), -bufferedImage.getHeight(null));
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Gaussian Blur Demo

   <source lang="java">
 

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.io.IOException; import java.util.Arrays; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

* @author Romain Guy <romain.guy@mac.ru>
*/

public class GaussianBlurDemo extends JFrame {

   private BlurTestPanel blurTestPanel;
   private JSlider radiusSlider;
   private JCheckBox fasterBlurCheck;
   public GaussianBlurDemo() {
       super("Gaussian Blur");
       blurTestPanel = new BlurTestPanel();
       add(blurTestPanel);
       radiusSlider = new JSlider(1, 50, 1);
       radiusSlider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               blurTestPanel.setRadius(radiusSlider.getValue());
           }
       });
       
       fasterBlurCheck = new JCheckBox("Resize trick");
       fasterBlurCheck.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               blurTestPanel.setFastBlur(fasterBlurCheck.isSelected());
           }
       });
       JPanel controls = new JPanel(new FlowLayout(FlowLayout.LEFT));
       controls.add(new JLabel("Radius: 1px"));
       controls.add(radiusSlider);
       controls.add(new JLabel("50px"));
       
       controls.add(Box.createHorizontalStrut(12));
       controls.add(fasterBlurCheck);
       add(controls, BorderLayout.SOUTH);
       pack();
       setLocationRelativeTo(null);
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   private static class BlurTestPanel extends JPanel {
       private BufferedImage image = null;
       private BufferedImage imageA;
       private int radius = 1;
       private boolean fasterBlur = false;
       public BlurTestPanel() {
           try {
               imageA = GraphicsUtilities.loadCompatibleImage(getClass().getResource("A.jpg"));
           } catch (IOException e) {
               e.printStackTrace();
           }
           setOpaque(false);
       }
       @Override
       public Dimension getPreferredSize() {
           return new Dimension(imageA.getWidth(), imageA.getHeight());
       }
       @Override
       protected void paintComponent(Graphics g) {
           if (image == null) {
               image = new BufferedImage(imageA.getWidth() + 2 * radius,
                                         imageA.getHeight() + 2 * radius,
                                         BufferedImage.TYPE_INT_ARGB);
               Graphics2D g2 = image.createGraphics();
               g2.drawImage(imageA, radius, radius, null);
               g2.dispose();
               long start = System.nanoTime();
               
               if (fasterBlur) {
                   image = changeImageWidth(image, image.getWidth() / 2);
                   image = getGaussianBlurFilter(radius / 2, true).filter(image, null);
                   image = getGaussianBlurFilter(radius / 2, false).filter(image, null);
                   image = changeImageWidth(image, image.getWidth() * 2);
               } else {
                   image = getGaussianBlurFilter(radius, true).filter(image, null);
                   image = getGaussianBlurFilter(radius, false).filter(image, null);
               }
               
               long delay = System.nanoTime() - start;
               System.out.println("time = " + (delay / 1000.0f / 1000.0f) + "ms for radius = " + radius);
           }
           int x = (getWidth() - image.getWidth()) / 2;
           int y = (getHeight() - image.getHeight()) / 2;
           g.drawImage(image, x, y, null);
       }
       public void setRadius(int radius) {
           this.radius = radius;
           image = null;
           repaint();
       }
       private void setFastBlur(boolean fasterBlur) {
           this.fasterBlur = fasterBlur;
           image = null;
           repaint();
       }
   }
   
   public static BufferedImage changeImageWidth(BufferedImage image, int width) {
       float ratio = (float) image.getWidth() / (float) image.getHeight();
       int height = (int) (width / ratio);
       
       BufferedImage temp = new BufferedImage(width, height,
               image.getType());
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   
   public static void printGaussianBlurFilter(int radius) {
       if (radius < 1) {
           throw new IllegalArgumentException("Radius must be >= 1");
       }
       
       int size = radius * 2 + 1;
       float[] data = new float[size * size];
       
       float sigma = radius / 3.0f;
       float twoSigmaSquare = 2.0f * sigma * sigma;
       float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
       float total = 0.0f;
       
       int index = 0;
       for (int y = -radius; y <= radius; y++) {
           for (int x = -radius; x <= radius; x++) {
               float distance = x * x + y * y;
               data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
               total += data[index];
               System.out.printf("%.3f\t", data[index]);
               index++;
           }
           System.out.println("");
       }
   }
   
   public static ConvolveOp getGaussianBlurFilter(int radius,
           boolean horizontal) {
       if (radius < 1) {
           throw new IllegalArgumentException("Radius must be >= 1");
       }
       
       int size = radius * 2 + 1;
       float[] data = new float[size];
       
       float sigma = radius / 3.0f;
       float twoSigmaSquare = 2.0f * sigma * sigma;
       float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
       float total = 0.0f;
       
       for (int i = -radius; i <= radius; i++) {
           float distance = i * i;
           int index = i + radius;
           data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
           total += data[index];
       }
       
       for (int i = 0; i < data.length; i++) {
           data[i] /= total;
       }        
       
       Kernel kernel = null;
       if (horizontal) {
           kernel = new Kernel(size, 1, data);
       } else {
           kernel = new Kernel(1, size, data);
       }
       return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
   }
   public static void main(String... args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               printGaussianBlurFilter(3);
               new GaussianBlurDemo().setVisible(true);
           }
       });
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

}



 </source>
   
  
 
  



Getting and Setting Pixels in a Buffered Image

   <source lang="java">
 

import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_INT_RGB);
   int rgb = bufferedImage.getRGB(1, 1);
   int w = bufferedImage.getWidth(null);
   int h = bufferedImage.getHeight(null);
   int[] rgbs = new int[w * h];
   bufferedImage.getRGB(0, 0, w, h, rgbs, 0, w);
   rgb = 0xFF00FF00; // green
   bufferedImage.setRGB(1, 1, rgb);
 }

}


 </source>
   
  
 
  



If the buffered image supports transparency

   <source lang="java">
 

import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; public class BasicShapes extends JPanel {

 public void paint(Graphics g) {
   BufferedImage bimage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   Graphics2D g2d = bimage.createGraphics();
   Color transparent = new Color(0, 0, 0, 0);
   g2d.setColor(transparent);
   g2d.setComposite(AlphaComposite.Src);
   g2d.fill(new Rectangle2D.Float(20, 20, 100, 20));
   g2d.dispose();
 }
 public static void main(String[] args) {
   JFrame frame = new JFrame("Basic Shapes");
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.add(new BasicShapes());
   frame.setSize(350, 250);
   frame.setLocationRelativeTo(null);
   frame.setVisible(true);
 }

}


 </source>
   
  
 
  



Image reflection

   <source lang="java">
 


/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Paint; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

* @author Romain Guy <romain.guy@mac.ru>
*/

public class ImageReflectionDemoFilthy extends JFrame {

   private ReflectionPanel reflectionPanel;
   private JSlider opacitySlider;
   private JSlider lengthSlider;
   private JSlider radiusSlider;
   public ImageReflectionDemoFilthy() {
       super("Reflections");
       setContentPane(new GradientPanel());
       reflectionPanel = new ReflectionPanel();
       add(reflectionPanel);
       opacitySlider = new JSlider(0, 100, 35);
       opacitySlider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               reflectionPanel.setOpacity(opacitySlider.getValue() / 100.0f);
           }
       });
       lengthSlider = new JSlider(0, 100, 40);
       lengthSlider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               reflectionPanel.setLength(lengthSlider.getValue() / 100.0f);
           }
       });
       radiusSlider = new JSlider(1, 20, 1);
       radiusSlider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               reflectionPanel.setRadius(radiusSlider.getValue());
           }
       });
       JPanel controls = new JPanel(new GridBagLayout());
       JLabel label;
       controls.setOpaque(false);
       controls.add(label = new JLabel("Opacity: 0%"),
                    new GridBagConstraints(0, 0, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 6, 0, 0),
                                           0, 0));
       label.setForeground(Color.WHITE);
       controls.add(opacitySlider,
                    new GridBagConstraints(1, 0, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 0, 0),
                                           0, 0));
       controls.add(label = new JLabel("100%"),
                    new GridBagConstraints(2, 0, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 0, 0),
                                           0, 0));
       label.setForeground(Color.WHITE);
       controls.add(label = new JLabel("Length: 0%"),
                    new GridBagConstraints(0, 1, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 6, 0, 0),
                                           0, 0));
       label.setForeground(Color.WHITE);
       controls.add(lengthSlider,
                    new GridBagConstraints(1, 1, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 0, 0),
                                           0, 0));
       controls.add(label = new JLabel("100%"),
                    new GridBagConstraints(2, 1, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 0, 0),
                                           0, 0));
       label.setForeground(Color.WHITE);
       controls.add(label = new JLabel("Blur Radius: 1px"),
                    new GridBagConstraints(0, 2, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 6, 0, 0),
                                           0, 0));
       label.setForeground(Color.WHITE);
       controls.add(radiusSlider,
                    new GridBagConstraints(1, 2, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 0, 0),
                                           0, 0));
       controls.add(label = new JLabel("20px"),
                    new GridBagConstraints(2, 2, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 0, 0),
                                           0, 0));
       label.setForeground(Color.WHITE);
       JCheckBox blurCheckBox = new JCheckBox("Blur Enabled");
       blurCheckBox.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent changeEvent) {
               reflectionPanel.setBlurEnabled(
                       ((JCheckBox) changeEvent.getSource()).isSelected());
           }
       });
       blurCheckBox.setOpaque(false);
       blurCheckBox.setForeground(Color.WHITE);
       controls.add(blurCheckBox,
                    new GridBagConstraints(0, 3, 1, 1, 0.0, 1.0,
                                           GridBagConstraints.LINE_START,
                                           GridBagConstraints.NONE,
                                           new Insets(0, 0, 6, 0),
                                           0, 0));
       //add(controls, BorderLayout.SOUTH);
       reflectionPanel.setLayout(new BorderLayout());
       reflectionPanel.add(controls, BorderLayout.SOUTH);
       pack();
       setLocationRelativeTo(null);
       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   private static class ReflectionPanel extends JPanel {
       private BufferedImage image = null;
       private BufferedImage imageA;
       private ReflectionRenderer renderer = new ReflectionRenderer();
       public ReflectionPanel() {
           try {
               imageA = GraphicsUtilities.loadCompatibleImage(getClass().getResource("A.jpg"));
           } catch (IOException e) {
               e.printStackTrace();
           }
           image = renderer.createReflection(imageA);
           setOpaque(false);
       }
       @Override
       public Dimension getPreferredSize() {
           return new Dimension(640, 520);
       }
       @Override
       protected void paintComponent(Graphics g) {
           int x = (getWidth() - imageA.getWidth()) / 2;
           int y = 24;
           if (renderer.isBlurEnabled()) {
               x -= renderer.getEffectiveBlurRadius();
               y -= renderer.getEffectiveBlurRadius() + 1;
           }
           g.drawImage(image, x, y + imageA.getHeight(), null);
           if (renderer.isBlurEnabled()) {
               x += renderer.getEffectiveBlurRadius();
               y += renderer.getEffectiveBlurRadius() + 1;
           }
           g.drawImage(imageA, x, y, null);
       }
       public void setOpacity(float opacity) {
           renderer.setOpacity(opacity);
           image = renderer.createReflection(imageA);
           repaint();
       }
       public void setLength(float length) {
           renderer.setLength(length);
           image = renderer.createReflection(imageA);
           repaint();
       }
       public void setBlurEnabled(boolean selected) {
           renderer.setBlurEnabled(selected);
           image = renderer.createReflection(imageA);
           repaint();
       }
       public void setRadius(int radius) {
           renderer.setBlurRadius(radius);
           image = renderer.createReflection(imageA);
           repaint();
       }
   }
   private static class GradientPanel extends JPanel {
       GradientPanel() {
           super(new BorderLayout());
       }
       @Override
       protected void paintComponent(Graphics g) {
           Graphics2D g2 = (Graphics2D) g;
           Rectangle clip = g2.getClipBounds();
           Paint paint = g2.getPaint();
           g2.setPaint(new GradientPaint(0.0f, getHeight() * 0.22f,
                                         new Color(0x202737),
                                         0.0f, getHeight() * 0.7f,
                                         Color.BLACK));
           g2.fillRect(clip.x, clip.y, clip.width, clip.height);
           g2.setPaint(paint);
       }
   }
   public static void main(String... args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new ImageReflectionDemoFilthy().setVisible(true);
           }
       });
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       BufferedImage temp = null;
       Graphics2D g2 = null;
       int previousWidth = width;
       int previousHeight = height;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           if (temp == null) {
               temp = createCompatibleImage(image, width, height);
               g2 = temp.createGraphics();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           }
           g2.drawImage(thumb, 0, 0, width, height,
                        0, 0, previousWidth, previousHeight, null);
           previousWidth = width;
           previousHeight = height;
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       g2.dispose();
       if (width != thumb.getWidth() || height != thumb.getHeight()) {
           temp = createCompatibleImage(image, width, height);
           g2 = temp.createGraphics();
           g2.drawImage(thumb, 0, 0, null);
           g2.dispose();
           thumb = temp;
       }
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       BufferedImage temp = null;
       Graphics2D g2 = null;
       int previousWidth = width;
       int previousHeight = height;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           if (temp == null) {
               temp = createCompatibleImage(image, width, height);
               g2 = temp.createGraphics();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           }
           g2.drawImage(thumb, 0, 0, width, height,
                        0, 0, previousWidth, previousHeight, null);
           previousWidth = width;
           previousHeight = height;
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       g2.dispose();
       if (width != thumb.getWidth() || height != thumb.getHeight()) {
           temp = createCompatibleImage(image, width, height);
           g2 = temp.createGraphics();
           g2.drawImage(thumb, 0, 0, null);
           g2.dispose();
           thumb = temp;
       }
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

A fast blur filter can be used to blur pictures quickly. This filter is an * implementation of the box blur algorithm. The blurs generated by this * algorithm might show square artifacts, especially on pictures containing * straight lines (rectangles, text, etc.) On most pictures though, the * result will look very good.

*

The force of the blur can be controlled with a radius and the * default radius is 3. Since the blur clamps values on the edges of the * source picture, you might need to provide a picture with empty borders * to avoid artifacts at the edges. The performance of this filter are * independant from the radius.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class FastBlurFilter extends AbstractFilter {

   private final int radius;
   /**
*

Creates a new blur filter with a default radius of 3.

    */
   public FastBlurFilter() {
       this(3);
   }
   /**
*

Creates a new blur filter with the specified radius. If the radius * is lower than 1, a radius of 1 will be used automatically.

    *
    * @param radius the radius, in pixels, of the blur
    */
   public FastBlurFilter(int radius) {
       if (radius < 1) {
           radius = 1;
       }
       this.radius = radius;
   }
   /**
*

Returns the radius used by this filter, in pixels.

    *
    * @return the radius of the blur
    */
   public int getRadius() {
       return radius;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       // horizontal pass
       blur(srcPixels, dstPixels, width, height, radius);
       // vertical pass
       //noinspection SuspiciousNameCombination
       blur(dstPixels, srcPixels, height, width, radius);
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }
   /**
*

Blurs the source pixels into the destination pixels. The force of * the blur is specified by the radius which must be greater than 0.

*

The source and destination pixels arrays are expected to be in the * INT_ARGB format.

*

After this method is executed, dstPixels contains a transposed and * filtered copy of srcPixels.

    *
    * @param srcPixels the source pixels
    * @param dstPixels the destination pixels
    * @param width the width of the source picture
    * @param height the height of the source picture
    * @param radius the radius of the blur effect
    */
   static void blur(int[] srcPixels, int[] dstPixels,
                    int width, int height, int radius) {
       final int windowSize = radius * 2 + 1;
       final int radiusPlusOne = radius + 1;
       int sumAlpha;
       int sumRed;
       int sumGreen;
       int sumBlue;
       int srcIndex = 0;
       int dstIndex;
       int pixel;
       int[] sumLookupTable = new int[256 * windowSize];
       for (int i = 0; i < sumLookupTable.length; i++) {
           sumLookupTable[i] = i / windowSize;
       }
       int[] indexLookupTable = new int[radiusPlusOne];
       if (radius < width) {
           for (int i = 0; i < indexLookupTable.length; i++) {
               indexLookupTable[i] = i;
           }
       } else {
           for (int i = 0; i < width; i++) {
               indexLookupTable[i] = i;
           }
           for (int i = width; i < indexLookupTable.length; i++) {
               indexLookupTable[i] = width - 1;
           }
       }
       for (int y = 0; y < height; y++) {
           sumAlpha = sumRed = sumGreen = sumBlue = 0;
           dstIndex = y;
           pixel = srcPixels[srcIndex];
           sumAlpha += radiusPlusOne * ((pixel >> 24) & 0xFF);
           sumRed   += radiusPlusOne * ((pixel >> 16) & 0xFF);
           sumGreen += radiusPlusOne * ((pixel >>  8) & 0xFF);
           sumBlue  += radiusPlusOne * ( pixel        & 0xFF);
           for (int i = 1; i <= radius; i++) {
               pixel = srcPixels[srcIndex + indexLookupTable[i]];
               sumAlpha += (pixel >> 24) & 0xFF;
               sumRed   += (pixel >> 16) & 0xFF;
               sumGreen += (pixel >>  8) & 0xFF;
               sumBlue  +=  pixel        & 0xFF;
           }
           for  (int x = 0; x < width; x++) {
               dstPixels[dstIndex] = sumLookupTable[sumAlpha] << 24 |
                                     sumLookupTable[sumRed]   << 16 |
                                     sumLookupTable[sumGreen] <<  8 |
                                     sumLookupTable[sumBlue];
               dstIndex += height;
               int nextPixelIndex = x + radiusPlusOne;
               if (nextPixelIndex >= width) {
                   nextPixelIndex = width - 1;
               }
               int previousPixelIndex = x - radius;
               if (previousPixelIndex < 0) {
                   previousPixelIndex = 0;
               }
               int nextPixel = srcPixels[srcIndex + nextPixelIndex];
               int previousPixel = srcPixels[srcIndex + previousPixelIndex];
               sumAlpha += (nextPixel     >> 24) & 0xFF;
               sumAlpha -= (previousPixel >> 24) & 0xFF;
               sumRed += (nextPixel     >> 16) & 0xFF;
               sumRed -= (previousPixel >> 16) & 0xFF;
               sumGreen += (nextPixel     >> 8) & 0xFF;
               sumGreen -= (previousPixel >> 8) & 0xFF;
               sumBlue += nextPixel & 0xFF;
               sumBlue -= previousPixel & 0xFF;
           }
           srcIndex += width;
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

Provides an abstract implementation of the BufferedImageOp * interface. This class can be used to created new image filters based * on BufferedImageOp.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

abstract class AbstractFilter implements BufferedImageOp {

   public abstract BufferedImage filter(BufferedImage src, BufferedImage dest);
   /**
    * {@inheritDoc}
    */
   public Rectangle2D getBounds2D(BufferedImage src) {
       return new Rectangle(0, 0, src.getWidth(), src.getHeight());
   }
   /**
    * {@inheritDoc}
    */
   public BufferedImage createCompatibleDestImage(BufferedImage src,
                                                  ColorModel destCM) {
       if (destCM == null) {
           destCM = src.getColorModel();
       }
       return new BufferedImage(destCM,
                                destCM.createCompatibleWritableRaster(
                                        src.getWidth(), src.getHeight()),
                                destCM.isAlphaPremultiplied(), null);
   }
   /**
    * {@inheritDoc}
    */
   public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
       return (Point2D) srcPt.clone();
   }
   /**
    * {@inheritDoc}
    */
   public RenderingHints getRenderingHints() {
       return null;
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

A reflection renderer generates the reflection of a given picture. The * result can be either the reflection itself, or an image containing both the * source image and its reflection.

*

Reflection Properties

*

A reflection is defined by three properties: *

    *
  • opacity: the opacity of the reflection. You will usually * change this valued according to the background color.
  • *
  • length: the length of the reflection. The length is a fraction * of the height of the source image.
  • *
  • blur enabled: perfect reflections are hardly natural. You can * blur the reflection to make it look a bit more natural.
  • *
* You can set these properties using the provided mutaters or the appropriate
* constructor. Here are two ways of creating a blurred reflection, with an
* opacity of 50% and a length of 30% the height of the original image:
*
 * ReflectionRenderer renderer = new ReflectionRenderer(0.5f, 0.3f, true);
 * // ..
 * renderer = new ReflectionRenderer();
 * renderer.setOpacity(0.5f);
 * renderer.setLength(0.3f);
 * renderer.setBlurEnabled(true);
 * 
* The default constructor provides the following default values:
*
    *
  • opacity: 35%
  • *
  • length: 40%
  • *
  • blur enabled: false
  • *

*

Generating Reflections

*

A reflection is generated as a BufferedImage from another * BufferedImage. Once the renderer is set up, you must call * {@link #createReflection(java.awt.image.BufferedImage)} to actually generate * the reflection: *

 * ReflectionRenderer renderer = new ReflectionRenderer();
 * // renderer setup
 * BufferedImage reflection = renderer.createReflection(bufferedImage);
 * 

*

The returned image contains only the reflection. You will have to append * it to the source image at painting time to get a realistic results. You can * also asks the rendered to return a picture composed of both the source image * and its reflection: *

 * ReflectionRenderer renderer = new ReflectionRenderer();
 * // renderer setup
 * BufferedImage reflection = renderer.appendReflection(bufferedImage);
 * 

*

Properties Changes

*

This renderer allows to register property change listeners with * {@link #addPropertyChangeListener}. Listening to properties changes is very * useful when you emebed the renderer in a graphical component and give the API * user the ability to access the renderer. By listening to properties changes, * you can easily repaint the component when needed.

*

Threading Issues

*

ReflectionRenderer is not guaranteed to be thread-safe.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class ReflectionRenderer {

   /**
*

Identifies a change to the opacity used to render the reflection.

    */
   public static final String OPACITY_CHANGED_PROPERTY = "reflection_opacity";
   /**
*

Identifies a change to the length of the rendered reflection.

    */
   public static final String LENGTH_CHANGED_PROPERTY = "reflection_length";
   /**
*

Identifies a change to the blurring of the rendered reflection.

    */
   public static final String BLUR_ENABLED_CHANGED_PROPERTY = "reflection_blur";
   // opacity of the reflection
   private float opacity;
   // length of the reflection
   private float length;
   // should the reflection be blurred?
   private boolean blurEnabled;
   // notifies listeners of properties changes
   private PropertyChangeSupport changeSupport;
   private StackBlurFilter stackBlurFilter;
   /**
*

Creates a default good looking reflections generator. * The default reflection renderer provides the following default values: *

    *
  • opacity: 35%
  • *
  • length: 40%
  • *
  • blurring: disabled with a radius of 1 pixel
  • *

*

These properties provide a regular, good looking reflection.

    *
    * @see #getOpacity()
    * @see #setOpacity(float)
    * @see #getLength()
    * @see #setLength(float)
    * @see #isBlurEnabled()
    * @see #setBlurEnabled(boolean)
    * @see #getBlurRadius()
    * @see #setBlurRadius(int)
    */
   public ReflectionRenderer() {
       this(0.35f, 0.4f, false);
   }
   /**
*

Creates a default good looking reflections generator with the * specified opacity. The default reflection renderer provides the following * default values: *

    *
  • length: 40%
  • *
  • blurring: disabled with a radius of 1 pixel
  • *

    *
    * @param opacity the opacity of the reflection, between 0.0 and 1.0
    * @see #getOpacity()
    * @see #setOpacity(float)
    * @see #getLength()
    * @see #setLength(float)
    * @see #isBlurEnabled()
    * @see #setBlurEnabled(boolean)
    * @see #getBlurRadius()
    * @see #setBlurRadius(int)
    */
   public ReflectionRenderer(float opacity) {
       this(opacity, 0.4f, false);
   }
   /**
*

Creates a reflections generator with the specified properties. Both * opacity and length are numbers between 0.0 (0%) and 1.0 (100%). If the * provided numbers are outside this range, they are clamped.

*

Enabling the blur generates a different kind of reflections that might * look more natural. The default blur radius is 1 pixel

    *
    * @param opacity the opacity of the reflection
    * @param length the length of the reflection
    * @param blurEnabled if true, the reflection is blurred
    * @see #getOpacity(),#setOpacity(float),#getLength(),#setLength(float)
    * @see #isBlurEnabled(),#setBlurEnabled(boolean)
    * @see #getBlurRadius()
    * @see #setBlurRadius(int)
    */
   public ReflectionRenderer(float opacity, float length, boolean blurEnabled) {
       //noinspection ThisEscapedInObjectConstruction
       this.changeSupport = new PropertyChangeSupport(this);
       this.stackBlurFilter = new StackBlurFilter(1);
       setOpacity(opacity);
       setLength(length);
       setBlurEnabled(blurEnabled);
   }
   /**
*

Add a PropertyChangeListener to the listener list. The listener is * registered for all properties. The same listener object may be added * more than once, and will be called as many times as it is added. If * listener is null, no exception is thrown and no action * is taken.

    *
    * @param listener the PropertyChangeListener to be added
    */
   public void addPropertyChangeListener(PropertyChangeListener listener) {
       changeSupport.addPropertyChangeListener(listener);
   }
   /**
*

Remove a PropertyChangeListener from the listener list. This removes * a PropertyChangeListener that was registered for all properties. If * listener was added more than once to the same event source, * it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown * and no action is taken.

    *
    * @param listener the PropertyChangeListener to be removed
    */
   public void removePropertyChangeListener(PropertyChangeListener listener) {
       changeSupport.removePropertyChangeListener(listener);
   }
   /**
*

Gets the opacity used by the factory to generate reflections.

*

The opacity is comprised between 0.0f and 1.0f; 0.0f being fully * transparent and 1.0f fully opaque.

    *
    * @return this factory"s shadow opacity
    * @see #getOpacity()
    * @see #createReflection(java.awt.image.BufferedImage)
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public float getOpacity() {
       return opacity;
   }
   /**
*

Sets the opacity used by the factory to generate reflections.

*

Consecutive calls to {@link #createReflection} will all use this * opacity until it is set again.

*

The opacity is comprised between 0.0f and 1.0f; 0.0f being fully * transparent and 1.0f fully opaque. If you provide a value out of these * boundaries, it will be restrained to the closest boundary.

    *
    * @param opacity the generated reflection opacity
    * @see #setOpacity(float)
    * @see #createReflection(java.awt.image.BufferedImage)
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public void setOpacity(float opacity) {
       float oldOpacity = this.opacity;
       if (opacity < 0.0f) {
           opacity = 0.0f;
       } else if (opacity > 1.0f) {
           opacity = 1.0f;
       }
       if (oldOpacity != opacity) {
           this.opacity = opacity;
           changeSupport.firePropertyChange(OPACITY_CHANGED_PROPERTY,
                                            oldOpacity,
                                            this.opacity);
       }
   }
   /**
*

Returns the length of the reflection. The result is a number between * 0.0 and 1.0. This number is the fraction of the height of the source * image that is used to compute the size of the reflection.

    *
    * @return the length of the reflection, as a fraction of the source image
    *   height
    * @see #setLength(float)
    * @see #createReflection(java.awt.image.BufferedImage)
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public float getLength() {
       return length;
   }
   /**
*

Sets the length of the reflection, as a fraction of the height of the * source image.

*

Consecutive calls to {@link #createReflection} will all use this * opacity until it is set again.

*

The opacity is comprised between 0.0f and 1.0f; 0.0f being fully * transparent and 1.0f fully opaque. If you provide a value out of these * boundaries, it will be restrained to the closest boundary.

    *
    * @param length the length of the reflection, as a fraction of the source
    *   image height
    * @see #getLength()
    * @see #createReflection(java.awt.image.BufferedImage)
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public void setLength(float length) {
       float oldLength = this.length;
       if (length < 0.0f) {
           length = 0.0f;
       } else if (length > 1.0f) {
           length = 1.0f;
       }
       if (oldLength != length) {
           this.length = length;
           changeSupport.firePropertyChange(LENGTH_CHANGED_PROPERTY,
                                            oldLength,
                                            this.length);
       }
   }
   /**
*

Returns true if the blurring of the reflection is enabled, false * otherwise. When blurring is enabled, the reflection is blurred to look * more natural.

    *
    * @return true if blur is enabled, false otherwise
    * @see #setBlurEnabled(boolean)
    * @see #createReflection(java.awt.image.BufferedImage)
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public boolean isBlurEnabled() {
       return blurEnabled;
   }
   /**
*

Setting the blur to true will enable the blurring of the reflection * when {@link #createReflection} is invoked.

*

Enabling the blurring of the reflection can yield to more natural * results which may or may not be better looking, depending on the source * picture.

*

Consecutive calls to {@link #createReflection} will all use this * opacity until it is set again.

    *
    * @param blurEnabled true to enable the blur, false otherwise
    * @see #isBlurEnabled()
    * @see #createReflection(java.awt.image.BufferedImage)
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public void setBlurEnabled(boolean blurEnabled) {
       if (blurEnabled != this.blurEnabled) {
           boolean oldBlur = this.blurEnabled;
           this.blurEnabled= blurEnabled;
           changeSupport.firePropertyChange(BLUR_ENABLED_CHANGED_PROPERTY,
                                            oldBlur,
                                            this.blurEnabled);
       }
   }
   /**
*

Returns the effective radius, in pixels, of the blur used by this * renderer when {@link #isBlurEnabled()} is true.

    *
    * @return the effective radius of the blur used when
    *   isBlurEnabled is true
    * @see #isBlurEnabled()
    * @see #setBlurEnabled(boolean)
    * @see #setBlurRadius(int)
    * @see #getBlurRadius()
    */
   public int getEffectiveBlurRadius() {
       return stackBlurFilter.getEffectiveRadius();
   }
   /**
*

Returns the radius, in pixels, of the blur used by this renderer when * {@link #isBlurEnabled()} is true.

    *
    * @return the radius of the blur used when isBlurEnabled
    *         is true
    * @see #isBlurEnabled()
    * @see #setBlurEnabled(boolean)
    * @see #setBlurRadius(int)
    * @see #getEffectiveBlurRadius()
    */
   public int getBlurRadius() {
       return stackBlurFilter.getRadius();
   }
   /**
*

Sets the radius, in pixels, of the blur used by this renderer when * {@link #isBlurEnabled()} is true. This radius changes the size of the * generated image when blurring is applied.

    *
    * @param radius the radius, in pixels, of the blur
    * @see #isBlurEnabled()
    * @see #setBlurEnabled(boolean)
    * @see #getBlurRadius()
    */
   public void setBlurRadius(int radius) {
       this.stackBlurFilter = new StackBlurFilter(radius);
   }
   /**
*

Returns the source image and its reflection. The appearance of the * reflection is defined by the opacity, the length and the blur * properties.

* *

The width of the generated image will be augmented when * {@link #isBlurEnabled()} is true. The generated image will have the width * of the source image plus twice the effective blur radius (see * {@link #getEffectiveBlurRadius()}). The default blur radius is 1 so the * width will be augmented by 6. You might need to take this into account * at drawing time.

*

The returned image height depends on the value returned by * {@link #getLength()} and {@link #getEffectiveBlurRadius()}. For instance, * if the length is 0.5 (or 50%) and the source image is 480 pixels high, * then the reflection will be 246 (480 * 0.5 + 3 * 2) pixels high.

*

You can create only the reflection by calling * {@link #createReflection(java.awt.image.BufferedImage)}.

    *
    * @param image the source image
    * @return the source image with its reflection below
    * @see #createReflection(java.awt.image.BufferedImage)
    */
   public BufferedImage appendReflection(BufferedImage image) {
       BufferedImage reflection = createReflection(image);
       BufferedImage buffer = GraphicsUtilities.createCompatibleTranslucentImage(
               reflection.getWidth(), image.getHeight() + reflection.getHeight());
       Graphics2D g2 = buffer.createGraphics();
       int effectiveRadius = isBlurEnabled() ? stackBlurFilter.getEffectiveRadius() : 0;
       g2.drawImage(image, effectiveRadius, 0, null);
       g2.drawImage(reflection, 0, image.getHeight() - effectiveRadius, null);
       g2.dispose();
       reflection.flush();
       return buffer;
   }
   /**
*

Returns the reflection of the source image. The appearance of the * reflection is defined by the opacity, the length and the blur * properties.

* *

The width of the generated image will be augmented when * {@link #isBlurEnabled()} is true. The generated image will have the width * of the source image plus twice the effective blur radius (see * {@link #getEffectiveBlurRadius()}). The default blur radius is 1 so the * width will be augmented by 6. You might need to take this into account * at drawing time.

*

The returned image height depends on the value returned by * {@link #getLength()} and {@link #getEffectiveBlurRadius()}. For instance, * if the length is 0.5 (or 50%) and the source image is 480 pixels high, * then the reflection will be 246 (480 * 0.5 + 3 * 2) pixels high.

*

The returned image contains only * the reflection. You will have to append it to the source image to produce * the illusion of a reflective environement. The method * {@link #appendReflection(java.awt.image.BufferedImage)} provides an easy * way to create an image containing both the source and the reflection.

    *
    * @param image the source image
    * @return the reflection of the source image
    * @see #appendReflection(java.awt.image.BufferedImage)
    */
   public BufferedImage createReflection(BufferedImage image) {
       if (length == 0.0f) {
           return GraphicsUtilities.createCompatibleTranslucentImage(1, 1);
       }
       int blurOffset = isBlurEnabled() ?
                        stackBlurFilter.getEffectiveRadius() : 0;
       int height = (int) (image.getHeight() * length);
       BufferedImage buffer =
               GraphicsUtilities.createCompatibleTranslucentImage(
                       image.getWidth() + blurOffset * 2,
                       height + blurOffset * 2);
       Graphics2D g2 = buffer.createGraphics();
       g2.translate(0, image.getHeight());
       g2.scale(1.0, -1.0);
       g2.drawImage(image, blurOffset, -blurOffset, null);
       g2.scale(1.0, -1.0);
       g2.translate(0, -image.getHeight());
       g2.setComposite(AlphaComposite.DstIn);
       g2.setPaint(new GradientPaint(0.0f, 0.0f,
                                     new Color(0.0f, 0.0f, 0.0f, getOpacity()),
                                     0.0f, buffer.getHeight(),
                                     new Color(0.0f, 0.0f, 0.0f, 0.0f), true));
       g2.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());
       g2.dispose();
       return isBlurEnabled() ? stackBlurFilter.filter(buffer, null) :
               buffer;
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

A stack blur filter can be used to create an approximation of a * gaussian blur. The approximation is controlled by the number of times the * {@link org.jdesktop.swingx.image.FastBlurFilter} is applied onto the source * picture. The default number of iterations, 3, provides a decent compromise * between speed and rendering quality.

*

The force of the blur can be controlled with a radius and the * default radius is 3. Since the blur clamps values on the edges of the * source picture, you might need to provide a picture with empty borders * to avoid artifacts at the edges. The performance of this filter are * independant from the radius.

*
* @author Romain Guy <romain.guy@mac.ru>
  • /

class StackBlurFilter extends AbstractFilter {

   private final int radius;
   private final int iterations;
   /**
*

Creates a new blur filter with a default radius of 3 and 3 iterations.

    */
   public StackBlurFilter() {
       this(3, 3);
   }
   /**
*

Creates a new blur filter with the specified radius and 3 iterations. * If the radius is lower than 1, a radius of 1 will be used automatically.

    *
    * @param radius the radius, in pixels, of the blur
    */
   public StackBlurFilter(int radius) {
       this(radius, 3);
   }
   /**
*

Creates a new blur filter with the specified radius. If the radius * is lower than 1, a radius of 1 will be used automatically. The number * of iterations controls the approximation to a gaussian blur. If the * number of iterations is lower than 1, one iteration will be used * automatically.

    *
    * @param radius the radius, in pixels, of the blur
    * @param iterations the number of iterations to approximate a gaussian blur
    */
   public StackBlurFilter(int radius, int iterations) {
       if (radius < 1) {
           radius = 1;
       }
       if (iterations < 1) {
           iterations = 1;
       }
       this.radius = radius;
       this.iterations = iterations;
   }
   /**
*

Returns the effective radius of the stack blur. If the radius of the * blur is 1 and the stack iterations count is 3, then the effective blur * radius is 1 * 3 = 3.

    * @return the number of iterations times the blur radius
    */
   public int getEffectiveRadius() {
       return getIterations() * getRadius();
   }
   /**
*

Returns the radius used by this filter, in pixels.

    *
    * @return the radius of the blur
    */
   public int getRadius() {
       return radius;
   }
   /**
*

Returns the number of iterations used to approximate a gaussian * blur.

    *
    * @return the number of iterations used by this blur
    */
   public int getIterations() {
       return iterations;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       for (int i = 0; i < iterations; i++) {
           // horizontal pass
           FastBlurFilter.blur(srcPixels, dstPixels, width, height, radius);
           // vertical pass
           FastBlurFilter.blur(dstPixels, srcPixels, height, width, radius);
       }
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }

}


 </source>
   
  
 
  



Image Zooming

   <source lang="java">
 

import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.transitions.Effect; import org.jdesktop.animation.transitions.EffectsManager; import org.jdesktop.animation.transitions.EffectsManager.TransitionType; import org.jdesktop.animation.transitions.ScreenTransition; import org.jdesktop.animation.transitions.TransitionTarget; import org.jdesktop.animation.transitions.effects.rupositeEffect; import org.jdesktop.animation.transitions.effects.Move; import org.jdesktop.animation.transitions.effects.Scale; //import org.jdesktop.tools.io.FileTreeWalk; //import org.jdesktop.tools.io.FileTreeWalker; //import org.jdesktop.tools.io.UnixGlobFileFilter; import java.io.FileFilter; import java.io.File; import java.util.regex.Pattern; import java.util.regex.Matcher; /*

* ImageBrowser.java
*
* Created on May 3, 2007, 3:11 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* This demo of the AnimatedTransitions library uses a layout manager
* to assist in setting up the next screen that the application
* transitions to.
*
* The slider in the window controls the picture thumbnail size. The
* standard FlowLayout manager organizes the pictures according to
* the thumbnail sizes. The transition animates the change from
* one thumbnail size to the next.
*
* @author Chet
*/

public class ImageBrowser extends JComponent

       implements TransitionTarget, ChangeListener {
   
   private static final int SLIDER_INCREMENT = 50;
   int numPictures = 40;
   JLabel label[];
   Animator animator = new Animator(500);
   ScreenTransition transition = new ScreenTransition(this, this, animator);
   Dimension newSize = new Dimension();
   List<ImageHolder> images = new ArrayList<ImageHolder>();
   static int currentSize = 50;
   GradientPaint bgGradient = null;
   int prevHeight = 0;
   static JSlider slider = new JSlider(1, 400 / SLIDER_INCREMENT, 
           1 + currentSize / SLIDER_INCREMENT);
   static int numImages = 0;
   
   /** Creates a new instance of ImageBrowser */
   public ImageBrowser() {
       setOpaque(true);
       animator.setAcceleration(.1f);
       animator.setDeceleration(.4f);
       setLayout(new FlowLayout());
       loadImages();
       label = new JLabel[images.size()];
       // For each image:
       // - set the icon at the current thumbnail size
       // - create/set a custom effect that will move/scale the
       // images. Note that the main reason for the custom effect
       // is that scaling effects typically redraw the actual component
       // instead of using image tricks. In this case, image tricks are
       // just fine. So the custom effect is purely an optimization here.
       for (int i = 0; i < images.size(); ++i) {
           label[i] = new JLabel();
           label[i].setIcon(new ImageIcon(images.get(i).getImage(currentSize)));
           add(label[i]);
           Effect move = new Move();
           Effect scale = new Scale();
           CompositeEffect comp = new CompositeEffect(move);
           comp.addEffect(scale);
           comp.setRenderComponent(false);
           EffectsManager.setEffect(label[i], comp, TransitionType.CHANGING);
       }
   }
   /**
    * Paints a gradient in the background of this component
    */
   @Override
   protected void paintComponent(Graphics g) {
       if (getHeight() != prevHeight) {
           prevHeight = getHeight();
           bgGradient = new GradientPaint(0, 0, 
                   new Color(0xEBF4FA), 0, prevHeight, new Color(0xBBD9EE));
       }
       ((Graphics2D)g).setPaint(bgGradient);
       g.fillRect(0, 0, getWidth(), prevHeight);
   }
   
   /**
    * Loads all images found in the directory "images" (which therefore must
    * be found in the folder in which this app runs).
    */
   private void loadImages() {
       //try {
           //File imagesDir = new File("images");
           //FileTreeWalker walker = new FileTreeWalker(imagesDir, 
             //      new UnixGlobFileFilter("*.jpg"));
           //walker.walk(new FileTreeWalk() {
             //  public void walk(File path) {
                   //numImages++;
                   try {
                       BufferedImage image = ImageIO.read(ImageBrowser.class.getResource("shanghai.jpg"));
                       images.add(new ImageHolder(image));
                   } catch (Exception e) {
                       System.out.println("Problem loading images: " + e);
                   }
               //}
           //});
       //} catch (Exception e) {
         //  System.out.println("Problem loading images: " + e);
       //}
   }
       
   /**
    * TransitionTarget implementation: The setup for the next screen entails
    * merely assigning a new icon to each JLabel with the new thumbnail
    * size
    */
   public void setupNextScreen() {
       for (int i = 0; i < images.size(); ++i) {
           label[i].setIcon(new ImageIcon(images.get(i).getImage(currentSize)));
       }
       // revalidation is necessary for the LayoutManager to do its job
       revalidate();
   }
   
   /**
    * This method handles changes in slider state, which can come from either
    * mouse manipulation of the slider or right/left keyboard events. This
    * event changes the current thumbnail size and starts the transition.
    * We will then receive a callback to setupNextScreen() where we set up
    * the GUI according to this new thumbnail size.
    */
   public void stateChanged(ChangeEvent ce) {
       currentSize = slider.getValue() * 25;
       if (!transition.getAnimator().isRunning()) {
           transition.start();
       }
   }
   
   private static void createAndShowGUI() {
 JFrame f = new JFrame("Image Browser");
       f.setLayout(new BorderLayout());
 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 f.setSize(500, 400);
 ImageBrowser component = new ImageBrowser();
 f.add(component, BorderLayout.CENTER);
       f.add(slider, BorderLayout.SOUTH);
       slider.setBackground(new Color(0xBBD9EE));
       slider.addChangeListener(component);
 f.setVisible(true);
   }
   /**
    * @param args the command line arguments
    */
   public static void main(String[] args) {
       try {
           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       } catch (ClassNotFoundException ex) {
           ex.printStackTrace();
       } catch (InstantiationException ex) {
           ex.printStackTrace();
       } catch (IllegalAccessException ex) {
           ex.printStackTrace();
       } catch (UnsupportedLookAndFeelException ex) {
           ex.printStackTrace();
       }
 Runnable doCreateAndShowGUI = new Runnable() {
     public void run() {
   createAndShowGUI();
     }
 };
 SwingUtilities.invokeLater(doCreateAndShowGUI);
   }

} /**

* This is a utility class that holds our images at various scaled
* sizes. The images are pre-scaled down by halves, using the progressive
* bilinear technique. Thumbnails from these images are requested
* from this class, which are created by down-scaling from the next-largest
* pre-scaled size available.
*/

class ImageHolder {

   private List<BufferedImage> scaledImages = new ArrayList<BufferedImage>();
   private static final int MIN_SIZE = 50;
  
   /**
    * Given any image, this constructor creates and stores down-scaled
    * versions of this image down to some MIN_SIZE
    */
   ImageHolder(BufferedImage originalImage) {
       int imageW = originalImage.getWidth();
       int imageH = originalImage.getHeight();
       scaledImages.add(originalImage);
       BufferedImage prevImage = originalImage;
       while (imageW > MIN_SIZE && imageH > MIN_SIZE) {
           imageW = imageW >> 1;
           imageH = imageH >> 1;
           BufferedImage scaledImage = new BufferedImage(imageW, imageH,
                   prevImage.getType());
           Graphics2D g2d = scaledImage.createGraphics();
           g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                   RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2d.drawImage(prevImage, 0, 0, imageW, imageH, null);
           g2d.dispose();
           scaledImages.add(scaledImage);
       }
   }
   
   /**
    * This method returns an image with the specified width. It finds
    * the pre-scaled size with the closest/larger width and scales
    * down from it, to provide a fast and high-quality scaed version
    * at the requested size.
    */
   BufferedImage getImage(int width) {
       for (BufferedImage scaledImage : scaledImages) {
           int scaledW = scaledImage.getWidth();
           // This is the one to scale from if:
           // - the requested size is larger than this size
           // - the requested size is between this size and 
           //   the next size down
           // - this is the smallest (last) size
           if (scaledW < width || ((scaledW >> 1) < width) ||
                   (scaledW >> 1) < MIN_SIZE) {
               if (scaledW != width) {
                   // Create new version scaled to this width
                   // Set the width at this width, scale the
                   // height proportional to the image width
                   float scaleFactor = (float)width / scaledW;
                   int scaledH = (int)(scaledImage.getHeight() * 
                           scaleFactor + .5f);
                   BufferedImage image = new BufferedImage(width,
                           scaledH, scaledImage.getType());
                   Graphics2D g2d = image.createGraphics();
                   g2d.setRenderingHint(
                           RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                   g2d.drawImage(scaledImage, 0, 0, 
                           width, scaledH, null);
                   g2d.dispose();
                   scaledImage = image;
               }
               return scaledImage;
           }
       }
       // shouldn"t get here
       return null;
   }

} /**

* Copyright (c) 2006, Sun Microsystems, Inc
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following 
*     disclaimer in the documentation and/or other materials provided 
*     with the distribution.
*   * Neither the name of the Harvester project nor the names of its
*     contributors may be used to endorse or promote products derived 
*     from this software without specific prior written permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class UnixGlobFileFilter implements FileFilter {

   private Pattern pattern;
   public UnixGlobFileFilter(String filter) {
       pattern = Pattern.rupile(globToRegex(filter));
   }
   public boolean accept(File file) {
       String path = file.getName();
       Matcher matcher = pattern.matcher(path);
       return matcher.matches();
   }
   private String globToRegex(String glob) {
       char c = "\0";
       boolean escape = false;
       boolean enclosed = false;
       StringBuffer buffer = new StringBuffer(glob.length());
       for (int i = 0; i < glob.length(); i++) {
           c = glob.charAt(i);
           if (escape) {
               buffer.append("\\");
               buffer.append(c);
               escape = false;
               continue;
           }
           switch (c) {
               case "*":
                   buffer.append(".").append("*");
                   break;
               case "?":
                   buffer.append(".");
                   break;
               case "\\":
                   escape = true;
                   break;
               case ".":
                   buffer.append("\\").append(".");
                   break;
               case "{":
                   buffer.append("(");
                   enclosed = true;
                   break;
               case "}":
                   buffer.append(")");
                   enclosed = false;
                   break;
               case ",":
                   if (enclosed)
                       buffer.append("|");
                   else
                       buffer.append(",");
                   break;
               default:
                   buffer.append(c);
           }
       }
       return buffer.toString();
   }

} /**

* Copyright (c) 2006, Sun Microsystems, Inc
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following 
*     disclaimer in the documentation and/or other materials provided 
*     with the distribution.
*   * Neither the name of the Harvester project nor the names of its
*     contributors may be used to endorse or promote products derived 
*     from this software without specific prior written permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class FileTreeWalker {

   private File path;
   private static final FileFilter directoryFilter = new FileFilter() {
       public boolean accept(File pathname) {
           return pathname.isDirectory();
       }
   };
   private FileFilter filter;
   public FileTreeWalker(File path) throws IOException {
       this(path, new FileFilter() {
           public boolean accept(File pathname) {
               return pathname.isFile();
           }
       });
   }
   public FileTreeWalker(File path, FileFilter filter) throws IOException {
       if (path == null || !path.exists() || path.isFile()) {
           throw new IOException("Path " + path + " is not a valid directory.");
       }
       this.path = path;
       this.filter = filter;
   }
   public void walk(FileTreeWalk walk) {
       walkDirectory(walk, path);
   }
   private void walkDirectory(FileTreeWalk walk, File dir) {
       File[] files = dir.listFiles(filter);
       for (File file : files) {
           walk.walk(file);
       }
       File[] dirs = dir.listFiles(directoryFilter);
       for (File subDir : dirs) {
           walkDirectory(walk, subDir);
       }
   }

} /**

* Copyright (c) 2006, Sun Microsystems, Inc
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following 
*     disclaimer in the documentation and/or other materials provided 
*     with the distribution.
*   * Neither the name of the Harvester project nor the names of its
*     contributors may be used to endorse or promote products derived 
*     from this software without specific prior written permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

interface FileTreeWalk {

   public void walk(File path);

}



 </source>
   
  
 
  



Intermediate Images

   <source lang="java">
 

import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; /*

* IntermediateImages.java
*
* Created on May 2, 2007, 10:58 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Chet
*/

public class IntermediateImages extends JComponent {

   private static final int SCALE_X = 20;
   private static final int SMILEY_X = 200;
   private static final int DIRECT_Y = 10;
   private static final int INTERMEDIATE_Y = 260;
   private static final int SMILEY_SIZE = 100;
   private static BufferedImage picture = null;
   private BufferedImage scaledImage = null;
   private BufferedImage smileyImage = null;
   private static final double SCALE_FACTOR = .1;
   private int scaleW, scaleH;
   
   /** Creates a new instance of IntermediateImages */
   public IntermediateImages() {
       try {
           URL url = getClass().getResource("A.jpg");
           picture = ImageIO.read(url);
           scaleW = (int)(SCALE_FACTOR * picture.getWidth());
           scaleH = (int)(SCALE_FACTOR * picture.getHeight());
       } catch (Exception e) {
           System.out.println("Problem reading image file: " + e);
           System.exit(0);
       }
   }
   
   /**
    * Draws both the direct and intermediate-image versions of a 
    * scaled-image, timing both variations.
    */
   private void drawScaled(Graphics g) {
       long startTime, endTime, totalTime;
       
       // Scaled image
       ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       startTime = System.nanoTime();
       for (int i = 0; i < 100; ++i) {
           g.drawImage(picture, SCALE_X, DIRECT_Y, scaleW, scaleH, null);
       }
       endTime = System.nanoTime();
       totalTime = (endTime - startTime) / 1000000;
       g.setColor(Color.BLACK);
       g.drawString("Direct: " + ((float)totalTime/100) + " ms", 
               SCALE_X, DIRECT_Y + scaleH + 20);
       System.out.println("scaled: " + totalTime);
       
       // Intermediate Scaled
       // First, create the intermediate image
       if (scaledImage == null ||
           scaledImage.getWidth() != scaleW ||
           scaledImage.getHeight() != scaleH)
       {
           GraphicsConfiguration gc = getGraphicsConfiguration();
           scaledImage = gc.createCompatibleImage(scaleW, scaleH);
           Graphics gImg = scaledImage.getGraphics();
           ((Graphics2D)gImg).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                   RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           gImg.drawImage(picture, 0, 0, scaleW, scaleH, null);
       }
       // Now, copy the intermediate image into place
       startTime = System.nanoTime();
       for (int i = 0; i < 100; ++i) {
           g.drawImage(scaledImage, SCALE_X, INTERMEDIATE_Y, null);
       }
       endTime = System.nanoTime();
       totalTime = (endTime - startTime) / 1000000;
       g.drawString("Intermediate: " + ((float)totalTime/100) + " ms", 
               SCALE_X, INTERMEDIATE_Y + scaleH + 20);
       System.out.println("Intermediate scaled: " + totalTime);
   }
   
   private void renderSmiley(Graphics g, int x, int y) {
 Graphics2D g2d = (Graphics2D)g.create();
       
 // Yellow face
 g2d.setColor(Color.yellow);
 g2d.fillOval(x, y, SMILEY_SIZE, SMILEY_SIZE);
       
 // Black eyes
 g2d.setColor(Color.black);
 g2d.fillOval(x + 30, y + 30, 8, 8);
 g2d.fillOval(x + 62, y + 30, 8, 8);
       
 // Black outline
 g2d.drawOval(x, y, SMILEY_SIZE, SMILEY_SIZE);
       
 // Black smile
 g2d.setStroke(new BasicStroke(3.0f));
 g2d.drawArc(x + 20, y + 20, 60, 60, 190, 160);
       
       g2d.dispose();
   }
   
   /**
    * Draws both the direct and intermediate-image versions of a 
    * smiley face, timing both variations.
    */
   private void drawSmiley(Graphics g) {
       long startTime, endTime, totalTime;
       // Draw smiley directly
       startTime = System.nanoTime();
       for (int i = 0; i < 100; ++i) {
           renderSmiley(g, SMILEY_X, DIRECT_Y);
       }
       endTime = System.nanoTime();
       totalTime = (endTime - startTime) / 1000000;
       g.setColor(Color.BLACK);
       g.drawString("Direct: " + ((float)totalTime/100) + " ms", 
               SMILEY_X, DIRECT_Y + SMILEY_SIZE + 20);
       System.out.println("Direct: " + totalTime);
       
       // Intermediate Smiley
       // First, create the intermediate image if necessary
 if (smileyImage == null) {
   GraphicsConfiguration gc = getGraphicsConfiguration();
   smileyImage = gc.createCompatibleImage(
                 SMILEY_SIZE + 1, SMILEY_SIZE + 1, Transparency.BITMASK);
   Graphics2D gImg = (Graphics2D)smileyImage.getGraphics();
   renderSmiley(gImg, 0, 0);
   gImg.dispose();
 }
       // Now, copy the intermediate image
       startTime = System.nanoTime();
       for (int i = 0; i < 100; ++i) {
           g.drawImage(smileyImage, SMILEY_X, INTERMEDIATE_Y, null);
       }
       endTime = System.nanoTime();
       totalTime = (endTime - startTime) / 1000000;
       g.drawString("Intermediate: " + ((float)totalTime/100) + " ms", 
               SMILEY_X, INTERMEDIATE_Y + SMILEY_SIZE + 20);
       System.out.println("intermediate smiley: " + totalTime);
   }
   
   
   protected void paintComponent(Graphics g) {
       g.setColor(Color.WHITE);
       g.fillRect(0, 0, getWidth(), getHeight());
       drawScaled(g);
       drawSmiley(g);
   }
   
   private static void createAndShowGUI() {    
       JFrame f = new JFrame("IntermediateImages");
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setSize(360, 540);
       f.add(new IntermediateImages());
       f.setVisible(true);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               createAndShowGUI();
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }

}


 </source>
   
  
 
  



Rotating a Buffered Image

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = new AffineTransform();
   tx.rotate(0.5, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_BILINEAR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Scaling a Buffered Image

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = new AffineTransform();
   tx.scale(1, 2);
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_BILINEAR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Shearing a Buffered Image

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = new AffineTransform();
   tx.shear(2, 3);
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_BILINEAR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Shrinking an image by skipping pixels

   <source lang="java">

import java.awt.image.BufferedImage; public class Main {

 static BufferedImage enlarge(BufferedImage image, int n) {
   int w = image.getWidth() / n;
   int h = image.getHeight() / n;
   BufferedImage shrunkImage = new BufferedImage(w, h, image.getType());
   for (int y = 0; y < h; ++y)
     for (int x = 0; x < w; ++x)
       shrunkImage.setRGB(x, y, image.getRGB(x * n, y * n));
   return shrunkImage;
 }

}

 </source>
   
  
 
  



Simple image handling and drawing interaction

   <source lang="java">
 

import java.io.File; import java.awt.*; import javax.swing.*; import no.geosoft.cc.geometry.Geometry; import no.geosoft.cc.graphics.*;

/**

* G demo program. Demonstrates:
*
*
    *
  • Simple image handling *
  • Simple drawing interaction *
* 
* @author 


Transform image

   <source lang="java">
 

import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import javax.swing.JFrame; public class Main extends Canvas {

 public Main() {
   setBackground(Color.white);
 }
 public void paint(Graphics g) {
   try {
     Graphics2D g2D;
     g2D = (Graphics2D) g;
     g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
     String fileName = "a.jpg";
     Image img = getToolkit().getImage(fileName);
     g2D.drawImage(img, new AffineTransform(), this);
     AffineTransform aTran = new AffineTransform();
     aTran.translate(200.0f, 0.0f);
     g2D.transform(aTran);
     AffineTransform imageSpaceTran = new AffineTransform();
     imageSpaceTran.setToShear(0.1f, 0.1f);
     // draw the sheared image
     g2D.drawImage(img, imageSpaceTran, this);
     // move the image over
     aTran.translate(50.0f, 0.0f);
     g2D.transform(aTran);
     imageSpaceTran.scale(1.5f, 1.5f);
     // draw the enlarged image
     g2D.drawImage(img, imageSpaceTran, this);
   } catch (Exception e) {
   }
 }
 public static void main(String s[]) {
   JFrame frame1 = new JFrame("2D Images ");
   frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame1.getContentPane().add("Center", new Main());
   frame1.pack();
   frame1.setSize(new Dimension(750, 370));
   frame1.setVisible(true);
 }

}


 </source>
   
  
 
  



Translating a Buffered Image

   <source lang="java">
 

import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; public class Main {

 public static void main(String[] argv) throws Exception {
   BufferedImage bufferedImage = new BufferedImage(200, 200,
       BufferedImage.TYPE_BYTE_INDEXED);
   AffineTransform tx = new AffineTransform();
   tx.translate(2, 1);
   AffineTransformOp op = new AffineTransformOp(tx,
       AffineTransformOp.TYPE_BILINEAR);
   bufferedImage = op.filter(bufferedImage, null);
 }

}


 </source>
   
  
 
  



Unsharp Mask Demo

   <source lang="java">
 


/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class UnsharpMaskDemo extends JFrame {

   private BufferedImage image;
   private JLabel viewer;
   
   private float amount;
   private int radius;
   private int threshold;
   private JPanel viewerPanel;
   private CardLayout carder;
   private JLabel amountLabel;
   private JLabel radiusLabel;
   private JLabel thresholdLabel;
   
   public UnsharpMaskDemo() {
       super("Unsharp Mask Demo");
       
       UnsharpMaskFilter filter = new UnsharpMaskFilter();
       this.amount = filter.getAmount();
       this.radius = filter.getRadius();
       this.threshold = filter.getThreshold();
       
       loadImage();
       add(buildInstructions(), BorderLayout.NORTH);
       add(buildControls(), BorderLayout.SOUTH);
       add(buildViewer());
       
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       
       pack();
       setResizable(false);
       setLocationRelativeTo(null);
   }
   
   private JComponent buildInstructions() {
       JLabel label = new JLabel("Click the image to see the original.");
       label.setBorder(new EmptyBorder(3, 3, 3, 3));
       return label;
   }
   
   private JComponent buildViewer() {
       viewerPanel = new JPanel(carder = new CardLayout());
       viewer = new JLabel(new ImageIcon(image));
       viewer.addMouseListener(new MouseAdapter() {
           public void mouseReleased(MouseEvent e) {
               carder.show(viewerPanel, "sharp");
           }
           public void mousePressed(MouseEvent e) {
               carder.show(viewerPanel, "original");
           }
       });
       updateViewer();
       
       viewerPanel.add(viewer, "sharp");
       viewerPanel.add(new JLabel(new ImageIcon(image)), "original");
       
       return viewerPanel;
   }
   
   private JComponent buildControls() {
       JPanel panel = new JPanel(new GridBagLayout());
       JSlider slider;
       
       panel.add(amountLabel = new JLabel("Amount: "),
               new GridBagConstraints(0, 0, 1, 1,
                   1.0, 1.0, GridBagConstraints.LINE_START,
                   GridBagConstraints.NONE, new Insets(3, 3, 0, 3), 0, 0));
       panel.add(slider = new JSlider(0, 500, (int) (amount * 100.0f)),
               new GridBagConstraints(0, 1, 1, 1,
                   1.0, 1.0, GridBagConstraints.LINE_START,
                   GridBagConstraints.NONE, new Insets(0, 3, 0, 3), 0, 0));
       slider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               JSlider slider = (JSlider) e.getSource();
               float amount = slider.getValue() / 100.0f;
               setAmount(amount);
           }
       });
       
       panel.add(radiusLabel = new JLabel("Radius: "),
               new GridBagConstraints(0, 2, 1, 1,
                   1.0, 1.0, GridBagConstraints.LINE_START,
                   GridBagConstraints.NONE, new Insets(3, 3, 0, 3), 0, 0));
       panel.add(slider = new JSlider(1, 50, radius),
               new GridBagConstraints(0, 3, 1, 1,
                   1.0, 1.0, GridBagConstraints.LINE_START,
                   GridBagConstraints.NONE, new Insets(0, 3, 0, 3), 0, 0));
       slider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               JSlider slider = (JSlider) e.getSource();
               setRadius(slider.getValue());
           }
       });
       
       panel.add(thresholdLabel = new JLabel("Threshold: "),
               new GridBagConstraints(0, 4, 1, 1,
                   1.0, 1.0, GridBagConstraints.LINE_START,
                   GridBagConstraints.NONE, new Insets(3, 3, 0, 3), 0, 0));
       panel.add(slider = new JSlider(0, 255, threshold),
               new GridBagConstraints(0, 5, 1, 1,
                   1.0, 1.0, GridBagConstraints.LINE_START,
                   GridBagConstraints.NONE, new Insets(0, 3, 3, 3), 0, 0));
        slider.addChangeListener(new ChangeListener() {
           public void stateChanged(ChangeEvent e) {
               JSlider slider = (JSlider) e.getSource();
               setThreshold(slider.getValue());
           }
       });
       
       return panel;
   }
   
   private void setAmount(float amount) {
       this.amount = amount;
       updateViewer();
   }
   
   private void setRadius(int radius) {
       this.radius = radius;
       updateViewer();
   }
   
   private void setThreshold(int threshold) {
       this.threshold = threshold;
       updateViewer();
   }
   
   private void updateViewer() {
       amountLabel.setText("Amount: " + (int) (amount * 100.0f) + "%");
       radiusLabel.setText("Radius: " + radius  + " pixels");
       thresholdLabel.setText("Threshold: " + threshold + " levels");
       
       UnsharpMaskFilter filter = new UnsharpMaskFilter(amount, radius, threshold);
       viewer.setIcon(new ImageIcon(filter.filter(image, null)));
   }
   
   private void loadImage() {
       try {
           this.image = GraphicsUtilities.loadCompatibleImage(getClass().
                   getResource("A.jpg"));
           this.image = GraphicsUtilities.createThumbnail(this.image, 300);
       } catch (IOException ex) {
           ex.printStackTrace();
       }
   }
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new UnsharpMaskDemo().setVisible(true);
           } 
       });
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class UnsharpMaskFilter extends AbstractFilter {

   private final float amount;
   private final int radius;
   private final int threshold;
   public UnsharpMaskFilter() {
       this(0.7f, 2, 2);
   }
   public UnsharpMaskFilter(float amount, int radius, int threshold) {
       this.amount = amount;
       this.radius = radius;
       this.threshold = threshold;
   }
   
   public float getAmount() {
       return amount;
   }
           
   public int getRadius() {
       return radius;
   }
   
   public int getThreshold() {
       return threshold;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       float[] kernel = GaussianBlurFilter.createGaussianKernel(radius);
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       // horizontal pass
       GaussianBlurFilter.blur(srcPixels, dstPixels, width, height, kernel, radius);
       // vertical pass
       //noinspection SuspiciousNameCombination
       GaussianBlurFilter.blur(dstPixels, srcPixels, height, width, kernel, radius);
       
       // blurred image is in srcPixels, we copy the original in dstPixels
       GraphicsUtilities.getPixels(src, 0, 0, width, height, dstPixels);
       // we compare original and blurred images,
       // and store the result in srcPixels
       sharpen(dstPixels, srcPixels, width, height, amount, threshold);
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }
   static void sharpen(int[] original, int[] blurred, int width, int height,
           float amount, int threshold) {
       
       int index = 0;
       
       int srcR, srcB, srcG;
       int dstR, dstB, dstG;
       
       amount *= 1.6f;
       
       for (int y = 0; y < height; y++) {
           for (int x = 0; x < width; x++) {
               int srcColor = original[index];
               srcR = (srcColor >> 16) & 0xFF;
               srcG = (srcColor >>  8) & 0xFF;
               srcB = (srcColor      ) & 0xFF;
               
               int dstColor = blurred[index];
               dstR = (dstColor >> 16) & 0xFF;
               dstG = (dstColor >>  8) & 0xFF;
               dstB = (dstColor      ) & 0xFF;
               
               if (Math.abs(srcR - dstR) >= threshold) {
                   srcR = (int) (amount * (srcR - dstR) + srcR);
                   srcR = srcR > 255 ? 255 : srcR < 0 ? 0 : srcR;
               }
               
               if (Math.abs(srcG - dstG) >= threshold) {
                   srcG = (int) (amount * (srcG - dstG) + srcG);
                   srcG = srcG > 255 ? 255 : srcG < 0 ? 0 : srcG;
               }
               if (Math.abs(srcB - dstB) >= threshold) {
                   srcB = (int) (amount * (srcB - dstB) + srcB);
                   srcB = srcB > 255 ? 255 : srcB < 0 ? 0 : srcB;
               }
               
               int alpha = srcColor & 0xFF000000;
               blurred[index] = alpha | (srcR << 16) | (srcG << 8) | srcB;
               index++;
           }
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       BufferedImage temp = null;
       Graphics2D g2 = null;
       int previousWidth = width;
       int previousHeight = height;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           if (temp == null) {
               temp = createCompatibleImage(image, width, height);
               g2 = temp.createGraphics();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           }
           g2.drawImage(thumb, 0, 0, width, height,
                        0, 0, previousWidth, previousHeight, null);
           previousWidth = width;
           previousHeight = height;
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       g2.dispose();
       if (width != thumb.getWidth() || height != thumb.getHeight()) {
           temp = createCompatibleImage(image, width, height);
           g2 = temp.createGraphics();
           g2.drawImage(thumb, 0, 0, null);
           g2.dispose();
           thumb = temp;
       }
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       BufferedImage temp = null;
       Graphics2D g2 = null;
       int previousWidth = width;
       int previousHeight = height;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           if (temp == null) {
               temp = createCompatibleImage(image, width, height);
               g2 = temp.createGraphics();
               g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           }
           g2.drawImage(thumb, 0, 0, width, height,
                        0, 0, previousWidth, previousHeight, null);
           previousWidth = width;
           previousHeight = height;
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       g2.dispose();
       if (width != thumb.getWidth() || height != thumb.getHeight()) {
           temp = createCompatibleImage(image, width, height);
           g2 = temp.createGraphics();
           g2.drawImage(thumb, 0, 0, null);
           g2.dispose();
           thumb = temp;
       }
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

class GaussianBlurFilter extends AbstractFilter {

   private final int radius;
   /**
*

Creates a new blur filter with a default radius of 3.

    */
   public GaussianBlurFilter() {
       this(3);
   }
   /**
*

Creates a new blur filter with the specified radius. If the radius * is lower than 0, a radius of 0.1 will be used automatically.

    *
    * @param radius the radius, in pixels, of the blur
    */
   public GaussianBlurFilter(int radius) {
       if (radius < 1) {
           radius = 1;
       }
       this.radius = radius;
   }
   /**
*

Returns the radius used by this filter, in pixels.

    *
    * @return the radius of the blur
    */
   public int getRadius() {
       return radius;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       int width = src.getWidth();
       int height = src.getHeight();
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int[] srcPixels = new int[width * height];
       int[] dstPixels = new int[width * height];
       float[] kernel = createGaussianKernel(radius);
       GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels);
       // horizontal pass
       blur(srcPixels, dstPixels, width, height, kernel, radius);
       // vertical pass
       //noinspection SuspiciousNameCombination
       blur(dstPixels, srcPixels, height, width, kernel, radius);
       // the result is now stored in srcPixels due to the 2nd pass
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels);
       return dst;
   }
   /**
*

Blurs the source pixels into the destination pixels. The force of * the blur is specified by the radius which must be greater than 0.

*

The source and destination pixels arrays are expected to be in the * INT_ARGB format.

*

After this method is executed, dstPixels contains a transposed and * filtered copy of srcPixels.

    *
    * @param srcPixels the source pixels
    * @param dstPixels the destination pixels
    * @param width the width of the source picture
    * @param height the height of the source picture
    * @param kernel the kernel of the blur effect
    * @param radius the radius of the blur effect
    */
   static void blur(int[] srcPixels, int[] dstPixels,
                    int width, int height,
                    float[] kernel, int radius) {
       float a;
       float r;
       float g;
       float b;
       int ca;
       int cr;
       int cg;
       int cb;
       for (int y = 0; y < height; y++) {
           int index = y;
           int offset = y * width;
           for (int x = 0; x < width; x++) {
               a = r = g = b = 0.0f;
               for (int i = -radius; i <= radius; i++) {
                   int subOffset = x + i;
                   if (subOffset < 0 || subOffset >= width) {
                       subOffset = (x + width) % width;
                   }
                   int pixel = srcPixels[offset + subOffset];
                   float blurFactor = kernel[radius + i];
                   a += blurFactor * ((pixel >> 24) & 0xFF);
                   r += blurFactor * ((pixel >> 16) & 0xFF);
                   g += blurFactor * ((pixel >>  8) & 0xFF);
                   b += blurFactor * ((pixel      ) & 0xFF);
               }
               ca = (int) (a + 0.5f);
               cr = (int) (r + 0.5f);
               cg = (int) (g + 0.5f);
               cb = (int) (b + 0.5f);
               dstPixels[index] = ((ca > 255 ? 255 : ca) << 24) |
                                  ((cr > 255 ? 255 : cr) << 16) |
                                  ((cg > 255 ? 255 : cg) <<  8) |
                                   (cb > 255 ? 255 : cb);
               index += height;
           }
       }
   }
   static float[] createGaussianKernel(int radius) {
       if (radius < 1) {
           throw new IllegalArgumentException("Radius must be >= 1");
       }
       float[] data = new float[radius * 2 + 1];
       float sigma = radius / 3.0f;
       float twoSigmaSquare = 2.0f * sigma * sigma;
       float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
       float total = 0.0f;
       for (int i = -radius; i <= radius; i++) {
           float distance = i * i;
           int index = i + radius;
           data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
           total += data[index];
       }
       for (int i = 0; i < data.length; i++) {
           data[i] /= total;
       }
       return data;
   }

} /*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

Provides an abstract implementation of the BufferedImageOp * interface. This class can be used to created new image filters based * on BufferedImageOp.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

abstract class AbstractFilter implements BufferedImageOp {

   public abstract BufferedImage filter(BufferedImage src, BufferedImage dest);
   /**
    * {@inheritDoc}
    */
   public Rectangle2D getBounds2D(BufferedImage src) {
       return new Rectangle(0, 0, src.getWidth(), src.getHeight());
   }
   /**
    * {@inheritDoc}
    */
   public BufferedImage createCompatibleDestImage(BufferedImage src,
                                                  ColorModel destCM) {
       if (destCM == null) {
           destCM = src.getColorModel();
       }
       return new BufferedImage(destCM,
                                destCM.createCompatibleWritableRaster(
                                        src.getWidth(), src.getHeight()),
                                destCM.isAlphaPremultiplied(), null);
   }
   /**
    * {@inheritDoc}
    */
   public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
       return (Point2D) srcPt.clone();
   }
   /**
    * {@inheritDoc}
    */
   public RenderingHints getRenderingHints() {
       return null;
   }

}


 </source>