Java/2D Graphics GUI/Buffer Paint
Содержание
- 1 Buffered draw without flicker
- 2 Composite BufferedImage
- 3 Create a multiple buffer strategy with the number of buffers given
- 4 Data Buffer Grabber
- 5 Demos of a custom buffered image operation
- 6 Compatible Images
- 7 Creating Thumbnails
- 8 Image Manipulation
- 9 Image offline rendering
- 10 repaint just the affected part of the component
- 11 RepaintManager.currentManager(null).setDoubleBufferingEnabled(false)
- 12 Smooth move using double buffer
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 ofimage
*/ 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 asimage
*/ 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 asimage
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 ofimage
* @throws IllegalArgumentException * ifnewSize
is larger than the largest dimension * ofimage
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 ofimage
* @throws IllegalArgumentException * ifnewWidth
is larger than the width of *image
or if code>newHeight</code> is larger * than the height ofimage
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 ofimage
* @throws IllegalArgumentException * ifnewSize
is larger than the largest dimension * ofimage
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 ofimage
* @throws IllegalArgumentException * ifnewWidth
is larger than the width of *image
or if code>newHeight</code> is larger * than the height ofimage 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 * ispixels
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>