下面列出了java.awt.image.BufferedImage#getTransparency() 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
public cfData execute( cfSession _session, cfArgStructData argStruct ) throws cfmRunTimeException{
cfImageData im = getImage( _session, argStruct );
int thickness = getNamedIntParam(argStruct, "thickness", 0 );
if ( thickness == 0 )
throwException(_session, "please specify a thickness greater than zero (0)" );
String bordertype = getNamedStringParam(argStruct, "type", "constant" ).trim();
if ( !bordertype.equals("constant") && !bordertype.equals("zero") ) {
throwException(_session, "type must be 'constant' or 'zero'" );
}
Color color;
if ( bordertype.equals("zero") ){
color = Color.black;
}else{
color = colour.getColor( getNamedStringParam(argStruct, "color", "black") );
}
int targetWidth = im.getWidth() + ( thickness*2 );
int targetHeight = im.getHeight() + ( thickness*2 );
BufferedImage img = im.getImage();
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage tmp = new BufferedImage(targetWidth, targetHeight, type);
Graphics2D g2 = tmp.createGraphics();
g2.setBackground(color);
g2.clearRect(0, 0, targetWidth, targetHeight );
g2.translate(thickness, thickness);
g2.drawRenderedImage(img, null);
g2.dispose();
im.setImage( tmp );
return cfBooleanData.TRUE;
}
public static BufferedImage cloneImageRegion(BufferedImage srcImg, int x, int y, int width, int height) {
int imgType = (srcImg.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB;
BufferedImage newImage = new BufferedImage(width, height, imgType);
Graphics2D g2 = newImage.createGraphics();
g2.drawImage(srcImg.getSubimage(x, y, width, height), 0, 0, null);
g2.dispose();
return newImage;
}
/**
* This method returns a scaled instance of the provided {@code BufferedImage}. The image is scaled so
* that its maximum dimension becomes {@link #targetSize} pixels.
*
* @param img
* the original image to be scaled
* @return a scaled version of the original {@code BufferedImage} or the original image if no scaling was
* applied
*/
public BufferedImage rectScaling(BufferedImage img) {
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB;
// get dimensions of original image
int originalWidth = img.getWidth();
int originalHeight = img.getHeight();
int maxDimension = Math.max(originalWidth, originalHeight);
// no processing is required
if (maxDimension <= targetSize) {
return img;
}
// scaling is required
BufferedImage ret = (BufferedImage) img;
double scalingRatio = (double) targetSize / maxDimension;
int targetWidth = (int) (originalWidth * scalingRatio);
int targetHeight = (int) (originalHeight * scalingRatio);
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = originalWidth;
h = originalHeight;
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
// long start = System.currentTimeMillis();
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
// long end = System.currentTimeMillis();
g2.dispose();
// System.out.println(end - start);
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
/**
* Used to copy a {@link BufferedImage} from a non-optimal type into a new
* {@link BufferedImage} instance of an optimal type (RGB or ARGB). If
* <code>src</code> is already of an optimal type, then it is returned
* unmodified.
* <p/>
* This method is meant to be used by any calling code (imgscalr's or
* otherwise) to convert any inbound image from a poorly supported image
* type into the 2 most well-supported image types in Java2D (
* {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
* ) in order to ensure all subsequent graphics operations are performed as
* efficiently and correctly as possible.
* <p/>
* When using Java2D to work with image types that are not well supported,
* the results can be anything from exceptions bubbling up from the depths
* of Java2D to images being completely corrupted and just returned as solid
* black.
*
* @param src
* The image to copy (if necessary) into an optimally typed
* {@link BufferedImage}.
*
* @return a representation of the <code>src</code> image in an optimally
* typed {@link BufferedImage}, otherwise <code>src</code> if it was
* already of an optimal type.
*
* @throws IllegalArgumentException
* if <code>src</code> is <code>null</code>.
*/
protected static BufferedImage copyToOptimalImage(BufferedImage src) throws IllegalArgumentException {
if (src == null)
throw new IllegalArgumentException("src cannot be null");
// Calculate the type depending on the presence of alpha.
int type =
(src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
BufferedImage result = new BufferedImage(src.getWidth(), src.getHeight(), type);
// Render the src image into our new optimal source.
Graphics g = result.getGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
return result;
}
/**
* Adapted from http://stackoverflow.com/questions/7951290/re-sizing-an-image-without-losing-quality
* @param img The Image to scale
* @param targetWidth target width
* @param targetHeight target height
* @return A scaled image copy of the original image.
* @throws IllegalArgumentException
*/
private static BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight) {
if (targetWidth < 0) {
throw new IllegalArgumentException(String.format("Negative target sizes not allowed: %d", targetWidth));
}
if (targetHeight < 0) {
throw new IllegalArgumentException(String.format("Negative target sizes not allowed: %d", targetHeight));
}
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = img;
int w;
int h;
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
int breakLoop = 0;
do {
breakLoop++;
w = reduce(w, targetWidth);
h = reduce(h, targetHeight);
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
if(breakLoop > 20) {
// damn infinite loop...
return null;
}
} while ((w != targetWidth) || (h != targetHeight));
return ret;
}
/**
* Convenience method that returns a scaled instance of the
* provided {@code BufferedImage}.
*
* @param img the original image to be scaled
* @param targetWidth the desired width of the scaled instance,
* in pixels
* @param targetHeight the desired height of the scaled instance,
* in pixels
* @param hint one of the rendering hints that corresponds to
* {@code RenderingHints.KEY_INTERPOLATION} (e.g.
* {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
* {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
* {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
* @param higherQuality if true, this method will use a multi-step
* scaling technique that provides higher quality than the usual
* one-step technique (only useful in down-scaling cases, where
* {@code targetWidth} or {@code targetHeight} is
* smaller than the original dimensions, and generally only when
* the {@code BILINEAR} hint is specified)
* @return a scaled version of the original {@code BufferedImage}
*/
// From https://today.java.net/article/2007/03/30/perils-imagegetscaledinstance
public static BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = img;
int w = img.getWidth(), h = img.getHeight();
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
if(w < targetWidth || h < targetHeight || !higherQuality)
return scaleImage(ret, type, hint, targetWidth, targetHeight);
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
do {
if (w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
ret = scaleImage(ret, type, hint, w, h);
} while (w != targetWidth || h != targetHeight);
return ret;
}
private static BufferedImage scaleToSize(BufferedImage img, int targetWidth, int targetHeight, boolean higherQuality) {
if (targetWidth == img.getWidth() && targetHeight == img.getHeight()) {
return img;
}
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
/**
* Used to copy a {@link BufferedImage} from a non-optimal type into a new
* {@link BufferedImage} instance of an optimal type (RGB or ARGB). If
* <code>src</code> is already of an optimal type, then it is returned
* unmodified.
* <p/>
* This method is meant to be used by any calling code (imgscalr's or
* otherwise) to convert any inbound image from a poorly supported image
* type into the 2 most well-supported image types in Java2D (
* {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
* ) in order to ensure all subsequent graphics operations are performed as
* efficiently and correctly as possible.
* <p/>
* When using Java2D to work with image types that are not well supported,
* the results can be anything from exceptions bubbling up from the depths
* of Java2D to images being completely corrupted and just returned as solid
* black.
*
* @param src
* The image to copy (if necessary) into an optimally typed
* {@link BufferedImage}.
*
* @return a representation of the <code>src</code> image in an optimally
* typed {@link BufferedImage}, otherwise <code>src</code> if it was
* already of an optimal type.
*
* @throws IllegalArgumentException
* if <code>src</code> is <code>null</code>.
*/
protected static BufferedImage copyToOptimalImage(BufferedImage src) throws IllegalArgumentException {
if (src == null)
throw new IllegalArgumentException("src cannot be null");
// Calculate the type depending on the presence of alpha.
int type = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB);
BufferedImage result = new BufferedImage(src.getWidth(), src.getHeight(), type);
// Render the src image into our new optimal source.
Graphics g = result.getGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
return result;
}
private static PDImageXObject createFromRGBImage(BufferedImage image, PDDocument document) throws IOException
{
int height = image.getHeight();
int width = image.getWidth();
int[] rgbLineBuffer = new int[width];
int bpc = 8;
PDDeviceColorSpace deviceColorSpace = PDDeviceRGB.INSTANCE;
byte[] imageData = new byte[width * height * 3];
int byteIdx = 0;
int alphaByteIdx = 0;
int alphaBitPos = 7;
int transparency = image.getTransparency();
int apbc = transparency == Transparency.BITMASK ? 1 : 8;
byte[] alphaImageData;
if (transparency != Transparency.OPAQUE)
{
alphaImageData = new byte[((width * apbc / 8) + (width * apbc % 8 != 0 ? 1 : 0)) * height];
}
else
{
alphaImageData = new byte[0];
}
for (int y = 0; y < height; ++y)
{
for (int pixel : image.getRGB(0, y, width, 1, rgbLineBuffer, 0, width))
{
imageData[byteIdx++] = (byte) ((pixel >> 16) & 0xFF);
imageData[byteIdx++] = (byte) ((pixel >> 8) & 0xFF);
imageData[byteIdx++] = (byte) (pixel & 0xFF);
if (transparency != Transparency.OPAQUE)
{
// we have the alpha right here, so no need to do it separately
// as done prior April 2018
if (transparency == Transparency.BITMASK)
{
// write a bit
alphaImageData[alphaByteIdx] |= ((pixel >> 24) & 1) << alphaBitPos;
if (--alphaBitPos < 0)
{
alphaBitPos = 7;
++alphaByteIdx;
}
}
else
{
// write a byte
alphaImageData[alphaByteIdx++] = (byte) ((pixel >> 24) & 0xFF);
}
}
}
// skip boundary if needed
if (transparency == Transparency.BITMASK && alphaBitPos != 7)
{
alphaBitPos = 7;
++alphaByteIdx;
}
}
PDImageXObject pdImage = prepareImageXObject(document, imageData,
image.getWidth(), image.getHeight(), bpc, deviceColorSpace);
if (transparency != Transparency.OPAQUE)
{
PDImageXObject pdMask = prepareImageXObject(document, alphaImageData,
image.getWidth(), image.getHeight(), apbc, PDDeviceGray.INSTANCE);
pdImage.getCOSObject().setItem(COSName.SMASK, pdMask);
}
return pdImage;
}
private BufferedImage scaleToSize(BufferedImage img, int targetWidth, int targetHeight, Object interpolation) {
if (targetWidth == img.getWidth() && targetHeight == img.getHeight()) {
return img;
}
boolean higherQuality = (
// Set flag to use multi-step technique only if the
// target size is less than 50% of the original size
// and the interpolation mode is bilinear or bicubic
(targetWidth < (int)(img.getWidth() * 0.5)) &&
(
(interpolation == RenderingHints.VALUE_INTERPOLATION_BILINEAR) ||
(interpolation == RenderingHints.VALUE_INTERPOLATION_BICUBIC)
)
);
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, interpolation );
g2.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
/**
* Convenience method that returns a scaled instance of the
* provided {@code BufferedImage}.
*
* @param img the original image to be scaled
* @param targetWidth the desired width of the scaled instance,
* in pixels
* @param targetHeight the desired height of the scaled instance,
* in pixels
* @param hint one of the rendering hints that corresponds to
* {@code RenderingHints.KEY_INTERPOLATION} (e.g.
* {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
* {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
* {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
* @param higherQuality if true, this method will use a multi-step
* scaling technique that provides higher quality than the usual
* one-step technique (only useful in down scaling cases, where
* {@code targetWidth} or {@code targetHeight} is
* smaller than the original dimensions, and generally only when
* the {@code BILINEAR} hint is specified)
* @return a scaled version of the original {@code BufferedImage}
*/
public BufferedImage getScaledInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint,
boolean higherQuality)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = img;
int w;
int h;
if (higherQuality)
{
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
}
else
{
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
// If we are scaling up, just do the one pass.
if (w < targetWidth || h < targetWidth)
{
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do
{
if (higherQuality && w > targetWidth)
{
w /= 2;
if (w < targetWidth)
{
w = targetWidth;
}
}
if (higherQuality && h > targetHeight)
{
h /= 2;
if (h < targetHeight)
{
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
}
while (w != targetWidth || h != targetHeight);
return ret;
}
/**
* Used to copy a {@link BufferedImage} from a non-optimal type into a new
* {@link BufferedImage} instance of an optimal type (RGB or ARGB). If
* <code>src</code> is already of an optimal type, then it is returned
* unmodified.
* <p>
* This method is meant to be used by any calling code (imgscalr's or
* otherwise) to convert any inbound image from a poorly supported image
* type into the 2 most well-supported image types in Java2D (
* {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
* ) in order to ensure all subsequent graphics operations are performed as
* efficiently and correctly as possible.
* <p>
* When using Java2D to work with image types that are not well supported,
* the results can be anything from exceptions bubbling up from the depths
* of Java2D to images being completely corrupted and just returned as solid
* black.
*
* @param src The image to copy (if necessary) into an optimally typed
* {@link BufferedImage}.
* @return a representation of the <code>src</code> image in an optimally
* typed {@link BufferedImage}, otherwise <code>src</code> if it was
* already of an optimal type.
* @throws IllegalArgumentException if <code>src</code> is <code>null</code>.
*/
protected static BufferedImage copyToOptimalImage(BufferedImage src)
throws IllegalArgumentException {
if (src == null)
throw new IllegalArgumentException("src cannot be null");
// Calculate the type depending on the presence of alpha.
int type = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB);
BufferedImage result = new BufferedImage(src.getWidth(),
src.getHeight(), type);
// Render the src image into our new optimal source.
Graphics g = result.getGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
return result;
}
/**
* <p>Returns a thumbnail of a source image.</p>
* <p>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.</p>
*
* @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 <code>BufferedImage</code> containing a
* thumbnail of <code>image</code>
* @throws IllegalArgumentException if <code>newWidth</code> is larger than
* the width of <code>image</code> or if code>newHeight</code> is larger
* than the height of <code>image or if one the dimensions is not > 0</code>
*/
public static BufferedImage createThumbnail(BufferedImage image,
int newWidth, int newHeight) {
int width = image.getWidth();
int height = image.getHeight();
boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE;
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;
BufferedImage temp = null;
Graphics2D g2 = null;
try {
int previousWidth = width;
int previousHeight = height;
do {
if (width > newWidth) {
width /= 2;
if (width < newWidth) {
width = newWidth;
}
}
if (height > newHeight) {
height /= 2;
if (height < newHeight) {
height = newHeight;
}
}
if (temp == null || isTranslucent) {
if (g2 != null) {
//do not need to wrap with finally
//outer finally block will ensure
//that resources are properly reclaimed
g2.dispose();
}
temp = createCompatibleImage(image, width, height);
g2 = temp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
}
g2.drawImage(thumb, 0, 0, width, height,
0, 0, previousWidth, previousHeight, null);
previousWidth = width;
previousHeight = height;
thumb = temp;
} while (width != newWidth || height != newHeight);
} finally {
g2.dispose();
}
if (width != thumb.getWidth() || height != thumb.getHeight()) {
temp = createCompatibleImage(image, width, height);
g2 = temp.createGraphics();
try {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null);
} finally {
g2.dispose();
}
thumb = temp;
}
return thumb;
}
/**
* Used to copy a {@link BufferedImage} from a non-optimal type into a new
* {@link BufferedImage} instance of an optimal type (RGB or ARGB). If
* <code>src</code> is already of an optimal type, then it is returned
* unmodified.
* <p/>
* This method is meant to be used by any calling code (imgscalr's or
* otherwise) to convert any inbound image from a poorly supported image
* type into the 2 most well-supported image types in Java2D (
* {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
* ) in order to ensure all subsequent graphics operations are performed as
* efficiently and correctly as possible.
* <p/>
* When using Java2D to work with image types that are not well supported,
* the results can be anything from exceptions bubbling up from the depths
* of Java2D to images being completely corrupted and just returned as solid
* black.
*
* @param src
* The image to copy (if necessary) into an optimally typed
* {@link BufferedImage}.
*
* @return a representation of the <code>src</code> image in an optimally
* typed {@link BufferedImage}, otherwise <code>src</code> if it was
* already of an optimal type.
*
* @throws IllegalArgumentException
* if <code>src</code> is <code>null</code>.
*/
protected static BufferedImage copyToOptimalImage(BufferedImage src)
throws IllegalArgumentException {
if (src == null)
throw new IllegalArgumentException("src cannot be null");
// Calculate the type depending on the presence of alpha.
int type = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB);
BufferedImage result = new BufferedImage(src.getWidth(),
src.getHeight(), type);
// Render the src image into our new optimal source.
Graphics g = result.getGraphics();
g.drawImage(src, 0, 0, null);
g.dispose();
return result;
}
/**
* Convenience method that returns a scaled instance of the
* provided {@code BufferedImage}. NEEDS WORK.
* Alternate option is getScaledInstance off of the BufferedImage object.
*
* @param srcImage The image to be scaled.
* @param targetWidth the desired width of the scaled instance,
* in pixels
* @param targetHeight the desired height of the scaled instance,
* in pixels
* @param hint one of the rendering hints that corresponds to
* {@code RenderingHints.KEY_INTERPOLATION} (e.g.
* {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
* {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
* {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
* @param higherQuality if true, this method will use a multi-step
* scaling technique that provides higher quality than the usual
* one-step technique (only useful in downscaling cases, where
* {@code targetWidth} or {@code targetHeight} is
* smaller than the original dimensions, and generally only when
* the {@code BILINEAR} hint is specified). NEEDS WORK.
* @param keepProportion Don't stretch the original image to fit into the
* target height/width.
* @return a scaled version of the original {@code BufferedImage}
*
*/
public static BufferedImage getScaledInstance(BufferedImage srcImage,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality,
boolean keepProportion)
{
int type = (srcImage.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)srcImage;
int w, h;
//keep things proportioned///////////
double ratio = 0;
double SrcW = srcImage.getWidth();
double SrcH = srcImage.getHeight();
double tW = targetWidth;
double tH = targetHeight;
if(keepProportion)
{
ratio = Math.min((tH / SrcH), (tW / SrcW));
//ratio = (SrcW / tW);
targetWidth = (int)((SrcW) * ratio);
targetHeight = (int)((SrcH) * ratio);
}///////////////////////////////////
if (higherQuality)
{
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = srcImage.getWidth();
h = srcImage.getHeight();
}
else
{
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do
{
if (higherQuality && w > targetWidth)
{
w /= 2;
if (w < targetWidth)
{
w = targetWidth;
}
}
if (higherQuality && h > targetHeight)
{
h /= 2;
if (h < targetHeight)
{
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
/**
* This method returns a scaled instance of the provided {@code BufferedImage}. The image is scaled to a
* maximum of {@link #targetSize} pixels in total by keeping the aspect ratio.
*
* @param img
* the original image to be scaled
* @return a scaled version of the original {@code BufferedImage} or the original image if no scaling was
* applied
*/
public BufferedImage maxPixelsScaling(BufferedImage img) {
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB;
// get dimensions of original image
int originalWidth = img.getWidth();
int originalHeight = img.getHeight();
long originalSize = originalWidth * originalHeight;
if (originalSize <= targetSize) {
return img;
}
double scalingRatio = (double) targetSize / originalSize;
// scaling ratio per dimension
scalingRatio = Math.sqrt(scalingRatio);
int targetWidth = (int) (originalWidth * scalingRatio);
int targetHeight = (int) (originalHeight * scalingRatio);
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
// long start = System.currentTimeMillis();
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
// long end = System.currentTimeMillis();
g2.dispose();
// System.out.println(end - start);
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
private static PDImageXObject createFromRGBImage(BufferedImage image) throws IOException
{
int height = image.getHeight();
int width = image.getWidth();
int[] rgbLineBuffer = new int[width];
int bpc = 8;
PDDeviceColorSpace deviceColorSpace = PDDeviceRGB.INSTANCE;
byte[] imageData = new byte[width * height * 3];
int byteIdx = 0;
int alphaByteIdx = 0;
int alphaBitPos = 7;
int transparency = image.getTransparency();
int apbc = transparency == Transparency.BITMASK ? 1 : 8;
byte[] alphaImageData;
if (transparency != Transparency.OPAQUE)
{
alphaImageData = new byte[((width * apbc / 8) + (width * apbc % 8 != 0 ? 1 : 0))
* height];
}
else
{
alphaImageData = new byte[0];
}
for (int y = 0; y < height; ++y)
{
for (int pixel : image.getRGB(0, y, width, 1, rgbLineBuffer, 0, width))
{
imageData[byteIdx++] = (byte) ((pixel >> 16) & 0xFF);
imageData[byteIdx++] = (byte) ((pixel >> 8) & 0xFF);
imageData[byteIdx++] = (byte) (pixel & 0xFF);
if (transparency != Transparency.OPAQUE)
{
// we have the alpha right here, so no need to do it separately
// as done prior April 2018
if (transparency == Transparency.BITMASK)
{
// write a bit
alphaImageData[alphaByteIdx] |= ((pixel >> 24) & 1) << alphaBitPos;
if (--alphaBitPos < 0)
{
alphaBitPos = 7;
++alphaByteIdx;
}
}
else
{
// write a byte
alphaImageData[alphaByteIdx++] = (byte) ((pixel >> 24) & 0xFF);
}
}
}
// skip boundary if needed
if (transparency == Transparency.BITMASK && alphaBitPos != 7)
{
alphaBitPos = 7;
++alphaByteIdx;
}
}
PDImageXObject pdImage = prepareImageXObject(imageData, image.getWidth(), image.getHeight(),
bpc, deviceColorSpace);
if (transparency != Transparency.OPAQUE)
{
PDImageXObject pdMask = prepareImageXObject(alphaImageData, image.getWidth(),
image.getHeight(), apbc, PDDeviceGray.INSTANCE);
pdImage.getCOSObject().setItem(COSName.SMASK, pdMask);
}
return pdImage;
}
/**
* Convenience method that returns a scaled instance of the provided
* {@code BufferedImage}.
* https://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
*
* @param img the original image to be scaled
* @param targetWidth the desired width of the scaled instance, in pixels
* @param targetHeight the desired height of the scaled instance, in pixels
* @param hint one of the rendering hints that corresponds to
* {@code RenderingHints.KEY_INTERPOLATION} (e.g.
* {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
* {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
* {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
* @param higherQuality if true, this method will use a multi-step scaling
* technique that provides higher quality than the usual one-step technique
* (only useful in downscaling cases, where {@code targetWidth} or
* {@code targetHeight} is smaller than the original dimensions, and
* generally only when the {@code BILINEAR} hint is specified)
* @return a scaled version of the original {@code BufferedImage}
*/
public static BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality /*&& (img.getWidth() > targetWidth || img.getHeight() > targetHeight)*/) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality) {
if (w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
} else {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
} else {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
/**
* Used to create a {@link BufferedImage} with the given dimensions and the
* most optimal RGB TYPE ( {@link BufferedImage#TYPE_INT_RGB} or
* {@link BufferedImage#TYPE_INT_ARGB} ) capable of being rendered into from
* the given <code>src</code>.
* <p/>
* This does not perform a copy of the image data from <code>src</code> into
* the result image; see {@link #copyToOptimalImage(BufferedImage)} for
* that.
* <p/>
* We force all rendering results into one of these two types, avoiding the
* case where a source image is of an unsupported (or poorly supported)
* format by Java2D causing the rendering result to end up looking terrible
* (common with GIFs) or be totally corrupt (e.g. solid black image).
* <p/>
* Originally reported by Magnus Kvalheim from Movellas when scaling certain
* GIF and PNG images.
*
* @param src
* The source image that will be analyzed to determine the most
* optimal image type it can be rendered into.
* @param width
* The width of the newly created resulting image.
* @param height
* The height of the newly created resulting image.
*
* @return a new {@link BufferedImage} representing the most optimal target
* image type that <code>src</code> can be rendered into.
*
* @throws IllegalArgumentException
* if <code>width</code> or <code>height</code> are < 0.
*
* @see <a
* href="http://www.mail-archive.com/[email protected]/msg05621.html">How
* Java2D handles poorly supported image types</a>
* @see <a
* href="http://code.google.com/p/java-image-scaling/source/browse/trunk/src/main/java/com/mortennobel/imagescaling/MultiStepRescaleOp.java">Thanks
* to Morten Nobel for implementation hint</a>
*/
protected static BufferedImage createOptimalImage(BufferedImage src,
int width, int height) throws IllegalArgumentException {
if (width < 0 || height < 0)
throw new IllegalArgumentException("width [" + width
+ "] and height [" + height + "] must be >= 0");
return new BufferedImage(
width,
height,
(src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB));
}
/**
* Used to create a {@link BufferedImage} with the given dimensions and the
* most optimal RGB TYPE ( {@link BufferedImage#TYPE_INT_RGB} or
* {@link BufferedImage#TYPE_INT_ARGB} ) capable of being rendered into from
* the given <code>src</code>.
* <p>
* This does not perform a copy of the image data from <code>src</code> into
* the result image; see {@link #copyToOptimalImage(BufferedImage)} for
* that.
* <p>
* We force all rendering results into one of these two types, avoiding the
* case where a source image is of an unsupported (or poorly supported)
* format by Java2D causing the rendering result to end up looking terrible
* (common with GIFs) or be totally corrupt (e.g. solid black image).
* <p>
* Originally reported by Magnus Kvalheim from Movellas when scaling certain
* GIF and PNG images.
*
* @param src The source image that will be analyzed to determine the most
* optimal image type it can be rendered into.
* @param width The width of the newly created resulting image.
* @param height The height of the newly created resulting image.
* @return a new {@link BufferedImage} representing the most optimal target
* image type that <code>src</code> can be rendered into.
* @throws IllegalArgumentException if <code>width</code> or <code>height</code> are < 0.
* @see <a href="http://www.mail-archive.com/[email protected]com/msg05621.html">How Java2D handles poorly supported image types</a>
* @see <a href="http://code.google.com/p/java-image-scaling/source/browse/trunk/src/main/java/com/mortennobel/imagescaling/MultiStepRescaleOp.java">Thanks to Morten Nobel for implementation hint</a>
*/
protected static BufferedImage createOptimalImage(BufferedImage src,
int width, int height) throws IllegalArgumentException {
if (width <= 0 || height <= 0)
throw new IllegalArgumentException("width [" + width
+ "] and height [" + height + "] must be > 0");
return new BufferedImage(
width,
height,
(src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
: BufferedImage.TYPE_INT_ARGB));
}