/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.util;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import javax.imageio.*;
import javax.swing.*;
import net.java.sip.communicator.util.swing.*;
/**
* Utility methods for image manipulation.
*
* @author Sebastien Mazy
* @author Yana Stamcheva
* @author Lubomir Marinov
*/
public class ImageUtils
{
/**
* The Logger used by the ImageUtils class for logging
* output.
*/
private static final Logger logger = Logger.getLogger(ImageUtils.class);
/**
* Returns a scaled image fitting within the given bounds while keeping the
* aspect ratio.
*
* @param image the image to scale
* @param width maximum width of the scaled image
* @param height maximum height of the scaled image
* @return the scaled image
*/
public static Image scaleImageWithinBounds( Image image,
int width,
int height)
{
int initialWidth = image.getWidth(null);
int initialHeight = image.getHeight(null);
Image scaledImage;
int scaleHint = Image.SCALE_SMOOTH;
double originalRatio =
(double) initialWidth / initialHeight;
double areaRatio = (double) width / height;
if(originalRatio > areaRatio)
scaledImage = image.getScaledInstance(width, -1, scaleHint);
else
scaledImage = image.getScaledInstance(-1, height, scaleHint);
return scaledImage;
}
/**
* Scales the given image to fit in the given width and
* height.
* @param image the image to scale
* @param width the desired width
* @param height the desired height
* @return the scaled image
*/
public static ImageIcon scaleIconWithinBounds(Image image, int width,
int height)
{
return new ImageIcon(scaleImageWithinBounds(image, width, height));
}
/**
* Scales the given image to fit in the given width and
* height.
*
* @param imageBytes the bytes of the image to scale
* @param width the desired width
* @param height the desired height
* @return the scaled image
*/
public static ImageIcon scaleImageWithinBounds( byte[] imageBytes,
int width,
int height)
{
if (imageBytes == null || !(imageBytes.length > 0))
return null;
Image imageIcon = null;
try
{
Image image = null;
// sometimes ImageIO fails, will fall back to awt Toolkit
try
{
image = ImageIO.read(new ByteArrayInputStream(imageBytes));
} catch (Exception e)
{
try
{
image = Toolkit.getDefaultToolkit().createImage(imageBytes);
} catch (Exception e1)
{
// if it fails throw the original exception
throw e;
}
}
if(image != null)
imageIcon = scaleImageWithinBounds(image, width, height);
else
if (logger.isTraceEnabled())
logger.trace("Unknown image format or error reading image");
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Could not create image.", e);
}
if (imageIcon != null)
return new ImageIcon(imageIcon);
return null;
}
/**
* Creates a rounded avatar image.
*
* @param image image of the initial avatar image.
* @param width the desired width
* @param height the desired height
* @return The rounded corner image.
*/
public static Image getScaledRoundedImage( Image image,
int width,
int height)
{
ImageIcon scaledImage =
ImageUtils.scaleIconWithinBounds(image, width, height);
int scaledImageWidth = scaledImage.getIconWidth();
int scaledImageHeight = scaledImage.getIconHeight();
if(scaledImageHeight <= 0 ||
scaledImageWidth <= 0)
return null;
// Just clipping the image would cause jaggies on Windows and Linux.
// The following is a soft clipping solution based on the solution
// proposed by Chris Campbell:
// http://java.sun.com/mailers/techtips/corejava/2006/tt0923.html
BufferedImage destImage
= new BufferedImage(scaledImageWidth, scaledImageHeight,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = destImage.createGraphics();
try
{
// Render our clip shape into the image. Note that we enable
// antialiasing to achieve the soft clipping effect.
g.setComposite(AlphaComposite.Src);
AntialiasingManager.activateAntialiasing(g);
g.setColor(Color.WHITE);
g.fillRoundRect(0, 0, scaledImageWidth, scaledImageHeight, 15, 15);
// We use SrcAtop, which effectively uses the
// alpha value as a coverage value for each pixel stored in the
// destination. For the areas outside our clip shape, the
// destination alpha will be zero, so nothing is rendered in those
// areas. For the areas inside our clip shape, the destination alpha
// will be fully opaque, so the full color is rendered. At the edges,
// the original antialiasing is carried over to give us the desired
// soft clipping effect.
g.setComposite(AlphaComposite.SrcAtop);
g.drawImage(scaledImage.getImage(), 0, 0, null);
}
finally
{
g.dispose();
}
return destImage;
}
/**
* Returns a scaled instance of the given image.
* @param image the image to scale
* @param width the desired width
* @param height the desired height
* @return a byte array containing the scaled image
*/
public static byte[] getScaledInstanceInBytes(
Image image, int width, int height)
{
byte[] scaledBytes = null;
BufferedImage scaledImage
= (BufferedImage) getScaledRoundedImage(image, width, height);
if (scaledImage != null)
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try
{
ImageIO.write(scaledImage, "png", outStream);
scaledBytes = outStream.toByteArray();
}
catch (IOException e)
{
if (logger.isDebugEnabled())
logger.debug("Could not scale image in bytes.", e);
}
}
return scaledBytes;
}
/**
* Returns a scaled rounded icon from the given image, scaled
* within the given width and height.
* @param image the image to scale
* @param width the maximum width of the scaled icon
* @param height the maximum height of the scaled icon
* @return a scaled rounded icon
*/
public static ImageIcon getScaledRoundedIcon(Image image, int width,
int height)
{
Image scaledImage = getScaledRoundedImage(image, width, height);
if (scaledImage != null)
return new ImageIcon(scaledImage);
return null;
}
/**
* Creates a rounded corner scaled image.
*
* @param imageBytes The bytes of the image to be scaled.
* @param width The maximum width of the scaled image.
* @param height The maximum height of the scaled image.
*
* @return The rounded corner scaled image.
*/
public static ImageIcon getScaledRoundedIcon( byte[] imageBytes,
int width,
int height)
{
if (imageBytes == null || !(imageBytes.length > 0))
return null;
ImageIcon imageIcon = null;
try
{
Image image = null;
// sometimes ImageIO fails, will fall back to awt Toolkit
try
{
image = ImageIO.read(new ByteArrayInputStream(imageBytes));
} catch (Exception e)
{
try
{
image = Toolkit.getDefaultToolkit().createImage(imageBytes);
} catch (Exception e1)
{
// if it fails throw the original exception
throw e;
}
}
if(image != null)
imageIcon = getScaledRoundedIcon(image, width, height);
else
if (logger.isTraceEnabled())
logger.trace("Unknown image format or error reading image");
}
catch (Exception e)
{
if (logger.isDebugEnabled())
logger.debug("Could not create image.", e);
}
return imageIcon;
}
/**
* Returns the buffered image corresponding to the given url image path.
*
* @param imagePath the path indicating, where we can find the image.
*
* @return the buffered image corresponding to the given url image path.
*/
public static BufferedImage getBufferedImage(URL imagePath)
{
BufferedImage image = null;
if (imagePath != null)
{
try
{
image = ImageIO.read(imagePath);
}
catch (IOException ex)
{
if (logger.isDebugEnabled())
logger.debug("Failed to load image:" + imagePath, ex);
}
}
return image;
}
/**
* Returns the buffered image corresponding to the given image
* @param source an image
* @return the buffered image corresponding to the given image
*/
public static BufferedImage getBufferedImage(Image source)
{
if (source == null)
{
return null;
}
else if (source instanceof BufferedImage)
{
return (BufferedImage) source;
}
int width = source.getWidth(null);
int height = source.getHeight(null);
BufferedImage image
= new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.createGraphics();
graphics.drawImage(source, 0, 0, null);
graphics.dispose();
return image;
}
/**
* Extracts bytes from image.
* @param image the image.
* @return the bytes of the image.
*/
public static byte[] toByteArray(BufferedImage image)
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
try
{
ImageIO.write(image, "png", out);
}
catch (IOException e)
{
logger.debug("Cannot convert buffered image to byte[]", e);
return null;
}
return out.toByteArray();
}
/**
* Loads an image from a given bytes array.
* @param imageBytes The bytes array to load the image from.
* @return The image for the given bytes array.
*/
public static Image getBytesInImage(byte[] imageBytes)
{
Image image = null;
try
{
image = ImageIO.read(
new ByteArrayInputStream(imageBytes));
}
catch (Exception e)
{
logger.error("Failed to convert bytes to image.", e);
}
return image;
}
}