GraphicsUtilities
contains a set of tools to perform
* common graphics operations easily. These operations are divided into
* several themes, listed below.
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.
*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.
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 GuyReturns 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.
BufferedImage
, compatible with the color model
* of image
*/
public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
ColorModel cm = image.getColorModel();
return new BufferedImage(cm,
cm.createCompatibleWritableRaster(image.getWidth(),
image.getHeight()),
cm.isAlphaPremultiplied(), null);
}
/**
* Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.
* * @see java.awt.Transparency * @see #createCompatibleImage(int, int) * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) * @see #createCompatibleTranslucentImage(int, int) * @see #loadCompatibleImage(java.net.URL) * @see #toCompatibleImage(java.awt.image.BufferedImage) * @param image the reference image from which the dimension and the * transparency of the new image are obtained * @return a new compatibleBufferedImage
with the same
* dimension and transparency as image
*/
public static BufferedImage createCompatibleImage(BufferedImage image) {
return createCompatibleImage(image, image.getWidth(), image.getHeight());
}
/**
* Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.
* * @see java.awt.Transparency * @see #createCompatibleImage(java.awt.image.BufferedImage) * @see #createCompatibleImage(int, int) * @see #createCompatibleTranslucentImage(int, int) * @see #loadCompatibleImage(java.net.URL) * @see #toCompatibleImage(java.awt.image.BufferedImage) * @param width the width of the new image * @param height the height of the new image * @param image the reference image from which the transparency of the new * image is obtained * @return a new compatibleBufferedImage
with the same
* transparency as image
and the specified dimension
*/
public static BufferedImage createCompatibleImage(BufferedImage image,
int width, int height) {
return getGraphicsConfiguration().createCompatibleImage(width, height,
image.getTransparency());
}
/**
* Returns a new opaque compatible image of the specified width and * height.
* * @see #createCompatibleImage(java.awt.image.BufferedImage) * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) * @see #createCompatibleTranslucentImage(int, int) * @see #loadCompatibleImage(java.net.URL) * @see #toCompatibleImage(java.awt.image.BufferedImage) * @param width the width of the new image * @param height the height of the new image * @return a new opaque compatibleBufferedImage
of the
* specified width and height
*/
public static BufferedImage createCompatibleImage(int width, int height) {
return getGraphicsConfiguration().createCompatibleImage(width, height);
}
/**
* Returns a new translucent compatible image of the specified width * and height.
* * @see #createCompatibleImage(java.awt.image.BufferedImage) * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) * @see #createCompatibleImage(int, int) * @see #loadCompatibleImage(java.net.URL) * @see #toCompatibleImage(java.awt.image.BufferedImage) * @param width the width of the new image * @param height the height of the new image * @return a new translucent compatibleBufferedImage
of the
* specified width and height
*/
public static BufferedImage createCompatibleTranslucentImage(int width,
int height) {
return getGraphicsConfiguration().createCompatibleImage(width, height,
Transparency.TRANSLUCENT);
}
/**
* Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.
* * @see #createCompatibleImage(java.awt.image.BufferedImage) * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) * @see #createCompatibleImage(int, int) * @see #createCompatibleTranslucentImage(int, int) * @see #toCompatibleImage(java.awt.image.BufferedImage) * @param resource the URL of the picture to load as a compatible image * @return a new translucent compatibleBufferedImage
of the
* specified width and height
* @throws java.io.IOException if the image cannot be read or loaded
*/
public static BufferedImage loadCompatibleImage(URL resource)
throws IOException {
BufferedImage image = ImageIO.read(resource);
return toCompatibleImage(image);
}
/**
* Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.
* * @see #createCompatibleImage(java.awt.image.BufferedImage) * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) * @see #createCompatibleImage(int, int) * @see #createCompatibleTranslucentImage(int, int) * @see #loadCompatibleImage(java.net.URL) * @param image the image to copy into a new compatible image * @return a new compatible copy, with the * same width and height and transparency and content, ofimage
*/
public static BufferedImage toCompatibleImage(BufferedImage image) {
if (image.getColorModel().equals(
getGraphicsConfiguration().getColorModel())) {
return image;
}
BufferedImage compatibleImage =
getGraphicsConfiguration().createCompatibleImage(
image.getWidth(), image.getHeight(),
image.getTransparency());
Graphics g = compatibleImage.getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return compatibleImage;
}
/**
* Returns a thumbnail of a source image. newSize
defines
* the length of the longest dimension of the thumbnail. The other
* dimension is then computed according to the dimensions ratio of the
* original picture.
This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.
* * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) * @see #createThumbnail(java.awt.image.BufferedImage, int) * @see #createThumbnail(java.awt.image.BufferedImage, int, int) * @param image the source image * @param newSize the length of the largest dimension of the thumbnail * @return a new compatibleBufferedImage
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 compatibleBufferedImage
containing a
* thumbnail of image
* @throws IllegalArgumentException if newWidth
is larger than
* the width of image
or if code>newHeight 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 compatibleBufferedImage
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 compatibleBufferedImage
containing a
* thumbnail of image
* @throws IllegalArgumentException if newWidth
is larger than
* the width of image
or if code>newHeight 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.
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.
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);
}
}
}
/*
* $Id: ColorTintFilter.java,v 1.1 2007/01/28 01:45:30 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 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 pixelcS
: color of the source pixelcM
: the solid color to mix with the source imagemixValue
: strength of the mix, a value between 0.0 and 1.0Creates a new color mixer filter. The specified color will be used
* to tint the source image, with a mixing strength defined by
* mixValue
.
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) { dst = createCompatibleDestImage(src, null); } int width = src.getWidth(); int height = src.getHeight(); int[] pixels = new int[width * height]; GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels); mixColor(pixels); GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels); return dst; } private void mixColor(int[] pixels) { 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: AbstractFilter.java,v 1.1 2007/01/28 01:45:27 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 GuyProvides an abstract implementation of the BufferedImageOp
* interface. This class can be used to created new image filters based
* on BufferedImageOp
.