BufferedImage img = ImageIO.read(new File(paramString));
double locationX = img.getWidth(this) / 2;
double locationY = img.getHeight(this) / 2;
AffineTransform tx = AffineTransform.getRotateInstance(Math.toRadians(paramInt3), locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
BufferedImage bimg = new BufferedImage (img.getWidth(this), img.getWidth(this), BufferedImage.TYPE_BYTE_INDEXED);
bimg = op.filter (img, null);
ImageIcon localImageIcon = new ImageIcon (bimg);
Why might the side be cut off?
I'm using a library that requires the end result to be a ImageIcon.
Your destination image is created here:
BufferedImage bimg = new BufferedImage (img.getWidth(this), img.getWidth(this), BufferedImage.TYPE_BYTE_INDEXED);
It's a square! If your source image is not a square you should create a destination image like this:
BufferedImage bimg = new BufferedImage (img.getHeight(this), img.getWidth(this), BufferedImage.TYPE_BYTE_INDEXED);
Maybe it's just a rounding problem:
double locationX = img.getWidth(this) / 2;
double locationY = img.getHeight(this) / 2;
Width and height are integers, if they are not even you might get rounding issues. Try this:
double locationX = ((double)img.getWidth(this)) / 2;
double locationY = ((double)img.getHeight(this)) / 2;
Related
I cannot seem to figure out how to draw a transparent and rotated image. I need to be able to draw an image that is transparent and rotated to a certain degree.
I tried this code:
// draws an image that is rotated to a certain degree
public static void drawRotatedImage(BufferedImage image_, int x, int y, int degrees, float scale) {
// graphics used for the utilities of drawing the image (processing)
Graphics2D utilGraphics;
// make rectangular image
int radius = (int) Math.sqrt(image_.getWidth() * image_.getWidth() + image_.getHeight() * image_.getHeight());
BufferedImage image1 = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
utilGraphics = image1.createGraphics();
// centers image
utilGraphics.drawImage(image_, image1.getWidth() / 2 - image_.getWidth() / 2, image1.getHeight() / 2 - image_.getHeight() / 2, null);
// scale image
int nw = (int) (image1.getWidth() * scale);
int nh = (int) (image1.getHeight() * scale);
BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
utilGraphics.drawImage(image1, 0, 0, nw, nh, null);
// Rotation information
double rotationRequired = Math.toRadians (degrees);
double locationX = image.getWidth() / 2;
double locationY = image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
ImageProducer filteredImgProd = new FilteredImageSource(op.filter(image, null).getSource(), filter);
Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
// Drawing the rotated image at the required drawing locations
g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null);
}
The filter variable is defined as:
private static final ImageFilter filter = new RGBImageFilter() {
int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;
public final int filterRGB(int x, int y, int rgb) {
if ((rgb | 0x0000ffcc) == transparentColor) {
return 0x0000ffcc & rgb;
} else {
return rgb;
}
}
};
This ...
BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
centeredGraphics.drawImage(image1, 0, 0, nw, nh, null);
You're creating a new BufferedImage (image), but you never actually paint anything to it, instead, you paint image1 to it's own Graphics context.
Now, if you wanted a transparent image, you should have used...
BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
instead of...
BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
And I never used g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null); as it just doesn't make sense to me (transparentImg is already an Image 🤷♂️)
Now, having said all that, I would "suggest" you take each step individually, start by scaling the original image using something like Java: maintaining aspect ratio of JPanel background image and the rotate the image using something like Rotate a buffered image in Java (which will generate a image large enough to contain the rotated image)
Also, if you "create" a Graphics context, you should also dispose of it when you no longer need it, otherwise you could end up with a memory leak.
"Fixed" code...
Just to be clear, I would still recommend sing ARGB instead of RGB for centeredImage as your filter workflow never seemed to work for, but I started with a transparent image anyway
public Image rotateAndScaleImage(BufferedImage originalImage, int degrees, float scale) {
// make rectangular image
int radius = (int) Math.sqrt(originalImage.getWidth() * originalImage.getWidth() + originalImage.getHeight() * originalImage.getHeight());
BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = centeredImage.createGraphics();
// centers image
int xPos = (centeredImage.getWidth() - originalImage.getWidth()) / 2;
int yPos = (centeredImage.getHeight() - originalImage.getHeight()) / 2;
graphics.drawImage(originalImage, xPos, yPos, null);
graphics.dispose();
// scale image
int nw = (int) (centeredImage.getWidth() * scale);
int nh = (int) (centeredImage.getHeight() * scale);
BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
graphics = image.createGraphics();
// No scaling is done ???
graphics.drawImage(centeredImage, 0, 0, nw, nh, null);
// Rotation information
double rotationRequired = Math.toRadians(degrees);
double locationX = centeredImage.getWidth() / 2;
double locationY = centeredImage.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
ImageProducer filteredImgProd = new FilteredImageSource(op.filter(centeredImage, null).getSource(), filter);
Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
return transparentImg;
}
private static final ImageFilter filter = new RGBImageFilter() {
int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;
public final int filterRGB(int x, int y, int rgb) {
if ((rgb | 0x0000ffcc) == transparentColor) {
return 0x0000ffcc & rgb;
} else {
return rgb;
}
}
};
Oh, and I'm returning an Image because I painted directly to a component for testing
I need to resize alot of images from the ratio aspect (2:3) to (3:4).
The images are 800px x 1200px currently. I need them to be 600px x 800px eventually without any cropping.
May I know what libraries are available for me to do padding and resizing without cropping in Java?
From your current Image (assuming a java.awt.Image) you can use :
Image.getScaledInstance(w,h,h) as method
Image.SCALE_SMOOTH as algorithm for resize
And these steps:
compute the ratios in width and in height
depending on their values (padding width or padding height)
compute the width and height to obtain the scaled image
compute the padding required
write the image at the good position
static BufferedImage pad(BufferedImage image, double width, double height, Color pad) {
double ratioW = image.getWidth() / width;
double ratioH = image.getHeight() / height;
double newWidth = width, newHeight = height;
int fitW = 0, fitH = 0;
BufferedImage resultImage;
Image resize;
//padding width
if (ratioW < ratioH) {
newWidth = image.getWidth() / ratioH;
newHeight = image.getHeight() / ratioH;
fitW = (int) ((width - newWidth) / 2.0);
}//padding height
else if (ratioH < ratioW) {
newWidth = image.getWidth() / ratioW;
newHeight = image.getHeight() / ratioW;
fitH = (int) ((height - newHeight) / 2.0);
}
resize = image.getScaledInstance((int) newWidth, (int) newHeight, Image.SCALE_SMOOTH);
resultImage = new BufferedImage((int) width, (int) height, image.getType());
Graphics g = resultImage.getGraphics();
g.setColor(pad);
g.fillRect(0, 0, (int) width, (int) height);
g.drawImage(resize, fitW, fitH, null);
g.dispose();
return resultImage;
}
To use as
BufferedImage image = ...;
BufferedImage result = pad(image, 600, 800, Color.white);
Managed to do it using below code:
'w' is the amount of padding you need on each side.
BufferedImage newImage = new BufferedImage(image.getWidth()+2*w, image.getHeight(),
image.getType());
Graphics g = newImage.getGraphics();
g.setColor(Color.white);
g.fillRect(0,0,image.getWidth()+2*w,image.getHeight());
g.drawImage(image, w, 0, null);
g.dispose();
I think ffmpeg can help you to do anything with image.
e.g. Use ffmpeg to resize image
You can keep ffmpeg binaries in some conf folder.
Create sh script for ffmpeg command.
Use CommandLine from (Apache Commons exec library) to run the script.
is it possible to print part of the screen around the mouse?
I try with :
Toolkit tool = Toolkit.getDefaultToolkit();
Dimension d = tool.getScreenSize();
Rectangle rect = new Rectangle(d);
Robot robot = new Robot();
File f = new File("screenshot.jpg");
BufferedImage img = robot.createScreenCapture(rect);
ImageIO.write(img,"jpeg",f);
but it prints all screen, i can see that i can set the size of rectangle but i don't see how can i center rectangle so that it be around mouse.
public static BufferedImage printScrAroundCursor(int width, int height)
{
Toolkit tool = Toolkit.getDefaultToolkit();
Robot robot = new Robot();
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
int x = (int) b.getX();
int y = (int) b.getY();
int topLeftX = Math.max(0, x - (width / 2));
int topLeftY = Math.max(0, y - (height / 2));
if (topLeftX + width > tool.getScreenSize().getWidth())
width = tool.getScreenSize().getWidth() - topLeftX;
if (topLeftX + width > tool.getScreenSize().getHeight())
width = tool.getScreenSize().getHeight() - topLeftY;
return robot.createScreenCapture(new Rectangle(topLeftX , topLeftY , width, height));
}
You can use MouseInfo to get the mouse's location. From there, it's simple midpoint math:
int width = ...;
int height = ...;
Point m = MouseInfo.getPointerInfo().getLocation();
Rectangle rect = new Rectangle(m.x - width / 2, m.y - height / 2, width, height);
Robot robot = new Robot();
File f = new File("screenshot.jpg");
BufferedImage img = robot.createScreenCapture(rect);
ImageIO.write(img, "jpeg" ,f);
You will probably encounter strange results if the mouse is too close to the edge of the screen, but without more information, this special behavior is up to you to define how you wish for it to be.
Point mousePos = MouseInfo.getPointerInfo().getLocation();
int width = 300;
int height = 300;
Point origin = new Point(mousePos.getX() - width / 2, mousePos.getY() - height / 2);
Rectangle rect = new Rectangle(origin.getX(), origin.getY(), width, height);
BufferedImage img = robot.createScreenCapture(rect);
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.
im trying to resize bufferdImage in memory in java but to keep the aspect ratio of the image
im have something like this but this is not good
int w = picture.getWidth();
int h = picture.getWidth();
int neww=w;
int newh=h;
int wfactor = w;
int hfactor = h;
if(w > DEFULT_PICTURE_WIDTH || h > DEFULT_PICTURE_HIGHT)
{
while(neww > DEFULT_PICTURE_WIDTH)
{
neww = wfactor /2;
newh = hfactor /2;
wfactor = neww;
hfactor = newh;
}
}
picture = Utils.resizePicture(picture,neww,newh);
Adding to Erik's point about getScaledInstance, if you moved away from it to using the recommended scaling mechanisms in Java2D, you might have noticed that your images look noticeably worse.
The reason for that is when the Java2D discouraged use of getScaledInstance and AreaAveragingScaleFilter, they didn't replace it with anything as easy to use in the API, instead we were left to our own devices using Java2D APIs directly. Fortunately, Chris Campbell (from the J2D team) followed up with the recommendation of using an incremental scaling technique that gives similar looking results to AreaAveragingScaleFilter and runs faster; unfortunately the code is of a decent size and doesn't address your original question of honoring proportions.
About 6 months ago I saw all these questions on SO again and again about "scaling images in Java" and eventually collected all the advice, did all the digging and research I could, and compiled all of into a single "best practices" image scaling library.
The API is dead simple as it is only 1 class and a bunch of static methods. Basic use looks like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);
This is the simplest call where the library will make a best-guess at the quality, honor your image proportions, and fit the result within a 320x320 bounding box. NOTE, the bounding box is just the maximum W/H used, since your image proportions are honored, the resulting image would still honor that, say 320x200.
If you want to override the automatic mode and force it to give you the best-looking result and even apply a very mild anti-alias filter to the result so it looks even better (especially good for thumbnails), that call would look like:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
These are all just examples, the API is broad and covers everything from super-simple use cases to very specialized. You can even pass in your own BufferedImageOps to be applied to the image (and the library automatically fixes the 6-year BufferedImageOp JDK bug for you!)
There is a lot more to scaling images in Java successfully that the library does for you, for example always keeping the image in one of the best supported RGB or ARGB image types while operating on it. Under the covers the Java2D image processing pipeline falls back to an inferior software pipeline if the image type used for any image operations is poorly supported.
If all that sounded like a lot of headache, it sort of is... that's why I wrote the library and open sourced it, so folks could just resize their images and move on with their lives without needing to worry about it.
If width, height of source and target are known, use following function to determine scale of the image.
private double determineImageScale(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) {
double scalex = (double) targetWidth / sourceWidth;
double scaley = (double) targetHeight / sourceHeight;
return Math.min(scalex, scaley);
}
Then use this scale to scale up/down the image using following code
Image scaledImage = sourceBufferedImage.getScaledInstance((int) (width * scale), (int) (height * scale), Image.SCALE_SMOOTH);
For starters - take a look at line 2. Shouldnt that be getHeight()?
You dont want a while loop for the resizing, you want to find out the resizing ratio, which is a simple bit of math.
(width / height) = (new_width / new_height)
If you know one of the 'new' sizes, the other can be found via multiplication
new_height * (width / height) = new_width
You can also use the lazy method provided by BufferedImage's superclass Image, getScaledInstance() - using -1 for either width or height will maintain aspect ratio
ex:
scaledPic = picture.getScaledInstance(new_width, -1, Image.SCALE_FAST);
You may have a look at perils-of-image-getscaledinstance.html that explains why getScaledInstance(), used in some of the answers, should be avoided.
The article also provides alternative code.
I use these two methods to scale images, where max is the bigger dimension of your destination image. For 100x100 image it will be 100, for 200x300 image it will be 300.
public static BufferedImage scale(InputStream is, int max) {
Image image = null;
try {
image = ImageIO.read(is);
} catch (IOException e) {
e.printStackTrace();
}
int width = image.getWidth(null);
int height = image.getHeight(null);
double dWidth = 0;
double dHeight = 0;
if (width == height) {
dWidth = max;
dHeight = max;
}
else if (width > height) {
dWidth = max;
dHeight = ((double) height / (double) width) * max;
}
else {
dHeight = max;
dWidth = ((double) width / (double) height) * max;
}
image = image.getScaledInstance((int) dWidth, (int) dHeight, Image.SCALE_SMOOTH);
BufferedImage bImage = toBufferedImage(image);
return bImage;
}
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
return bimage;
}
If you want to resize a picture of w0 x h0 to w1 x h1 by keeping the aspect ratio, then calculate the vertical and horizontal scale and select the smaller one.
double scalex = 1;
double scaley = 1;
if (scalingMode == ScalingMode.WINDOW_SIZE) {
scalex = (double)getWidth() / frontbuffer.getWidth();
scaley = (double)getHeight() / frontbuffer.getHeight();
} else
if (scalingMode == ScalingMode.KEEP_ASPECT) {
double sx = (double)getWidth() / frontbuffer.getWidth();
double sy = (double)getHeight() / frontbuffer.getHeight();
scalex = Math.min(sx, sy);
scaley = scalex;
// center the image
g2.translate((getWidth() - (frontbuffer.getWidth() * scalex)) / 2,
(getHeight() - (frontbuffer.getHeight() * scaley)) / 2);
}
g2.scale(scalex, scaley);
if (interpolation != ImageInterpolation.NONE) {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolation.hint);
}
g2.drawImage(frontbuffer, 0, 0, null);
private static BufferedImage resize(BufferedImage img, int width, int height) {
double scalex = (double) width / img.getWidth();
double scaley = (double) height / img.getHeight();
double scale = Math.min(scalex, scaley);
int w = (int) (img.getWidth() * scale);
int h = (int) (img.getHeight() * scale);
Image tmp = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
BufferedImage resized = new BufferedImage(w, h, img.getType());
Graphics2D g2d = resized.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return resized;
}