/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Icy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.image;
import icy.gui.util.FontUtil;
import icy.network.URLUtil;
import icy.system.thread.ThreadUtil;
import icy.util.GraphicsUtil;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;
/**
* Image utilities class.
*
* @author stephane
*/
public class ImageUtil
{
public static String getImageTypeString(int type)
{
switch (type)
{
case BufferedImage.TYPE_CUSTOM:
return "TYPE_CUSTOM";
case BufferedImage.TYPE_INT_RGB:
return "TYPE_INT_RGB";
case BufferedImage.TYPE_INT_ARGB:
return "TYPE_INT_ARGB";
case BufferedImage.TYPE_INT_ARGB_PRE:
return "TYPE_INT_ARGB_PRE";
case BufferedImage.TYPE_INT_BGR:
return "TYPE_INT_BGR";
case BufferedImage.TYPE_3BYTE_BGR:
return "TYPE_3BYTE_BGR";
case BufferedImage.TYPE_4BYTE_ABGR:
return "TYPE_4BYTE_ABGR";
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
return "TYPE_4BYTE_ABGR_PRE";
case BufferedImage.TYPE_USHORT_565_RGB:
return "TYPE_USHORT_565_RGB";
case BufferedImage.TYPE_USHORT_555_RGB:
return "TYPE_USHORT_555_RGB";
case BufferedImage.TYPE_BYTE_GRAY:
return "TYPE_BYTE_GRAY";
case BufferedImage.TYPE_USHORT_GRAY:
return "TYPE_USHORT_GRAY";
case BufferedImage.TYPE_BYTE_BINARY:
return "TYPE_BYTE_BINARY";
case BufferedImage.TYPE_BYTE_INDEXED:
return "TYPE_BYTE_INDEXED";
default:
return "UNKNOWN TYPE";
}
}
public static String getTransparencyString(int transparency)
{
switch (transparency)
{
case Transparency.OPAQUE:
return "OPAQUE";
case Transparency.BITMASK:
return "BITMASK";
case Transparency.TRANSLUCENT:
return "TRANSLUCENT";
default:
return "UNKNOWN TRANSPARENCY";
}
}
/**
* Wait for dimension information of specified image being loaded.
*
* @param image
* image we are waiting informations for.
*/
public static void waitImageReady(Image image)
{
if (image != null)
{
final long st = System.currentTimeMillis();
// wait 2 seconds max
while ((image.getWidth(null) == -1) && ((System.currentTimeMillis() - st) < 2000))
ThreadUtil.sleep(1);
}
}
/**
* Create a 8 bits indexed buffered image from specified <code>IndexColorModel</code><br>
* and byte array data.
*/
public static BufferedImage createIndexedImage(int w, int h, IndexColorModel cm, byte[] data)
{
final WritableRaster raster = Raster.createInterleavedRaster(new DataBufferByte(data, w * h, 0), w, h, w, 1,
new int[] {0}, null);
return new BufferedImage(cm, raster, false, null);
}
/**
* Load an image from specified path
*/
public static BufferedImage load(String path, boolean displayError)
{
return load(URLUtil.getURL(path), displayError);
}
/**
* Load an image from specified path
*/
public static BufferedImage load(String path)
{
return load(path, true);
}
/**
* Load an image from specified url
*/
public static BufferedImage load(URL url, boolean displayError)
{
if (url != null)
{
try
{
return ImageIO.read(url);
}
catch (IOException e)
{
if (displayError)
System.err.println("Can't load image from " + url);
}
}
return null;
}
/**
* Asynchronously load an image from specified url.<br/>
* Use {@link #waitImageReady(Image)} to know if width and height property
*/
public static Image loadAsync(URL url)
{
return Toolkit.getDefaultToolkit().createImage(url);
}
/**
* Asynchronously load an image from specified path.<br/>
* Use {@link #waitImageReady(Image)} to know if width and height property
*/
public static Image loadAsync(String path)
{
return Toolkit.getDefaultToolkit().createImage(path);
}
/**
* Load an image from specified url
*/
public static BufferedImage load(URL url)
{
return load(url, true);
}
/**
* Load an image from specified file
*/
public static BufferedImage load(File file, boolean displayError)
{
if (file != null)
{
try
{
return ImageIO.read(file);
}
catch (IOException e)
{
if (displayError)
System.err.println("Can't load image from " + file);
}
}
return null;
}
/**
* Load an image from specified file
*/
public static BufferedImage load(File file)
{
return loadImage(file, true);
}
/**
* Load an image from specified InputStream
*/
public static BufferedImage load(InputStream input, boolean displayError)
{
if (input != null)
{
try
{
return ImageIO.read(input);
}
catch (Exception e)
{
if (displayError)
System.err.println("Can't load image from stream " + input);
}
}
return null;
}
/**
* Load an image from specified InputStream
*/
public static BufferedImage load(InputStream input)
{
return load(input, true);
}
/**
* @deprecated Use {@link ImageUtil#load(String, boolean)} instead
*/
@Deprecated
public static BufferedImage loadImage(String path, boolean displayError)
{
return load(path, displayError);
}
/**
* @deprecated Use {@link ImageUtil#load(String)} instead
*/
@Deprecated
public static BufferedImage loadImage(String path)
{
return load(path);
}
/**
* @deprecated Use {@link ImageUtil#load(URL, boolean)} instead
*/
@Deprecated
public static BufferedImage loadImage(URL url, boolean displayError)
{
return load(url, displayError);
}
/**
* @deprecated Use {@link ImageUtil#load(URL)} instead
*/
@Deprecated
public static Image loadImage(URL url)
{
return load(url);
}
/**
* @deprecated Use {@link ImageUtil#load(File, boolean)} instead
*/
@Deprecated
public static BufferedImage loadImage(File file, boolean displayError)
{
return load(file, displayError);
}
/**
* @deprecated Use {@link ImageUtil#load(File)} instead
*/
@Deprecated
public static BufferedImage loadImage(File file)
{
return load(file);
}
/**
* @deprecated Use {@link ImageUtil#load(InputStream, boolean)} instead
*/
@Deprecated
public static BufferedImage loadImage(InputStream input, boolean displayError)
{
return load(input, displayError);
}
/**
* @deprecated Use {@link ImageUtil#load(InputStream)} instead
*/
@Deprecated
public static BufferedImage loadImage(InputStream input)
{
return load(input);
}
/**
* Save an image to specified path in specified format
*/
public static boolean save(RenderedImage image, String format, String path)
{
if (path != null)
{
try
{
return ImageIO.write(image, format, new FileOutputStream(path));
}
catch (IOException e)
{
System.err.println("Can't save image to " + path);
}
}
return false;
}
/**
* Save an image to specified file in specified format
*/
public static boolean save(RenderedImage image, String format, File file)
{
if (file != null)
{
try
{
return ImageIO.write(image, format, file);
}
catch (IOException e)
{
System.err.println("Can't save image to " + file);
}
}
return false;
}
/**
* @deprecated Use {@link ImageUtil#save(RenderedImage, String, String)} instead
*/
@Deprecated
public static boolean saveImage(RenderedImage image, String format, String path)
{
return save(image, format, path);
}
/**
* @deprecated Use {@link ImageUtil#save(RenderedImage, String, File)} instead
*/
@Deprecated
public static boolean saveImage(RenderedImage image, String format, File file)
{
return save(image, format, file);
}
/**
* Return a RenderedImage from the given Image object.
*/
public static RenderedImage toRenderedImage(Image image)
{
return toBufferedImage(image);
}
/**
* Return a ARGB BufferedImage from the given Image object.
* If the image is already a BufferedImage image then it's directly returned
*/
public static BufferedImage toBufferedImage(Image image)
{
if (image instanceof BufferedImage)
return (BufferedImage) image;
// be sure image data are ready
waitImageReady(image);
final BufferedImage bufImage = new BufferedImage(image.getWidth(null), image.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = bufImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return bufImage;
}
/**
* Scale an image with specified size.
*/
public static BufferedImage scale(Image image, int width, int height)
{
if (image != null)
{
final BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = result.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(image, 0, 0, width, height, null);
g.dispose();
return result;
}
return null;
}
/**
* Scale an image with specified size (try to keep best quality).
*/
public static BufferedImage scaleQuality(Image image, int width, int height)
{
if (image != null)
{
Image current = image;
// be sure image data are ready
waitImageReady(image);
int w = image.getWidth(null);
int h = image.getHeight(null);
do
{
if (w > width)
{
w /= 2;
if (w < width)
w = width;
}
else
w = width;
if (h > height)
{
h /= 2;
if (h < height)
h = height;
}
else
h = height;
final BufferedImage result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = result.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(current, 0, 0, w, h, null);
g.dispose();
current = result;
}
while (w != width || h != height);
return (BufferedImage) current;
}
return null;
}
/**
* Convert an image to a BufferedImage.<br>
* If <code>out</out> is null, by default a <code>BufferedImage.TYPE_INT_ARGB</code> is created.
*/
public static BufferedImage convert(Image in, BufferedImage out)
{
final BufferedImage result;
// be sure image data are ready
waitImageReady(in);
// no output type specified ? use ARGB
if (out == null)
result = new BufferedImage(in.getWidth(null), in.getHeight(null), BufferedImage.TYPE_INT_ARGB);
else
result = out;
final Graphics g = result.getGraphics();
g.drawImage(in, 0, 0, null);
g.dispose();
return result;
}
/**
* Returns <code>true</code> if the specified image is a grayscale image whatever is the image
* type (GRAY, RGB, ARGB...)
*/
public static boolean isGray(BufferedImage image)
{
if (image == null)
return false;
if (image.getType() == BufferedImage.TYPE_BYTE_GRAY)
return true;
if (image.getType() == BufferedImage.TYPE_USHORT_GRAY)
return true;
final int[] rgbArray = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
for (int value : rgbArray)
{
final int c0 = (value >> 0) & 0xFF;
final int c1 = (value >> 8) & 0xFF;
if (c0 != c1)
return false;
final int c2 = (value >> 16) & 0xFF;
if (c0 != c2)
return false;
}
return true;
}
/**
* Convert an image to grey image (<code>BufferedImage.TYPE_BYTE_GRAY</code>).
*/
public static BufferedImage toGray(Image image)
{
if (image != null)
{
// be sure image data are ready
waitImageReady(image);
return convert(image, new BufferedImage(image.getWidth(null), image.getHeight(null),
BufferedImage.TYPE_BYTE_GRAY));
}
return null;
}
/**
* Convert an image to RGB image (<code>BufferedImage.TYPE_INT_RGB</code>).
*/
public static BufferedImage toRGBImage(Image image)
{
if (image != null)
{
// be sure image data are ready
waitImageReady(image);
return convert(image, new BufferedImage(image.getWidth(null), image.getHeight(null),
BufferedImage.TYPE_INT_RGB));
}
return null;
}
/**
* Convert an image to ARGB image (<code>BufferedImage.TYPE_INT_ARGB</code>).
*/
public static BufferedImage toARGBImage(Image image)
{
if (image != null)
{
// be sure image data are ready
waitImageReady(image);
return convert(image, new BufferedImage(image.getWidth(null), image.getHeight(null),
BufferedImage.TYPE_INT_ARGB));
}
return null;
}
/**
* @deprecated Use {@link ImageUtil#scale(Image, int, int)} instead.
*/
@Deprecated
public static BufferedImage scaleImage(Image image, int width, int height)
{
return scale(image, width, height);
}
/**
* @deprecated Use {@link ImageUtil#scaleQuality(Image, int, int)} instead.
*/
@Deprecated
public static BufferedImage scaleImageQuality(Image image, int width, int height)
{
return scaleQuality(image, width, height);
}
/**
* @deprecated Use {@link ImageUtil#convert(Image, BufferedImage)} instead.
*/
@Deprecated
public static BufferedImage convertImage(Image in, BufferedImage out)
{
return convert(in, out);
}
/**
* @deprecated Use {@link ImageUtil#toGray(Image)} instead.
*/
@Deprecated
public static BufferedImage toGrayImage(Image image)
{
return toGray(image);
}
/**
* Create a copy of the input image.<br>
* Result is always a <code>BufferedImage.TYPE_INT_ARGB</code> type image.
*/
public static BufferedImage getCopy(Image in)
{
return convert(in, null);
}
/**
* Return true if image has the same size
*/
public static boolean sameSize(BufferedImage im1, BufferedImage im2)
{
return (im1.getWidth() == im2.getWidth()) && (im1.getHeight() == im2.getHeight());
}
/**
* Apply simple color filter with specified alpha factor to the image
*/
public static void applyColorFilter(Image image, Color color, float alpha)
{
if (image != null)
{
// be sure image data are ready
waitImageReady(image);
// should be Graphics2D compatible
final Graphics2D g = (Graphics2D) image.getGraphics();
final Rectangle rect = new Rectangle(image.getWidth(null), image.getHeight(null));
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g.setColor(color);
g.fill(rect);
g.dispose();
}
}
/**
* Return an image which contains specified color depending original alpha intensity image
*/
public static Image getColorImageFromAlphaImage(Image alphaImage, Color color)
{
return paintColorImageFromAlphaImage(alphaImage, null, color);
}
/**
* Paint the specified color in 'out' image depending original alpha intensity from 'alphaImage'
*/
public static Image paintColorImageFromAlphaImage(Image alphaImage, Image out, Color color)
{
final int w;
final int h;
final Image result;
if (out == null)
{
// be sure image data are ready
waitImageReady(alphaImage);
w = alphaImage.getWidth(null);
h = alphaImage.getHeight(null);
if ((w == -1) || (h == -1))
return null;
result = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
else
{
// be sure image data are ready
waitImageReady(out);
w = out.getWidth(null);
h = out.getHeight(null);
if ((w == -1) || (h == -1))
return null;
result = out;
}
final Graphics2D g = (Graphics2D) result.getGraphics();
// clear
g.setBackground(new Color(0x00000000, true));
g.clearRect(0, 0, w, h);
// be sure image data are ready
waitImageReady(alphaImage);
// draw icon
g.drawImage(alphaImage, 0, 0, null);
// set fill color
g.setComposite(AlphaComposite.SrcAtop);
g.setColor(color);
g.fillRect(0, 0, w, h);
g.dispose();
return result;
}
/**
* Draw text in the specified image with specified parameters.<br>
*/
public static void drawText(Image image, String text, float x, float y, int size, Color color)
{
final Graphics2D g = (Graphics2D) image.getGraphics();
// prepare setting
g.setColor(color);
g.setFont(FontUtil.setSize(g.getFont(), size));
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// draw icon
g.drawString(text, x, y);
g.dispose();
}
/**
* Draw text at top right in the specified image with specified parameters.<br>
*/
public static void drawTextTopRight(Image image, String text, int size, boolean bold, Color color)
{
final Graphics2D g = (Graphics2D) image.getGraphics();
// prepare setting
g.setColor(color);
g.setFont(FontUtil.setSize(g.getFont(), size));
if (bold)
g.setFont(FontUtil.setStyle(g.getFont(), Font.BOLD));
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// get string bounds
final Rectangle2D bounds = GraphicsUtil.getStringBounds(g, text);
// be sure image data are ready
waitImageReady(image);
final float w = image.getWidth(null);
// draw text
g.drawString(text, w - ((float) bounds.getWidth()), 0 - (float) bounds.getY());
g.dispose();
}
}