I am a newbie to graphics. I've been using this code to make thumbnails of image files. When i use small files(~100KB) like wall papers, it works fine but when i use an image file(a photo) of size ~5MB, it produces just a few bytes(~1-8KB) of file which shows up as black image. It does not matter what Width and Height i give it. What could be going wrong here? Is it a difference between image types or the camera that produces the images? I'm sure the problem images are from a different camera than the non problematic ones. I am giving quality param as 100 to not miss out any detail that way...
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
int dx = thumbWidth, dy = thumbHeight;
Image image = Toolkit.getDefaultToolkit().createImage(file);
MediaTracker mediaTracker = new MediaTracker(new Container());
mediaTracker.addImage(image, 0);
mediaTracker.waitForID(0);
double thumbRatio = (double)thumbWidth / (double)thumbHeight;
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
double imageRatio = (double)imageWidth / (double)imageHeight;
if (thumbRatio < imageRatio) {
thumbHeight = (int)(thumbWidth / imageRatio);
} else {
thumbWidth = (int)(thumbHeight * imageRatio);
}
if(thumbWidth > dx) {
thumbWidth = dx;
thumbHeight = (int)(thumbWidth / imageRatio);
}
if(thumbHeight > dy)
{
thumbHeight = dy;
thumbWidth = (int) (thumbHeight*imageRatio);
}
log.debug("X="+thumbWidth+" Y="+thumbHeight);
BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = thumbImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);
quality = Math.max(0, Math.min(quality, 100));
param.setQuality((float)quality / 100.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(thumbImage);
log.debug("ThumbLength"+out.toByteArray().length);
FileOutputStream fos = new FileOutputStream("/root/testx.jpg");
fos.write(out.toByteArray());
fos.close();
} catch(Exception e) { log.debug(e.getMessage());}
return out.toByteArray();
You might try BufferedImage.TYPE_INT_ARGB, as shown here.
Also, your MediaTracker is waiting on the same thread; ImageIO.read() might be simpler.
Addendum: Also consider AffineTransformOp, although the src and dst must be different.
Related
I need some help concerning Writable- and BufferedImages in Java (11, Javafx):
How do I convert a WritabeImage to a BufferedImage? - WITHOUT using SwingFXUtils.fromFXImage()
It is important for me to find a solution without SwingFXUtils.fromFXImage(). I have already asked google, but the only results I am getting contain SwingFXUtils.fromFXImage(). So I am asking for your expertise.
Could you please help me?
Thanks!
Thanks for your help!
That is how my code looks like (including the 'SwingFXUtils' which I need to avoid):
static public void createImg(Node NODE, String name) {
WritableImage SNAPSHOT = NODE.snapshot(new SnapshotParameters(), null);
File file = new File(Properties.getSettingsPath() + File.separator + name + ".png");
try {
ImageIO.write(SwingFXUtils.fromFXImage(SNAPSHOT, null), "png", file);
} catch (IOException e) {
e.printStackTrace();
}
}
You can copy the pixels yourself:
BufferedImage convert(Image fxImage) {
int width = (int) Math.ceil(fxImage.getWidth());
int height = (int) Math.ceil(fxImage.getHeight());
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
int[] buffer = new int[width];
PixelReader reader = fxImage.getPixelReader();
WritablePixelFormat<IntBuffer> format =
PixelFormat.getIntArgbInstance();
for (int y = 0; y < height; y++) {
reader.getPixels(0, y, width, 1, format, buffer, 0, width);
image.getRaster().setDataElements(0, y, width, 1, buffer);
}
return image;
}
I'm converting an image to a pdf document by using iText.
I'm having trouble when I give the image a custom size that's bigger then the document itself.
Before I do doc.open(), I set the margins like this
doc.setMargins(marginLeft, marginRight, marginTop, marginBottom);
When I use my fit method, this works like a charm:
// scale
scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, bufferedImageType);
// instantiate
image = instantiateImage(awtImage, scaledAwtImage, scaledWidth, scaledHeight);
setAutoRotate(image);
// scaleWithMargins
float targetWidth = doc.getPageSize().getWidth() - mmToUnit(Float.parseFloat(marginLeft) + Float.parseFloat(marginRight));
float targetHeight = doc.getPageSize().getHeight() - mmToUnit(Float.parseFloat(marginBottom) + Float.parseFloat(marginTop));
image.scaleToFit(targetWidth, targetHeight);
Probably because of image.scaleToFit(). The following is the code I'm having trouble with. As you can see the right margin will get overwritten by the image. I can't use image.scaleToFit() here because I want to keep the custom sizes. Anyone got an idea?
// printWidth
if(printWidth != null && !printWidth.isEmpty() ){
scaledWidth = (int) mmToUnit(printWidth);
}
// printHeight
if(printHeight != null && !printHeight.isEmpty() ){
scaledHeight = (int) mmToUnit(printHeight);
}
scaledAwtImage = new BufferedImage(scaledWidth, scaledHeight, bufferedImageType);
//instantiate
image = instantiateImage(awtImage,scaledAwtImage, scaledWidth, scaledHeight);
EDIT
Forgot to post my instantiateImage() method for more information.
private Image instantiateImage(BufferedImage awtImage, BufferedImage scaledAwtImage, int scaledWidth, int scaledHeight) throws IOException, BadElementException {
Graphics2D g = scaledAwtImage.createGraphics();
g.drawImage(awtImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ImageIO.write(scaledAwtImage, "jpeg", bout);
byte[] imageBytes = bout.toByteArray();
Image image = Image.getInstance(imageBytes);
return image;
}
I am working a method to resize featured image and I have finished it, but after testing seems something isn't right cause the image is deformed and don't look nice at all, I am looking very carefully, but I don't get to see what's the problem, I would really appreciate if you have any idea what is wrong or a better way to solve this. Have a look please, I would really need some better advice and thanks in advance!!
private byte[] resizeFeatureImage(MultipartFile featureImage)
{
try
{
BufferedImage originalImage = ImageIO.read(featureImage.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
double featImageWidth = originalImage.getWidth();
double featImageHeight = originalImage.getHeight();
if (featImageHeight > MAX_FEAT_IMAGE_HEIGHT || featImageWidth > MAX_FEAT_IMAGE_WIDTH)
{
// Sanity check on the input (division by zero, infinity):
if (featImageWidth <= 1 || featImageHeight <= 1)
{
throw new IllegalArgumentException("Cannot do image resize for " + featureImage);
}
// The scaling factors to reach to max on WIDTH and HEIGHT:
double xScale = MAX_FEAT_IMAGE_WIDTH / featImageWidth;
double yScale = MAX_FEAT_IMAGE_HEIGHT / featImageHeight;
// Proportional (scale WIDTH and HEIGHT by the same factor):
double scale = Math.min(xScale, yScale);
// (Possibly) Do not enlarge:
scale = Math.min(1.0, scale);
int finalWidth = Math.min((int) Math.round(scale * featImageWidth), MAX_FEAT_IMAGE_WIDTH);
int finalHeight = Math.min((int) Math.round(scale * featImageHeight), MAX_FEAT_IMAGE_HEIGHT);
double ratio = featImageWidth / featImageHeight;
// WIDTH is bigger then HEIGHT
if (ratio > 1)
{
finalWidth = MAX_FEAT_IMAGE_WIDTH;
finalHeight = (int) Math.round(MAX_FEAT_IMAGE_HEIGHT / ratio);
}
// HEIGHT is bigger then WIDTH
else if (ratio < 1)
{
finalWidth = (int) Math.round(MAX_FEAT_IMAGE_WIDTH / ratio);
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
}
// WIDTH and HEIGHT are equal
else
{
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
finalWidth = MAX_FEAT_IMAGE_WIDTH;
}
logger.info("[resizeFeatureImage] [FEATURE IMAGE RESIZE] Starting to resize feature Image");
Graphics2D g2d;
BufferedImage resizedImage;
if (featureImage.getContentType().contains("png"))
{
resizedImage = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_INT_ARGB);
}
else
{
resizedImage = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_3BYTE_BGR);
}
g2d = resizedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(ImageIO.read(featureImage.getInputStream()), 0, 0, finalWidth, finalHeight,
null);
g2d.dispose();
ImageIO.write(resizedImage, featureImage.getContentType().split("/")[1], baos);
logger.info("[resizeFeatureImage] [FEATURE IMAGE RESIZE] Feature image resized!");
return baos.toByteArray();
}
else
{
ImageIO.write(originalImage, featureImage.getContentType().split("/")[1], baos);
return baos.toByteArray();
}
}
catch (Exception e)
{
logger.warn("[resizeFeatureImage] [STATUS] - ERROR ");
logger.warn("[resizeFeatureImage] [EXCEPTION] " + e.getMessage(), e);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"The file you uploaded can be damaged or has incorrect encoding.");
}
}
When you scale, your image needs to retain the same ratio.
double featImageWidth = originalImage.getWidth();
double featImageHeight = originalImage.getHeight();
double ratio = featImageWidth/featImageHeight;
//this width meets your constraints
int finalWidth = MAX_FEAT_IMAGE_WIDTH;
//this final height is what the height would be to keep the same ratio.
int finalHeight = (int)(finalWidth/ratio);
if(finalHeight > MAX_FEAT_IMAGE_HEIGHT){
//the height constrains the image.
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
finalWidth = (int)(finalHeight*ratio)
}
I have an image with transparent background. I'd like to rotate this image to a specific angle and keep the transparent background for the resulting image. For this purpose I use the following method:
public static BufferedImage rotateImage(BufferedImage image, double angle, Color backgroundColor) {
System.out.println(image.getType());
double theta = Math.toRadians(angle);
double sin = Math.abs(Math.sin(theta));
double cos = Math.abs(Math.cos(theta));
int w = image.getWidth();
int h = image.getHeight();
int newW = (int) Math.floor(w * cos + h * sin);
int newH = (int) Math.floor(h * cos + w * sin);
BufferedImage tmp = new BufferedImage(newW, newH, image.getType());
Graphics2D g2d = tmp.createGraphics();
if (backgroundColor != null) {
g2d.setColor(backgroundColor);
g2d.fillRect(0, 0, newW, newH);
}
g2d.fillRect(0, 0, newW, newH);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.translate((newW - w) / 2, (newH - h) / 2);
g2d.rotate(theta, w / 2, h / 2);
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return tmp;
}
I invoke it with background=null:
BufferedImage image = ImageIO.read(file);
rotateImage(image, 4, null);
ImageIO.write(bi, "PNG", new File("image.png"));
but the background of the resulting image.png is WHITE. What am I doing wrong and how to properly keep the transparent background for image.png?
I'm a bit puzzled about the behavior of Graphics.drawImage(). Maybe somebody else can comment about it.
However, Graphics2D.drawRenderedImage() works a treat. It takes an AffineTransform to control the rotation. The below example nicely works. You probably have additional requirement about the final image size and the location of the rotated image.
import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
public class ImageRotation {
public static void main(String[] args) {
ImageRotation rotation = new ImageRotation();
rotation.rotate("input.png", 45, "output.png");
}
public void rotate(String inputImageFilename, double angle, String outputImageFilename) {
try {
BufferedImage inputImage = ImageIO.read(new File(inputImageFilename));
BufferedImage outputImage = rotateImage(inputImage, angle);
ImageIO.write(outputImage, "PNG", new File(outputImageFilename));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private BufferedImage rotateImage(BufferedImage sourceImage, double angle) {
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
BufferedImage destImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = destImage.createGraphics();
AffineTransform transform = new AffineTransform();
transform.rotate(angle / 180 * Math.PI, width / 2 , height / 2);
g2d.drawRenderedImage(sourceImage, transform);
g2d.dispose();
return destImage;
}
}
Update
While the above code works for most PNGs, it does not work for the image that alexanoid is using. I've analyzed the image:
It's a grayscale image without a color palette (PNG color type 0) .
It uses simple transparency with a 2 byte long tRNS chunk.
As far as I can tell that's perfectly legal. However, ImageIO does not implement this combination. If the image has no palette, it simply ignores the tRNS chunk and therefore ignores the transparency information. That's most likely a bug.
You basically have two options now:
Look for an alternative library to read PNG files.
Fix the transparency after you have read the PNG file. This only works if know that the image used the particular problematic format.
Input and output for working PNG files
Input image:
Ouptput Image:
I tried the code below to generate a thumbnail.
I am able to get the thumbnail but the quality is not there. Please can any one help me in this one to generate a high quality thumbnail? The original image is high quality.
BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = thumbImage.createGraphics();
graphics2D.setBackground(Color.WHITE);
graphics2D.setPaint(Color.WHITE);
graphics2D.fillRect(0, 0, thumbWidth, thumbHeight);
graphics2D.setComposite(AlphaComposite.Src);
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
graphics2D.dispose();
File file = new File(thumbnailFile);
if (javax.imageio.ImageIO.write(thumbImage, "JPG", file))
return file;
You might want to take a look at this: http://download.oracle.com/javase/tutorial/uiswing/components/icon.html
http://download.oracle.com/javase/tutorial/uiswing/examples/components/IconDemoProject/src/components/IconDemoApp.java
I used that as a reference for doing something similar before.
I had the same problem and found this great article with sample code and sample images at the end:
http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
check this I found best jar file here
public static javaxt.io.Image resizeThumbnailImage(javaxt.io.Image image, int width, int height) {
Integer imgWidth = image.getWidth();
Integer imgHeight = image.getHeight();
Double imgRatio = (imgWidth.doubleValue() / imgHeight.doubleValue());
logger.info("\n======= imgRatio " + imgRatio);
if (imgRatio >= 2) {
image.setWidth(width - 1);
} else if (imgRatio < 1) {
image.setHeight(300);
} else {
Double expectedHeight = (imgRatio * (height / ProjectConstant.THUMBNAIL_IMG_ASPECT_RATIO));
image.setHeight(expectedHeight.intValue());
if (image.getWidth() > width) {
image.setWidth(width - 20);
}
}
logger.info("=======after Processing image Width " + image.getWidth()+" Hight "+image.getHeight());
return image;
}
my constant
public static final double THUMBNAIL_IMG_ASPECT_RATIO = 1.4;