Rotate BufferedImage shows black bound - java

I want to rotate a bufferedImage. The code I use makes it possible that the image rotates. But it cuts the image to a square. The screen shows then a black "border" on the left and the right side.
If I use debugging tool, the image width is about the whole width included the black "border". But the black "border" don't rotate, it's always at the left and the right side. And the image is missing the picture-parts left and right. The squared-image isn't cut again if I rotate it again. If I change the src.getWidth()-parts the image will be smaller with each rotation.
private static BufferedImage rotateImage(BufferedImage src, double degrees) {
AffineTransform affineTransform = AffineTransform.getRotateInstance(
Math.toRadians(degrees), (src.getWidth() / 2), (src.getHeight() / 2));
BufferedImage rotatedImage = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
g.setTransform(affineTransform);
g.drawImage(src, 0, 0, null);
return rotatedImage;
}
public void rotateImage(int degree) {
if (this.image != null) {
this.setImage(myJComponent.rotateImage(this.image, degree));
}
}

The size of the image will change as you rotate it.
Here is some code to play with:
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class Rotation
{
BufferedImage image;
JLabel label;
public Rotation(BufferedImage image)
{
this.image = image;
}
private BufferedImage getImage(double theta)
{
// Determine the size of the rotated image
double cos = Math.abs(Math.cos(theta));
double sin = Math.abs(Math.sin(theta));
double width = image.getWidth();
double height = image.getHeight();
int w = (int)(width * cos + height * sin);
int h = (int)(width * sin + height * cos);
// Rotate and paint the original image onto a BufferedImage
BufferedImage out = new BufferedImage(w, h, image.getType());
Graphics2D g2 = out.createGraphics();
g2.setPaint(UIManager.getColor("Panel.background"));
g2.fillRect(0,0,w,h);
double x = w/2;
double y = h/2;
AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
x = (w - width)/2;
y = (h - height)/2;
at.translate(x, y);
g2.drawRenderedImage(image, at);
g2.dispose();
return out;
}
private JLabel getLabel()
{
ImageIcon icon = new ImageIcon(image);
label = new JLabel(icon);
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
private JSlider getSlider()
{
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
BufferedImage bi = getImage(Math.toRadians(value));
label.setIcon(new ImageIcon(bi));
}
});
return slider;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
String path = "mong.jpg";
ClassLoader cl = Rotation.class.getClassLoader();
BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
Rotation r = new Rotation(bi);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(r.getLabel()));
f.getContentPane().add(r.getSlider(), "South");
f.pack();
f.setLocation(200,200);
f.setVisible(true);
}
catch(IOException e)
{
System.out.println(e);
}
}
});
}
}
Just change the image you want to read and use the slider to rotate the image.

Related

Measure pixel height of rotated string Java

I am attempting to calculate the pixel height of a rotated string in Java. At the moment, I am creating an affine transformation and rotating it by the desired amount.
Then, I create a font derived form this affine transformation. I also create a FontRenderContect and a GlyphVector.
When I attempt to get the pixel bounds from the glyph vector, it gives me the height of the text as if it weren't rotated
AffineTransform affineTransform = new AffineTransform();
affineTransform.rotate(Math.toRadians(45),0,0);
Font rotatedFont = font.deriveFont(affineTransform);
FontRenderContext frc = new FontRenderContext(affineTransform, true, false);
GlyphVector gv = rotatedFont.createGlyphVector(frc, "i");
System.out.println(gv.getPixelBounds(frc, 0, 0).getBounds());
gv = rotatedFont.createGlyphVector(frc, "iiiiiiiiiiii");
System.out.println(gv.getPixelBounds(frc, 0, 0).getBounds());
Results in:
java.awt.Rectangle[x=0,y=1,width=9,height=2]
java.awt.Rectangle[x=0,y=1,width=31,height=2]
The height should be much greater as it is rotated at a 45 degree angle (the width should equal the height).
Any help is appreciated.
Determining the bounding box is made easy by using a Shape & applying the AffineTransform directly to that shape.
import java.awt.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class BoundingBoxRotatedText {
private JComponent ui = null;
JLabel output = new JLabel();
Font font = new Font(Font.SERIF, Font.PLAIN, 20);
String string = "The quick brown fox jumps over the lazy dog";
Shape textShape = getShapeOfText(font, string);
Rectangle2D rectangle = textShape.getBounds2D();
BoundingBoxRotatedText() {
initUI();
}
public final void initUI() {
if (ui!=null) return;
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,4,4,4));
ui.add(output);
output.setIcon(new ImageIcon(getImage()));
}
public static Shape getShapeOfText(Font font, String msg) {
BufferedImage bi = new BufferedImage(
1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, msg);
return gv.getOutline();
}
public static Shape moveShapeToCenter(Shape shape, int w, int h) {
Rectangle2D b = shape.getBounds2D();
double xOff = -b.getX() + ((w - b.getWidth()) / 2d);
double yOff = -b.getY() + ((h - b.getHeight()) / 2d);
AffineTransform move = AffineTransform.getTranslateInstance(xOff, yOff);
return move.createTransformedShape(shape);
}
private BufferedImage getImage() {
int sz = (int)(rectangle.getBounds().width*1.2);
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, sz, sz);
Shape shape = moveShapeToCenter(textShape, sz, sz);
g.setColor(Color.GREEN);
g.fill(shape);
g.draw(shape);
g.draw(shape.getBounds());
AffineTransform rotate45 = AffineTransform.
getRotateInstance(Math.PI/4, sz/2, sz/2);
shape = rotate45.createTransformedShape(shape);
g.setColor(Color.BLUE);
g.fill(shape);
g.draw(shape);
g.draw(shape.getBounds());
shape = rotate45.createTransformedShape(shape);
g.setColor(Color.RED);
g.fill(shape);
g.draw(shape);
g.draw(shape.getBounds());
g.dispose();
return bi;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
BoundingBoxRotatedText o = new BoundingBoxRotatedText();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}

Java, JPanel crop image while rotating

i am making simple image editor in java. My problem is that when i rotate image its being cropped, i will try to show it on images:
before rotation
http://i.imgur.com/KjtHslb.jpg
after 45 degre
http://i.imgur.com/SKOEFP7.jpg
after 90 degrees rotation
i[dot]imgur[dot]com/4NrABIA.jpg
It looks like JPanel crops rotated image into size of original image, i dont know why :/ so i am asking for help. Thank you.
Heres my code:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import static java.awt.image.ImageObserver.WIDTH;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
public class OknoInterfejsu extends JFrame{
Container content;
BufferedImage image = null;
JLabel picLabel;
Image scaledImage;
private final JButton przyciski[];
private JComboBox wybor;
private String wybor_str[];
private int picHeight;
private int picWidth;
private int realHeight;
double picAspect;
private String path;
private class ObslugaPrzycisku implements ActionListener{
private final JFrame ref_okno;
ObslugaPrzycisku(JFrame okno){
ref_okno = okno;
}
public void actionPerformed(ActionEvent e) {
JButton bt = (JButton)e.getSource();
if(bt==przyciski[0]){
//-----------WCZYTAJ PRZYCISK-------------
{
JFileChooser fileChooser = new JFileChooser("C:\\Users\\Filip\\Pictures\\");
FileFilter imageFilter = new FileNameExtensionFilter(
"Image files", ImageIO.getReaderFileSuffixes());
fileChooser.setFileFilter(imageFilter);
if (fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
{
File plik = fileChooser.getSelectedFile();
path = plik.getPath();
System.out.print(path);
try {
image = ImageIO.read(new File(path));
} catch (IOException ex) {
Logger.getLogger(OknoInterfejsu.class.getName()).log(Level.SEVERE, null, ex);
}
picHeight = image.getHeight();
picWidth = image.getWidth();
picAspect = (double) picHeight / picWidth;
//if(picAspect>1)realHeight = 400;
//else realHeight = 800;
realHeight = 400;
scaledImage = image.getScaledInstance(realHeight, (int) (realHeight*picAspect),Image.SCALE_SMOOTH);
picLabel = new JLabel(new ImageIcon(scaledImage));
content.add(picLabel);
content.revalidate();
content.repaint();
}
}
}
//--------------------------OBROT +------------
else if(bt==przyciski[1]){
picLabel.setIcon(null);
int temp1 = (int) (400*picAspect);
picRotate(scaledImage, 400,temp1);
picLabel = new JLabel(new ImageIcon(scaledImage));
content.add(picLabel);
content.revalidate();
content.repaint();
}
}
}
public OknoInterfejsu()
{
super("Okno interfejsu");
setSize(1000,700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//-------------------------------------------------------
przyciski = new JButton[2];
przyciski[0] = new JButton("Wczytaj Obraz");
przyciski[0].addActionListener(new ObslugaPrzycisku(this));
przyciski[1] = new JButton("ObrĂ³t +");
przyciski[1].addActionListener(new ObslugaPrzycisku(this));
JPanel panelPrzyciski = new JPanel(new FlowLayout());
panelPrzyciski.add(przyciski[0]);
panelPrzyciski.add(przyciski[1]);
//-------------------------------------------------------
content = getContentPane();
content.setLayout(new BorderLayout());
content.add(panelPrzyciski,BorderLayout.SOUTH);
setVisible(true);
}
public static void main(String args[]){
new OknoInterfejsu();
}
public void picRotate(Image image,int w,int h){
BufferedImage temp=(BufferedImage)createImage(w,h);
Graphics2D g2d=(Graphics2D)temp.createGraphics();
g2d.rotate((Math.PI/2)*22.5, w/2, h/2);
g2d.drawImage(image,0,0,null);
this.scaledImage=temp;
g2d.dispose();
}
}
When you rotate an image, it changes size, you need to create a new image whose bounds encompass this new size, for example
public Image rotateBy(Image image, double degrees) {
// The size of the original image
int w = source.getWidth();
int h = source.getHeight();
// The angel of the rotation in radians
double rads = Math.toRadians(degrees);
// Some nice math which demonstrates I have no idea what I'm talking about
// Okay, this calculates the amount of space the image will need in
// order not be clipped when it's rotated
double sin = Math.abs(Math.sin(rads));
double cos = Math.abs(Math.cos(rads));
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
// A new image, into which the original can be painted
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
// The transformation which will be used to actually rotate the image
// The translation, actually makes sure that the image is positioned onto
// the viewable area of the image
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
// And we rotate about the center of the image...
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
// And we paint the original image onto the new image
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return rotated;
}
Then you just need to call it...
picLabel.setIcon(new ImageIcon(rotateBy(scaledImage, 22.5)));
Because setIcon is a bound property, it should trigger a layout and paint pass automatically, if not, you can simply call revalidate and repaint to trigger them manually
I see you're using picLabel to store the image and display it:
picRotate(scaledImage, 400,temp1);
picLabel = new JLabel(new ImageIcon(scaledImage));
content.add(picLabel);
content.revalidate();
content.repaint();
For some reason label size is the Image height and width but label doesn't know the image was rotated. A work around this might be to set the size of the label prior to adding the image to it, and have a way to guaranteed that the size will accommodate the whole image even if is rotated:
picRotate(scaledImage, 400,temp1);
picLabel = new JLabel();
picLabel.setPreferredSize(new Dimension(800, 800));
picLabel.add(new ImageIcon(scaledImage))
content.add(picLabel);
content.revalidate();
content.repaint();
Now picLabel width and height in the preferred size has to be calculated based on the space needed by the rotated image. I thing some trigonometric functions might help for this. I'm assuming 800x800 is enough space to accommodate the image

BufferedImage gets cut off after rotation

My code rotates the image by 0.4 degrees every "update", the image rotates in range [-10,+10] continuously.
The problem is that the buffered image gets cut off at the edges when it rotates, seems like the rotation changes the size that the bufferedImage needs but the size never updates, any ideas how i can get it to work?
protected double rotation = 0;
private double rotDir = 0.4;
private BufferedImage getRotatedSprite(){
if(Math.abs(rotation)>10)
rotDir=rotDir*(-1);
rotation+=rotDir;
ImageIcon icon = new ImageIcon(bImage);
BufferedImage bImg = new BufferedImage(icon.getIconWidth(),
icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d= (Graphics2D)bImg.getGraphics();
g2d.rotate(Math.toRadians(rotation), icon.getIconWidth()/2, icon.getIconHeight()/2);
g2d.drawImage(bImage, 0, 0, null);
return bImg;
}
public void drawSprite(Graphics g) {
BufferedImage image = getRotatedSprite();
if(customWidth != -1 && customHeight != -1){
g.drawImage(image, (int)locX, (int)locY, customWidth, customHeight, this);
}else
g.drawImage(image, (int)locX, (int)locY, this);
}
seems like the rotation changes the size that the bufferedImage
Yes the size will change based on the angle of rotation.
You may find it easier to use the Rotated Icon. It does the rotation for you and recalculates the size (if necessary). There is no need to create new BufferedImages, you just set the degrees of rotation.
Simple example. Use the slilder bar to rotate the icon:
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class Rotation3 extends JPanel
{
private Icon icon;
private RotatedIcon rotated;
private int degrees;
public Rotation3(Image image)
{
icon = new ImageIcon( image );
rotated = new RotatedIcon(icon, 0);
// rotated.setCircularIcon( true );
setDegrees( 0 );
setPreferredSize( new Dimension(600, 600) );
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// translate x/y so Icon rotated around a specific point (300, 300)
int x = 300 - (rotated.getIconWidth() / 2);
int y = 300 - (rotated.getIconHeight() / 2);
rotated.paintIcon(this, g, x, y);
g.setColor(Color.RED);
g.fillOval(295, 295, 10, 10);
}
public void setDegrees(int degrees)
{
this.degrees = degrees;
rotated.setDegrees(degrees);
repaint();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
// String path = "dukewavered.gif";
String path = "lunch75.jpg";
BufferedImage bi = ImageIO.read(Rotation3.class.getResourceAsStream(path));
final Rotation3 r = new Rotation3(bi);
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
r.setDegrees( value );
}
});
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(r));
f.add(slider, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
catch(IOException e)
{
System.out.println(e);
}
}
});
}
}

Display the whole image using DrawImage() after Image rotation

I am rotating and scaling the image using AffineTransform. When I display the image using Graphics2D.drawImage() the whole image does not get displayed so I am calling the AffineTransform.translate method.
Here is the code I have currently written:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Dimension dim = getPreferredSize();
int x = (getWidth() - image.getWidth(null)) / 2;
int y = (getHeight() - image.getHeight(null)) / 2;
double angle = Math.toRadians(rotateAngle);
AffineTransform identity = new AffineTransform();
identity.scale(scale, scale);
identity.translate(x,y);
AffineTransform at = new AffineTransform();
setPreferredSize(new Dimension((int)(identity.getTranslateX()+(image.getWidth(null)*scale)),(int)(identity.getTranslateY()+(image.getHeight(null)*scale))));
at.setTransform(identity);
at.rotate(angle);
g2d.transform(at);
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
}
The translate method sometimes displays the whole image and sometimes does not depending on the image size and rotating angle. Is there anyway to make sure the whole image gets displayed after a rotate.
I had a look at this previous asked question:
Java2D Image Rotation Issue
but the solution posted there gave the same problem for me.
I finally figured out a way to do this for 90 degrees, it will not work for other degrees.
void rotate90(){
if(image!=null){
int w = image.getWidth(null); //the Width of the original image
int h = image.getHeight(null);//the Height of the original image
if(w>h){
BufferedImage dimg = new BufferedImage(w, w, BufferedImage.TYPE_3BYTE_BGR );
Graphics2D g = dimg.createGraphics();
g.translate(-(w-h), 0);
g.rotate(Math.toRadians(90), w/2, w/2);
g.drawImage(image, 0, 0,null);
BufferedImage bimg = new BufferedImage(h, w, BufferedImage.TYPE_3BYTE_BGR );
Graphics2D g2 = (Graphics2D)bimg.getGraphics();
g2.drawImage(dimg,0,0,h,w,0,0,h,w,null);
dimg.flush();
dimg=null;
image = createImage(bimg.getSource());
bimg.flush();
bimg= null;
}
else{
BufferedImage dimg = new BufferedImage(h, h, BufferedImage.TYPE_3BYTE_BGR );
Graphics2D g = dimg.createGraphics();
g.translate(-(w-h), 0);
g.rotate(Math.toRadians(90), w/2, w/2);
g.drawImage(image, 0, 0,null);
BufferedImage bimg = new BufferedImage(h, w, BufferedImage.TYPE_3BYTE_BGR );
Graphics2D g2 = (Graphics2D)bimg.getGraphics();
g2.drawImage(dimg,0,0,h,w,0,0,h,w,null);
dimg.flush();
dimg=null;
image = createImage(bimg.getSource());
bimg.flush();
bimg= null;
}
}
}
void rotateClockWise(){
if(image!=null){
rotate90();
setPanelsize();
revalidate();
repaint();
}
}
sometimes displays the whole image and sometimes does not depending on the image size and rotating angle
You need to recalculate the size of the rotated image. Here is an example of how to do that:
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class Rotation
{
BufferedImage image;
JLabel label;
public Rotation(BufferedImage image)
{
this.image = image;
}
private BufferedImage getImage(double theta)
{
// Determine the size of the rotated image
double cos = Math.abs(Math.cos(theta));
double sin = Math.abs(Math.sin(theta));
double width = image.getWidth();
double height = image.getHeight();
int w = (int)(width * cos + height * sin);
int h = (int)(width * sin + height * cos);
// Rotate and paint the original image onto a BufferedImage
BufferedImage out = new BufferedImage(w, h, image.getType());
Graphics2D g2 = out.createGraphics();
g2.setPaint(UIManager.getColor("Panel.background"));
g2.fillRect(0,0,w,h);
double x = w/2;
double y = h/2;
AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
x = (w - width)/2;
y = (h - height)/2;
at.translate(x, y);
g2.drawRenderedImage(image, at);
g2.dispose();
return out;
}
private JLabel getLabel()
{
ImageIcon icon = new ImageIcon(image);
label = new JLabel(icon);
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
private JSlider getSlider()
{
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
BufferedImage bi = getImage(Math.toRadians(value));
label.setIcon(new ImageIcon(bi));
}
});
return slider;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
String path = "mong.jpg";
ClassLoader cl = Rotation.class.getClassLoader();
BufferedImage bi = ImageIO.read(cl.getResourceAsStream(path));
Rotation r = new Rotation(bi);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(r.getLabel()));
f.getContentPane().add(r.getSlider(), "South");
f.pack();
f.setLocation(200,200);
f.setVisible(true);
}
catch(IOException e)
{
System.out.println(e);
}
}
});
}
}
Not sure how the scaling will affect this. Maybe you can just multiply the width/height by the scaling factor?

Trying to paint image to JFrame with Java BufferedImage, Graphics

I'm trying to capture the screen and then paint the image to a JFrame recursively while scaling the image (to create that effect you get when you look at a mirror in a mirror).
I'm having trouble with my code - it doesn't paint any graphics. What am I doing wrong?
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class ScreenCapture extends JFrame {
BufferedImage screenCapture;
Graphics screenCaptureGraphics;
private static int recurseCount = 0;
private static float $scale = 0.9f;
private static float scale = 1.0f;
private static int height;
private static int width;
ScreenCapture() {
try {
screenCapture = new Robot().createScreenCapture(
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );
height = screenCapture.getHeight();
width = screenCapture.getWidth();
setSize(new Dimension(width, height));
addWindowListener(new LocalWindowListener());
Graphics g = recursiveDraw(screenCapture, getGraphics());
paint(g);
} catch (HeadlessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private Graphics recursiveDraw(BufferedImage img, Graphics imgG) {
updateScale(++recurseCount);
float newWidth = scale*width;
float newHeight = scale*height;
int w = (int) newWidth;
int h = (int) newHeight;
System.out.println("W: " + w + "; H: " + h);
if (w >= 10 && h >= 10) {
//scale image
System.out.print("Creating scaled Image...");
Image scaled = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
imgG = resized.createGraphics();
imgG.drawImage(scaled, 0, 0, null);
System.out.println("...Image drawn to graphics");
//return new graphics
return recursiveDraw(resized, imgG);
} else {
//otherwise return old graphics
System.out.println("Completed.");
return imgG;
}
}
private void updateScale(int count) {
for (int i=0; i<count; i++) {
scale *= $scale;
}
System.out.println("Updated scale: " + scale + "; Recurse count: " + recurseCount);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ScreenCapture().setVisible(true);
}
});
}
private class LocalWindowListener extends WindowAdapter {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0); return;
}
}
}
EDIT:
This is what I tried after #andrew-thompson 's answer:
ScreenCapture() {
try {
screenCapture = new Robot().createScreenCapture(
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );
height = screenCapture.getHeight();
width = screenCapture.getWidth();
setSize(new Dimension(width, height));
addWindowListener(new LocalWindowListener());
setLayout(new GridLayout());
add(new PaintPanel());
} catch (HeadlessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private class PaintPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
g=recursiveDraw(screenCapture, g);
//what to do with g?
}
}
I still have the same problem where I don't know how to make the BufferedImage paint to the graphics.
would separate out your Swing code from your recursive image creation code. In fact consider creating a static method that creates and returns the BufferedImage and that has no Swing code in it. Then have your GUI call the method when it wishes, and take the image and either write it to disk or display it in a JLabel's ImageIcon.
When I did this (today in fact), I created a recursive method with this signature
private static void recursiveDraw(BufferedImage img, Graphics imgG, double scale) {
and with this method body (in pseudo-code)
start recursiveDraw method
// most important: all recursions must have a good ending condition:
get img height and width. If either <= a min, return
create a BufferedImage, smlImg, for the smaller image using the height,
width and scale factor
Get the Graphics object, smlG, from the small image
Use smlG.drawImage(...) overload to draw the big image in shrunken
form onto the little image
recursively call recursiveDraw passing in smlImg, smlG, and scale.
dispose of smlG
draw smlImg (the small image) onto the bigger one using the bigger's
Graphics object (passed into this method) and a different
overload of the drawImage method.
end recursiveDraw method
This algorithm resulted in images like:
For example:
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class RecursiveDrawTest {
private static final Color BACKGRND_1 = Color.green;
private static final Color BACKGRND_2 = Color.MAGENTA;
private static final Color FOREGRND_1 = Color.blue;
private static final Color FOREGRND_2 = Color.RED;
private static void createAndShowGui() {
final JPanel mainPanel = new JPanel(new BorderLayout());
final JSlider slider = new JSlider(50, 90, 65);
slider.setMajorTickSpacing(10);
slider.setMinorTickSpacing(5);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
JPanel southPanel = new JPanel();
southPanel.add(new JLabel("Percent Size Reduction:"));
southPanel.add(slider);
southPanel.add(new JButton(new AbstractAction("Create Recursive Image") {
#Override
public void actionPerformed(ActionEvent arg0) {
try {
double scale = slider.getValue() / 100.0;
BufferedImage img = createRecursiveImg(scale);
ImageIcon icon = new ImageIcon(img);
JLabel label = new JLabel(icon);
Window win = SwingUtilities.getWindowAncestor(mainPanel);
JDialog dialog = new JDialog(win, "Image", ModalityType.MODELESS);
dialog.getContentPane().add(label);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
} catch (AWTException e) {
e.printStackTrace();
}
}
}));
mainPanel.add(new JScrollPane(new JLabel(new ImageIcon(createLabelImg()))),
BorderLayout.CENTER);
mainPanel.add(southPanel, BorderLayout.PAGE_END);
JFrame frame = new JFrame("RecursiveDrawTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// create a background image to display in a JLabel so that the GUI
// won't be boring.
private static BufferedImage createLabelImg() {
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int width = (5 * d.width) / 6;
int height = (5 * d.height) / 6;
BufferedImage img = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, BACKGRND_1, 40, 40, BACKGRND_2, true));
g2.fillRect(0, 0, width, height);
g2.setPaint(new GradientPaint(0, height, FOREGRND_1, 40, height - 40, FOREGRND_2, true));
g2.fillOval(0, 0, 2 * width, 2 * height);
g2.dispose();
return img;
}
// non-recursive image to get the initial image that will be drawn recursively
public static BufferedImage createRecursiveImg(double scale) throws AWTException {
Robot robot = new Robot();
Dimension screenSz = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle screenRect = new Rectangle(screenSz);
BufferedImage img = robot.createScreenCapture(screenRect);
Graphics g = img.getGraphics();
recursiveDraw(img, g, scale); // call recursive method
g.dispose();
return img;
}
// recursive method to draw image inside of image
private static void recursiveDraw(BufferedImage img, Graphics g, double scale) {
int w = img.getWidth();
int h = img.getHeight();
int smlW = (int)(w * scale);
int smlH = (int)(h * scale);
// criteria to end recursion
if (smlW <= 1 || smlH <= 1) {
return;
}
BufferedImage smlImg = new BufferedImage(smlW, smlH, BufferedImage.TYPE_INT_ARGB);
Graphics smlG = smlImg.getGraphics();
// draw big image in little image, scaled to little image
smlG.drawImage(img, 0, 0, smlW, smlH, null);
// recursive call
recursiveDraw(smlImg, smlG, scale);
smlG.dispose(); // clear resources when done with them
// these guys center the smaller img on the bigger
int smlX = (w - smlW) / 2;
int smlY = (h - smlH) / 2;
// draw small img on big img
g.drawImage(smlImg, smlX, smlY, smlW, smlH, null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Graphics g = recursiveDraw(screenCapture, getGraphics());
Don't call getGraphics(). Override paint(Graphics)1 & use the supplied Graphics instance.
When using Swing, it is actually best to override the paintComponent(Graphics) method of a JComponent or JPanel. Then add that to the top-level container.
Is this what you are hoping for :
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.*;
public class CaptureScreen extends JPanel
{
private BufferedImage screenShot;
private JLabel imageLabel;
private BufferedImage secondScreenShot;
public void createAndDisplayGUI()
{
JFrame frame = new JFrame("CAPTURE SCREEN");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
//imageLabel = new JLabel();
//getImageForLabel();
//add(imageLabel);
frame.getContentPane().add(this);
frame.setSize(600, 600);
frame.setVisible(true);
}
private void getImageForLabel()
{
Robot robot = null;
try
{
robot = new Robot();
}
catch(Exception e)
{
e.printStackTrace();
}
screenShot = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIcon icon = new ImageIcon(screenShot);
imageLabel.setIcon(icon);
}
public void paintComponent(Graphics g)
{
Robot robot = null;
try
{
robot = new Robot();
}
catch(Exception e)
{
e.printStackTrace();
}
screenShot = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
secondScreenShot = getScaledImage((Image) screenShot, 300, 300);
g.drawImage(screenShot, 0, 0, null);
g.drawImage(secondScreenShot, 150, 150, null);
}
private BufferedImage getScaledImage(Image srcImg, int w, int h)
{
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TRANSLUCENT);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(srcImg, 0, 0, w, h, null);
g2.dispose();
return resizedImg;
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new CaptureScreen().createAndDisplayGUI();
}
});
}
}
Here is the output :
Get rid of the recursion. Create a single buffered image of the correct size and create a Graphics object for it. Just use a loop to draw progressively smaller scaled images down to whatever threshold you choose. Finally use g.drawImage() inside paintComponent() to draw your image to the screen. If you keep the recursion you need to pass the image and continually overlay the scaled down image. You do not need to return anything from the method.

Categories

Resources