Java/2D Graphics GUI/Buffer Paint

Материал из Java эксперт
Перейти к: навигация, поиск

Buffered draw without flicker

<source lang="java"> import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; public class BufferedDraw extends JPanel implements MouseListener,

   MouseMotionListener {
 Rectangle rect = new Rectangle(0, 0, 100, 50);
 BufferedImage bi = new BufferedImage(5, 5, BufferedImage.TYPE_INT_RGB);
 Graphics2D big;
 int last_x, last_y;
 boolean firstTime = true;
 Rectangle area;
 boolean pressOut = false;
 public BufferedDraw() {
   setBackground(Color.white);
   addMouseMotionListener(this);
   addMouseListener(this);
 }
 // Handles the event of the user pressing down the mouse button.
 public void mousePressed(MouseEvent e) {
   last_x = rect.x - e.getX();
   last_y = rect.y - e.getY();
   // Checks whether or not the cursor is inside of the rectangle while the
   // user is pressing themouse.
   if (rect.contains(e.getX(), e.getY())) {
     updateLocation(e);
   } else {
     pressOut = true;
   }
 }
 // Handles the event of a user dragging the mouse while holding down the
 // mouse button.
 public void mouseDragged(MouseEvent e) {
   if (!pressOut) {
     updateLocation(e);
   }
 }
 // Handles the event of a user releasing the mouse button.
 public void mouseReleased(MouseEvent e) {
   if (rect.contains(e.getX(), e.getY())) {
     updateLocation(e);
   }
 }
 public void mouseMoved(MouseEvent e) {
 }
 public void mouseClicked(MouseEvent e) {
 }
 public void mouseExited(MouseEvent e) {
 }
 public void mouseEntered(MouseEvent e) {
 }
 public void updateLocation(MouseEvent e) {
   rect.setLocation(last_x + e.getX(), last_y + e.getY());
   repaint();
 }
 public void paint(Graphics g) {
   update(g);
 }
 public void update(Graphics g) {
   Graphics2D g2 = (Graphics2D) g;
   if (firstTime) {
     Dimension dim = getSize();
     int w = dim.width;
     int h = dim.height;
     area = new Rectangle(dim);
     bi = (BufferedImage) createImage(w, h);
     big = bi.createGraphics();
     rect.setLocation(w / 2 - 50, h / 2 - 25);
     big.setStroke(new BasicStroke(8.0f));
     firstTime = false;
   }
   big.setColor(Color.white);
   big.clearRect(0, 0, area.width, area.height);
   big.setPaint(Color.red);
   big.draw(rect);
   big.setPaint(Color.blue);
   big.fill(rect);
   g2.drawImage(bi, 0, 0, this);
 }
 private boolean checkRect() {
   if (area == null) {
     return false;
   }
   if (area.contains(rect.x, rect.y, 100, 50)) {
     return true;
   }
   int new_x = rect.x;
   int new_y = rect.y;
   if ((rect.x + 100) > area.width) {
     new_x = area.width - 99;
   }
   if (rect.x < 0) {
     new_x = -1;
   }
   if ((rect.y + 50) > area.height) {
     new_y = area.height - 49;
   }
   if (rect.y < 0) {
     new_y = -1;
   }
   rect.setLocation(new_x, new_y);
   return false;
 }
 public static void main(String s[]) {
   JFrame f = new JFrame("BufferedShapeMover");
   f.addWindowListener(new WindowAdapter() {
     public void windowClosing(WindowEvent e) {
       System.exit(0);
     }
   });
   f.getContentPane().setLayout(new BorderLayout());
   f.getContentPane().add(new BufferedDraw(), "Center");
   f.pack();
   f.setSize(new Dimension(550, 250));
   f.show();
 }

}


      </source>   



Composite BufferedImage

<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.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

* 
* @author Romain Guy
*/

public class SourceInDemo extends JFrame {

 private JCheckBox shadow;
 public SourceInDemo() {
   super("Source In");
   add(new ImageViewer(), BorderLayout.CENTER);
   JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
   panel.add(shadow = new JCheckBox("Drop Shadow"));
   shadow.addChangeListener(new ChangeListener() {
     public void stateChanged(ChangeEvent changeEvent) {
       repaint();
     }
   });
   add(panel, BorderLayout.SOUTH);
   setSize(350, 250);
   setLocationRelativeTo(null);
   setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 }
 private class ImageViewer extends JComponent {
   private BufferedImage image, landscape;
   private ImageViewer() {
     try {
       image = ImageIO.read(getClass().getResource("picture.png"));
       landscape = ImageIO.read(getClass().getResource("landscape.jpg"));
     } catch (IOException ex) {
       ex.printStackTrace();
     }
   }
   @Override
   protected void paintComponent(Graphics g) {
     BufferedImage temp = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
     Graphics2D g2 = temp.createGraphics();
     if (shadow.isSelected()) {
       int x = (getWidth() - image.getWidth()) / 2;
       int y = (getHeight() - image.getHeight()) / 2;
       g2.drawImage(image, x + 4, y + 10, null);
       Composite oldComposite = g2.getComposite();
       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.75f));
       g2.setColor(Color.BLACK);
       g2.fillRect(0, 0, getWidth(), getHeight());
       g2.setComposite(oldComposite);
       g2.drawImage(image, x, y, null);
     } else {
       int x = (getWidth() - image.getWidth()) / 2;
       int y = (getHeight() - image.getHeight()) / 2;
       g2.drawImage(image, x, y, null);
       Composite oldComposite = g2.getComposite();
       g2.setComposite(AlphaComposite.SrcIn);
       x = (getWidth() - landscape.getWidth()) / 2;
       y = (getHeight() - landscape.getHeight()) / 2;
       g2.drawImage(landscape, x, y, null);
       g2.setComposite(oldComposite);
     }
     g2.dispose();
     g.drawImage(temp, 0, 0, null);
   }
 }
 public static void main(String... args) {
   SwingUtilities.invokeLater(new Runnable() {
     public void run() {
       new SourceInDemo().setVisible(true);
     }
   });
 }

}

</source>   



Create a multiple buffer strategy with the number of buffers given

<source lang="java">

/*

* Copyright (c) 1995 - 2008 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 Sun Microsystems 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 test takes a number up to 13 as an argument (assumes 2 by
* default) and creates a multiple buffer strategy with the number of
* buffers given.  This application enters full-screen mode, if available,
* and flips back and forth between each buffer (each signified by a different
* color).
*/

import java.awt.Color; import java.awt.DisplayMode; import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.image.BufferStrategy; public class MultiBufferTest {

 private static Color[] COLORS = new Color[] { Color.red, Color.blue,
     Color.green, Color.white, Color.black, Color.yellow, Color.gray,
     Color.cyan, Color.pink, Color.lightGray, Color.magenta, Color.orange,
     Color.darkGray };
 private static DisplayMode[] BEST_DISPLAY_MODES = new DisplayMode[] {
     new DisplayMode(640, 480, 32, 0), new DisplayMode(640, 480, 16, 0),
     new DisplayMode(640, 480, 8, 0) };
 Frame mainFrame;
 public MultiBufferTest(int numBuffers, GraphicsDevice device) {
   try {
     GraphicsConfiguration gc = device.getDefaultConfiguration();
     mainFrame = new Frame(gc);
     mainFrame.setUndecorated(true);
     mainFrame.setIgnoreRepaint(true);
     device.setFullScreenWindow(mainFrame);
     if (device.isDisplayChangeSupported()) {
       chooseBestDisplayMode(device);
     }
     Rectangle bounds = mainFrame.getBounds();
     mainFrame.createBufferStrategy(numBuffers);
     BufferStrategy bufferStrategy = mainFrame.getBufferStrategy();
     for (float lag = 2000.0f; lag > 0.00000006f; lag = lag / 1.33f) {
       for (int i = 0; i < numBuffers; i++) {
         Graphics g = bufferStrategy.getDrawGraphics();
         if (!bufferStrategy.contentsLost()) {
           g.setColor(COLORS[i]);
           g.fillRect(0, 0, bounds.width, bounds.height);
           bufferStrategy.show();
           g.dispose();
         }
         try {
           Thread.sleep((int) lag);
         } catch (InterruptedException e) {
         }
       }
     }
   } catch (Exception e) {
     e.printStackTrace();
   } finally {
     device.setFullScreenWindow(null);
   }
 }
 private static DisplayMode getBestDisplayMode(GraphicsDevice device) {
   for (int x = 0; x < BEST_DISPLAY_MODES.length; x++) {
     DisplayMode[] modes = device.getDisplayModes();
     for (int i = 0; i < modes.length; i++) {
       if (modes[i].getWidth() == BEST_DISPLAY_MODES[x].getWidth()
           && modes[i].getHeight() == BEST_DISPLAY_MODES[x].getHeight()
           && modes[i].getBitDepth() == BEST_DISPLAY_MODES[x].getBitDepth()) {
         return BEST_DISPLAY_MODES[x];
       }
     }
   }
   return null;
 }
 public static void chooseBestDisplayMode(GraphicsDevice device) {
   DisplayMode best = getBestDisplayMode(device);
   if (best != null) {
     device.setDisplayMode(best);
   }
 }
 public static void main(String[] args) {
   try {
     int numBuffers = 2;
     if (args != null && args.length > 0) {
       numBuffers = Integer.parseInt(args[0]);
       if (numBuffers < 2 || numBuffers > COLORS.length) {
         System.err.println("Must specify between 2 and " + COLORS.length
             + " buffers");
         System.exit(1);
       }
     }
     GraphicsEnvironment env = GraphicsEnvironment
         .getLocalGraphicsEnvironment();
     GraphicsDevice device = env.getDefaultScreenDevice();
     MultiBufferTest test = new MultiBufferTest(numBuffers, device);
   } catch (Exception e) {
     e.printStackTrace();
   }
   System.exit(0);
 }

}

</source>   



Data Buffer Grabber

<source lang="java">

import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.Raster; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; /*

* DataBufferGrabber.java
*
* Created on May 2, 2007, 7:51 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 DataBufferGrabber extends JComponent {

   private final int SWATCH_SIZE = 500;
   
   /** Creates a new instance of DataBufferGrabber */
   public DataBufferGrabber() {
       setPreferredSize(new Dimension(2 * SWATCH_SIZE, SWATCH_SIZE));
   }
   
   /**
    * Perform and time several drawImage() calls with the given parameters
    * and return the number of milliseconds that the operation took.
    */
   private long copyImage(Graphics g, BufferedImage image, int x, int y) {
       long startTime = System.nanoTime();
       // Do the operation several times to make the timings more significant
       for (int i = 0; i < 100; ++i) {
           g.drawImage(image, x, y, null);
       }
       // Make sure any graphics commands in hardware get flushed before
       // stopping the clock
       Toolkit.getDefaultToolkit().sync();
       long endTime = System.nanoTime();
       return (endTime - startTime) / 1000000;
   }
   
   protected void paintComponent(Graphics g) {
       // create an image
       BufferedImage bImg = new BufferedImage(SWATCH_SIZE, 
               SWATCH_SIZE, BufferedImage.TYPE_INT_RGB);
       Graphics gImage = bImg.getGraphics();
       gImage.setColor(Color.WHITE);
       gImage.fillRect(0, 0, SWATCH_SIZE, SWATCH_SIZE);
       
       // Time how long it takes to copy the managed version
       long managedTime = copyImage(g, bImg, 0, 0);
       System.out.println("Managed: " + managedTime + " ms");
       
       // Now grab the pixel array, change the colors, re-run the test
       Raster raster = bImg.getRaster();
       DataBufferInt dataBuffer = (DataBufferInt)raster.getDataBuffer();
       int pixels[] = dataBuffer.getData();
       for (int i = 0; i < pixels.length; ++i) {
           // Make all pixels black
           pixels[i] = 0;
       }
       
       // Time this un-managed copy
       long unmanagedTime = copyImage(g, bImg, SWATCH_SIZE, 0);
       System.out.println("Unmanaged: " + unmanagedTime + " ms");
   }
   
   private static void createAndShowGUI() {    
       JFrame f = new JFrame("DataBufferGrabber");
       f.getContentPane().setLayout(new FlowLayout());
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setSize(100, 100);
       f.add(new DataBufferGrabber());
       f.validate();
       f.pack();
       f.setVisible(true);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               createAndShowGUI();
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

}

</source>   



Demos of a custom buffered image operation

<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.Dimension; 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.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.DirectColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; 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.event.ChangeEvent; import javax.swing.event.ChangeListener; /**

*

* Demos of a custom buffered image operation. *

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

public class ImageOpByRomain extends JFrame {

 private BufferedImage sourceImage;
 private ImagePanel imagePanel;
 private JSlider redSlider;
 private JSlider greenSlider;
 private JSlider blueSlider;
 private JSlider alphaSlider;
 public ImageOpByRomain() {
   super("Custom Image Op Demo");
   loadSourceImage();
   buildContent();
   pack();
   setLocationRelativeTo(null);
   setResizable(false);
   setDefaultCloseOperation(EXIT_ON_CLOSE);
 }
 public static void main(String... args) {
   SwingUtilities.invokeLater(new Runnable() {
     public void run() {
       new ImageOpByRomain().setVisible(true);
     }
   });
 }
 private void buildContent() {
   buildImagePanel();
   buildControlsPanel();
 }
 private void loadSourceImage() {
   try {
     sourceImage = GraphicsUtilities.loadCompatibleImage(getClass()
         .getResource("A.jpg"));
   } catch (IOException ex) {
     ex.printStackTrace();
   }
 }
 private void buildImagePanel() {
   add(imagePanel = new ImagePanel());
 }
 private void buildControlsPanel() {
   JPanel controls = new JPanel(new GridBagLayout());
   // red component
   controls.add(new JLabel("Red: 0"), new GridBagConstraints(0, 0, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(
           0, 0, 0, 0), 0, 0));
   controls.add(redSlider = new JSlider(0, 255, 255), new GridBagConstraints(
       1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
   controls.add(new JLabel("255"), new GridBagConstraints(2, 0, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE,
       new Insets(0, 0, 0, 0), 0, 0));
   // green component
   controls.add(new JLabel("Green: 0"), new GridBagConstraints(0, 1, 1, 1,
       1.0, 1.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE,
       new Insets(0, 0, 0, 0), 0, 0));
   controls.add(greenSlider = new JSlider(0, 255, 255),
       new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
           GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
   controls.add(new JLabel("255"), new GridBagConstraints(2, 1, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE,
       new Insets(0, 0, 0, 0), 0, 0));
   // blue component
   controls.add(new JLabel("Blue: 0"), new GridBagConstraints(0, 2, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(
           0, 0, 0, 0), 0, 0));
   controls.add(blueSlider = new JSlider(0, 255, 255), new GridBagConstraints(
       1, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
   controls.add(new JLabel("255"), new GridBagConstraints(2, 2, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE,
       new Insets(0, 0, 0, 0), 0, 0));
   // mix value
   controls.add(new JLabel("Mix: 0%"), new GridBagConstraints(0, 3, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(
           0, 0, 0, 0), 0, 0));
   controls.add(alphaSlider = new JSlider(0, 100, 50), new GridBagConstraints(
       1, 3, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
   controls.add(new JLabel("100%"), new GridBagConstraints(2, 3, 1, 1, 1.0,
       1.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE,
       new Insets(0, 0, 0, 0), 0, 0));
   // change listener
   ChangeListener colorChange = new ChangeListener() {
     public void stateChanged(ChangeEvent e) {
       imagePanel.setColor(new Color(redSlider.getValue(), greenSlider
           .getValue(), blueSlider.getValue()));
     }
   };
   redSlider.addChangeListener(colorChange);
   greenSlider.addChangeListener(colorChange);
   blueSlider.addChangeListener(colorChange);
   // alpha listener
   alphaSlider.addChangeListener(new ChangeListener() {
     public void stateChanged(ChangeEvent e) {
       imagePanel.setMix((float) alphaSlider.getValue() / 100.0f);
     }
   });
   add(controls, BorderLayout.SOUTH);
 }
 private class ImagePanel extends JComponent {
   private ColorTintFilter op = new ColorTintFilter(Color.WHITE, 0.5f);
   private BufferedImage cache;
   private boolean damaged;
   private ImagePanel() {
     cache = GraphicsUtilities.createCompatibleImage(sourceImage);
     damaged = true;
   }
   public void setColor(Color color) {
     op = new ColorTintFilter(color, op.getMixValue());
     damaged = true;
     repaint();
   }
   public void setMix(float mix) {
     op = new ColorTintFilter(op.getMixColor(), mix);
     damaged = true;
     repaint();
   }
   @Override
   public Dimension getPreferredSize() {
     return new Dimension(sourceImage.getWidth(), sourceImage.getHeight());
   }
   @Override
   protected void paintComponent(Graphics g) {
     Graphics2D g2 = (Graphics2D) g;
     if (damaged) {
       op.filter(sourceImage, cache);
     }
     int x = (getWidth() - cache.getWidth()) / 2;
     int y = (getHeight() - cache.getHeight()) / 2;
     g2.drawImage(cache, x, y, null);
   }
 }

} /*

* $Id: AbstractFilter.java,v 1.1 2006/11/05 15:40:51 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.5 2007/02/10 03:21:54 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 tint 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;
 private int[] preMultipliedRed;
 private int[] preMultipliedGreen;
 private int[] preMultipliedBlue;
 /**
*

* 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;
   int mix_r = (int) (mixColor.getRed() * mixValue);
   int mix_g = (int) (mixColor.getGreen() * mixValue);
   int mix_b = (int) (mixColor.getBlue() * mixValue);
   // Since we use only lookup tables to apply the filter, this filter
   // could be implemented as a LookupOp.
   float factor = 1.0f - mixValue;
   preMultipliedRed = new int[256];
   preMultipliedGreen = new int[256];
   preMultipliedBlue = new int[256];
   for (int i = 0; i < 256; i++) {
     int value = (int) (i * factor);
     preMultipliedRed[i] = value + mix_r;
     preMultipliedGreen[i] = value + mix_g;
     preMultipliedBlue[i] = value + mix_b;
   }
 }
 /**
*

* 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) {
     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);
   mixColor(pixels);
   GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels);
   return dst;
 }
 private void mixColor(int[] pixels) {
   for (int i = 0; i < pixels.length; i++) {
     int argb = pixels[i];
     pixels[i] = (argb & 0xFF000000)
         | preMultipliedRed[(argb >> 16) & 0xFF] << 16
         | preMultipliedGreen[(argb >> 8) & 0xFF] << 8
         | preMultipliedBlue[argb & 0xFF];
   }
 }

} /*

* $Id: GraphicsUtilities.java,v 1.1 2006/11/05 15:40:51 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);
   }
 }

}

</source>   



Image offline rendering

<source lang="java"> import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruponent; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.InputStream; import javax.swing.JFrame; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageDecoder; public class ImageDuplicity extends Component {

 private BufferedImage image;
 public void paint(Graphics g) {
   Graphics2D g2 = (Graphics2D) g;
   if (image == null)
     createOffscreenImage();
   g2.drawImage(image, 0, 0, this);
 }
 private void createOffscreenImage() {
   Dimension d = getSize();
   int width = d.width; 
   int height = d.height;
   image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
   Graphics2D g2 = image.createGraphics();
   g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
       RenderingHints.VALUE_ANTIALIAS_ON);
   try {
     String filename = "largejexpLogo.jpg";
     InputStream in = getClass().getResourceAsStream(filename);
     JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(in);
     BufferedImage image = decoder.decodeAsBufferedImage();
     in.close();
     g2.drawImage(image, 0, 0, width, height, null);
   } catch (Exception e) {
     System.out.print(e);
   }
   g2.setStroke(new BasicStroke(2));
   Color[] colors = { Color.red, Color.blue, Color.green };
   for (int i = -32; i < 40; i += 8) {
     g2.setPaint(colors[Math.abs(i) % 3]);
     g2.drawOval(i, i, width - i * 2, height - i * 2);
   }
 }
 public static void main(String[] args) {
   JFrame f = new JFrame();
   f.setLayout(new BorderLayout());
   Component c = new ImageDuplicity();
   f.add(c, BorderLayout.CENTER);
   f.setSize(200, 250);
   f.setVisible(true);
 }

}

      </source>   



repaint just the affected part of the component

<source lang="java"> import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import javax.swing.JComponent; import javax.swing.JFrame; public class ClippedDragImage extends DragImage {

 int oldX, oldY;
 public ClippedDragImage(Image i) {
   super(i);
 }
 public void mouseDragged(MouseEvent e) {
   imageX = e.getX();
   imageY = e.getY();
   Rectangle r = getAffectedArea(oldX, oldY, imageX, imageY, imageWidth, imageHeight);
   repaint(r); // repaint just the affected part of the component
   oldX = imageX;
   oldY = imageY;
 }
 private Rectangle getAffectedArea(int oldx, int oldy, int newx, int newy, int width, int height) {
   int x = Math.min(oldx, newx);
   int y = Math.min(oldy, newy);
   int w = (Math.max(oldx, newx) + width) - x;
   int h = (Math.max(oldy, newy) + height) - y;
   return new Rectangle(x, y, w, h);
 }
 public static void main(String[] args) {
   String imageFile = "A.jpg";
   Image image = Toolkit.getDefaultToolkit().getImage(
       ClippedDragImage.class.getResource(imageFile));
   image = image.getScaledInstance(imageWidth, imageHeight, Image.SCALE_DEFAULT);
   JFrame frame = new JFrame("ClippedDragImage");
   frame.getContentPane().add(new ClippedDragImage(image));
   frame.setSize(300, 300);
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setVisible(true);
 }

} class DragImage extends JComponent implements MouseMotionListener {

 static int imageWidth = 60, imageHeight = 60;
 int grid = 10;
 int imageX, imageY;
 Image image;
 public DragImage(Image i) {
   image = i;
   addMouseMotionListener(this);
 }
 public void mouseDragged(MouseEvent e) {
   imageX = e.getX();
   imageY = e.getY();
   repaint();
 }
 public void mouseMoved(MouseEvent e) {
 }
 public void paint(Graphics g) {
   Graphics2D g2 = (Graphics2D) g;
   g2.drawImage(image, imageX, imageY, this);
 }

}

</source>   



RepaintManager.currentManager(null).setDoubleBufferingEnabled(false)

<source lang="java">

import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.RepaintManager; public class DragImage extends JComponent implements MouseMotionListener {

 static int imageWidth = 60, imageHeight = 60;
 int imageX, imageY;
 Image image;
 public DragImage(Image i) {
   image = i;
   addMouseMotionListener(this);
 }
 public void mouseDragged(MouseEvent e) {
   imageX = e.getX();
   imageY = e.getY();
   repaint();
 }
 public void mouseMoved(MouseEvent e) {
 }
 public void paint(Graphics g) {
   Graphics2D g2 = (Graphics2D) g;
   g2.drawImage(image, imageX, imageY, this);
 }
 public static void main(String[] args) {
   String imageFile = "A.jpg";
   // Turn off double buffering
   RepaintManager.currentManager(null).setDoubleBufferingEnabled(false);
   Image image = Toolkit.getDefaultToolkit().getImage(DragImage.class.getResource(imageFile));
   image = image.getScaledInstance(imageWidth, imageHeight, Image.SCALE_DEFAULT);
   JFrame frame = new JFrame("DragImage");
   frame.getContentPane().add(new DragImage(image));
   frame.setSize(300, 300);
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   frame.setVisible(true);
 }

}

</source>   



Smooth move using double buffer

<source lang="java"> import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import javax.swing.JFrame; import javax.swing.JPanel; public class SmoothMove extends JPanel implements MouseMotionListener {

 private int mX, mY;
 private Image mImage;
 public static void main(String[] args) {
   JFrame f = new JFrame();
   f.getContentPane().add(new SmoothMove());
   f.setSize(200, 200);
   f.show();
 }
 public SmoothMove() {
   addMouseMotionListener(this);
   setVisible(true);
 }
 public void mouseMoved(MouseEvent me) {
   mX = (int) me.getPoint().getX();
   mY = (int) me.getPoint().getY();
   repaint();
 }
 public void mouseDragged(MouseEvent me) {
   mouseMoved(me);
 }
 public void update(Graphics g) {
   paint(g);
 }
 public void paint(Graphics g) {
   // Clear the offscreen image.
   Dimension d = getSize();
   checkOffscreenImage();
   Graphics offG = mImage.getGraphics();
   offG.setColor(getBackground());
   offG.fillRect(0, 0, d.width, d.height);
   // Draw into the offscreen image.
   paintOffscreen(mImage.getGraphics());
   // Put the offscreen image on the screen.
   g.drawImage(mImage, 0, 0, null);
 }
 private void checkOffscreenImage() {
   Dimension d = getSize();
   if (mImage == null || mImage.getWidth(null) != d.width
       || mImage.getHeight(null) != d.height) {
     mImage = createImage(d.width, d.height);
   }
 }
 public void paintOffscreen(Graphics g) {
   int s = 100;
   g.setColor(Color.blue);
   g.fillRect(mX - s / 2, mY - s / 2, s, s);
 }

}

      </source>