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.
Related
I am creating an image using the Graphics library, and attempting to save that created image using BufferedImage and ImageIO. After running, my image pops up, but the saved image is just black.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class drawing extends Canvas {
public static void main(String[] args) {
JFrame frame = new JFrame("My Drawing");
Canvas canvas = new drawing();
canvas.setSize(400, 400);
canvas.setBackground(Color.CYAN);
frame.add(canvas);
frame.pack();
frame.setVisible(true);
}
public void paint(Graphics g) {
Rectangle bb = new Rectangle(100, 100, 200, 200);
g.setColor(Color.yellow);
try {
mickey(g, bb);
} catch (IOException e) {
e.printStackTrace();
}
}
public void boxOval(Graphics g, Rectangle bb) {
g.fillOval(bb.x, bb.y, bb.width, bb.height);
g.setColor(Color.blue);
}
public void mickey(Graphics g, Rectangle bb) throws IOException {
boxOval(g, bb);
int dx = bb.width / 2;
int dy = bb.height / 2;
g.setColor(Color.RED);
Rectangle half = new Rectangle(bb.x, bb.y, dx, dy);
half.translate(-dx / 2, -dy / 2);
boxOval(g, half);
half.translate(dx * 2, 0);
boxOval(g, half);
half.translate(dx / 10, 50);
boxOval(g, half);
BufferedImage buff = new BufferedImage(dx, dy, BufferedImage.TYPE_INT_RGB);
File file = new File("mickey.png");
System.out.println("saving....");
ImageIO.write(buff, "png", file);
System.out.println("saved!");
}
}
I expect that the image saved to mickey.png would be the same as the one I draw earlier in the mickey method.
You need to do this:
BufferedImage buff = new BufferedImage(dx, dy, BufferedImage.TYPE_INT_RGB);
this.paint(buff.getGraphics()); // call paint to draw on the image
File file = new File("mickey.png");
System.out.println("saving....");
ImageIO.write(buff, "png", file);
System.out.println("saved!");
Rearrange your code to do the saving outside mickey() and outside paint().
I am trying to create a JLabel with a oval shaped image, like this.
My code is the following:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public final class RoundedButtonDemo {
private static Image bi;
public static void main(String[] args) {
try {
loadImage();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
} catch (IOException e) {
// handle exception
}
}
private static void loadImage() throws IOException {
int newWidth = 80;
int newHeight = 40;
bi = ImageIO.read(RoundedButtonDemo.class.getResource("/resources/login.png"));
bi = bi.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);
}
private static void createAndShowGUI() {
final JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel label = new JLabel();
label.setSize(new Dimension(5, 5));
label.setIcon(new ImageIcon(bi));
label.setText("Hello World");
label.setHorizontalTextPosition(JLabel.CENTER);
// label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
label.addMouseListener(new MouseListener() {
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == label) {
if (count % 2 == 0) {
label.setText("Bye");
} else {
label.setText("Hello World");
}
count++;
}
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
});
frame.add(label);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
This code creates a JLabel containing the above image. The text of the JLabel should alternate every time the button is clicked, based on a MouseListener added to the JLabel.
The problem I'm facing is that even when I click outside the image (also outside the JLabel), the MouseListener is triggered, and the text alternates.
The big picture of what I want to achieve is :
A rounded button which responds to a MouseListener, whenever it is clicked anywhere on its surface.
I tried your code. You are getting this behavior because your JLabel is actually filling the entire frame. You need to set a layout for your frame; something like this:
// ...
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout()); // <-- you need this
final JLabel label = new JLabel();
label.setPreferredSize(new Dimension(80, 40)); // <-- also this
label.setIcon(new ImageIcon(bi));
label.setText("Hello World");
// ...
FlowLayout is one of the simplest layout managers, of which there are many. You can read about them here: https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
Hope this helps.
PS: you had a good idea trying to debug this problem, adding a border like this:
// label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
Not sure why you commented it out. Maybe you didn't notice that there was a black border around the whole frame area. Try setting to color to Color.RED or something more noticeable.
As yoshi pointed out, the JLabel fills the entire content pane, so clicking anywhere in the content pane results in mouse events for the label.
Some words:
1) You could also use GridBagLayout, instead of FlowLayout, in case you want the JLabel centered in its parent component (both vertically and horizontally). This solution is here.
Like so:
final JPanel singleCenteredComponentJPanel = new JPanel(new GridBagLayout());
singleCenteredComponentJPanel.add(label);
frame.add(singleCenteredComponentJPanel);
2) If, furthermore, you want to ignore mouse clicks on any transparent pixels of the label icon (if any) you can do the following:
Use the BufferedImage.getRGB(int x, int y), BufferedImage.getColorModel() and ColorModel.getAlpha(int pixel) methods to determine the alpha value of the clicked pixel for each mouse event. If the alpha value is equal to 0, then the pixel is completely transparent, otherwise the alpha value is between 1 and 255 (both inclusive) which in turn means the pixel is not completely transparent. Example code follows below.
And instead of Image.getScaledInstance(...) to scale the image (which returns Image, but we need BufferedImage), use the solution provided here.
Like so:
private static void loadImage() throws IOException {
int newWidth = 80;
int newHeight = 40;
bi = ImageIO.read(RoundedButtonDemo.class.getResource("/resources/login.png"));
bi = getScaledBufferedImage(bi, newWidth, newHeight);
}
public static BufferedImage getScaledBufferedImage(final Image img,
final int newWidth,
final int newHeight) {
final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice gdev = genv.getDefaultScreenDevice();
final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
final BufferedImage simg = gcnf.createCompatibleImage(newWidth, newHeight, Transparency.TRANSLUCENT);
//Painting input image to output image:
final Graphics2D g2d = simg.createGraphics();
g2d.drawImage(img, 0, 0, newWidth, newHeight, null); //#Docs "...is scaled if necessary.".
g2d.dispose();
return simg;
}
Also change the reference bi from Image to BufferedImage.
And then the MouseListener of the label:
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == label) {
//Get the mouse click position (in pixels),
//relative to the top-left corner of label:
final Point relativeClickPoint = e.getPoint();
//Obtain alpha value from the TYPE_INT_ARGB pixel:
final int pixel = bi.getRGB(relativeClickPoint.x, relativeClickPoint.y);
final int alpha = bi.getColorModel().getAlpha(pixel);
if (alpha > 0) { //Check if the pixel is not transparent.
if (count % 2 == 0) {
label.setText("Bye");
} else {
label.setText("Hello World");
}
count++;
}
}
}
Be aware for the above implementation: All transparent pixels of the image will be ignored (including transparent pixels "inside" the shape, if any). Which means the user could click in the center of the image and nothing would happen if the clicked pixel was completely transparent. So I made the assumption that this is not the case with your image here.
3) For the scaling of the Image, you can determine the new size with the method collapseInside(...):
/**
* #param resultDim Output dimension (same aspect ratio as the original dimension, and inside containerDim).
* #param originalDim Original dimension.
* #param containerDim Dimension with the maximum width and maximum height.
*/
public static void collapseInside(final Dimension resultDim,
final Dimension originalDim,
final Dimension containerDim) {
resultDim.setSize(originalDim);
if (resultDim.width > containerDim.width) {
//Adjusting height for max width:
resultDim.height = ( resultDim.height * containerDim.width ) / resultDim.width;
resultDim.width = containerDim.width;
}
if (resultDim.height > containerDim.height) {
//Adjusting width for max height:
resultDim.width = ( resultDim.width * containerDim.height ) / resultDim.height;
resultDim.height = containerDim.height;
}
}
With this method:
a) Scaled Image's width will be less than or equal to container's size width (resultDim.width <= containerDim.width).
b) Same for height (resultDim.height <= containerDim.height).
c) Aspect ratio of the original Image size will be preserved in the new Image size (resultDim.width / resultDim.height == originalDim.width / originalDim.height).
With Image.getScaledInstance(...) and the previous getScaledBufferedImage(...) the aspect ratio of the Image could change.
So I edit the getScaledBufferedImage(...) to use collapseInside(...):
public static BufferedImage getScaledBufferedImage(final Image img,
final int newWidth,
final int newHeight) {
final Dimension originalDim = new Dimension(img.getWidth(null), img.getHeight(null)),
containerDim = new Dimension(newWidth, newHeight),
resultDim = new Dimension();
collapseInside(resultDim, originalDim, containerDim);
final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice gdev = genv.getDefaultScreenDevice();
final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
final BufferedImage simg = gcnf.createCompatibleImage(resultDim.width, resultDim.height, Transparency.TRANSLUCENT);
//Painting input image to output image:
final Graphics2D g2d = simg.createGraphics();
g2d.drawImage(img, 0, 0, resultDim.width, resultDim.height, null); //#Docs "...is scaled if necessary.".
g2d.dispose();
return simg;
}
Complete code for all three above:
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public final class RoundedButtonDemo {
private static BufferedImage bi;
public static void main(String[] args) {
try {
loadImage();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
} catch (IOException e) {
// handle exception
}
}
private static void loadImage() throws IOException {
int newWidth = 80;
int newHeight = 40;
bi = ImageIO.read(RoundedButtonDemo.class.getResource("/resources/login.png"));
bi = getScaledBufferedImage(bi, newWidth, newHeight);
}
public static BufferedImage getScaledBufferedImage(final Image img,
final int newWidth,
final int newHeight) {
final Dimension originalDim = new Dimension(img.getWidth(null), img.getHeight(null)),
containerDim = new Dimension(newWidth, newHeight),
resultDim = new Dimension();
collapseInside(resultDim, originalDim, containerDim);
final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice gdev = genv.getDefaultScreenDevice();
final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
final BufferedImage simg = gcnf.createCompatibleImage(resultDim.width, resultDim.height, Transparency.TRANSLUCENT);
//Painting input image to output image:
final Graphics2D g2d = simg.createGraphics();
g2d.drawImage(img, 0, 0, resultDim.width, resultDim.height, null); //#Docs "...is scaled if necessary.".
g2d.dispose();
return simg;
}
/**
* #param resultDim Output dimension (same aspect ratio as the original dimension, and inside containerDim).
* #param originalDim Original dimension.
* #param containerDim Dimension with the maximum width and maximum height.
*/
public static void collapseInside(final Dimension resultDim,
final Dimension originalDim,
final Dimension containerDim) {
resultDim.setSize(originalDim);
if (resultDim.width > containerDim.width) {
//Adjusting height for max width:
resultDim.height = ( resultDim.height * containerDim.width ) / resultDim.width;
resultDim.width = containerDim.width;
}
if (resultDim.height > containerDim.height) {
//Adjusting width for max height:
resultDim.width = ( resultDim.width * containerDim.height ) / resultDim.height;
resultDim.height = containerDim.height;
}
}
private static void createAndShowGUI() {
final JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel label = new JLabel();
label.setSize(new Dimension(5, 5));
label.setIcon(new ImageIcon(bi));
label.setText("Hello World");
label.setHorizontalTextPosition(JLabel.CENTER);
// label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
label.addMouseListener(new MouseListener() {
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == label) {
//Get the mouse click position (in pixels),
//relative to the top-left corner of label:
final Point relativeClickPoint = e.getPoint();
//Obtain alpha value from the TYPE_INT_ARGB pixel:
final int pixel = bi.getRGB(relativeClickPoint.x, relativeClickPoint.y);
final int alpha = bi.getColorModel().getAlpha(pixel);
if (alpha > 0) { //Check if the pixel is not transparent.
if (count % 2 == 0) {
label.setText("Bye");
} else {
label.setText("Hello World");
}
count++;
}
}
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
});
final JPanel singleCenteredComponentJPanel = new JPanel(new GridBagLayout());
singleCenteredComponentJPanel.add(label);
frame.add(singleCenteredComponentJPanel);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Alternatively, you may also override the paintComponent(...) of JPanel and use Graphics.drawImage(...) to paint the Image bi to the JPanel like so:
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
//Drawing the image:
g.drawImage(img, 0, 0, null);
//Drawing the text:
//For centering the text, I used code from:
//https://stackoverflow.com/questions/27706197/how-can-i-center-graphics-drawstring-in-java
final FontMetrics metrics = g.getFontMetrics(g.getFont());
final int x = (img.getWidth(null) - metrics.stringWidth(text)) / 2;
final int y = (img.getHeight(null) - metrics.getHeight()) / 2 + metrics.getAscent();
g.drawString(text, x, y);
}
Where img is bi and text is the text of the button (i.e. "Hello World" and "Bye").
Then add the MouseListener to the JPanel as is.
Complete code for this (as "RoundedButtonDemo1" class, in the same package):
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public final class RoundedButtonDemo1 {
private static BufferedImage bi;
public static void main(String[] args) {
try {
loadImage();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
} catch (IOException e) {
// handle exception
}
}
private static void loadImage() throws IOException {
int newWidth = 80;
int newHeight = 40;
bi = ImageIO.read(RoundedButtonDemo1.class.getResource("/resources/login.png"));
bi = getScaledBufferedImage(bi, newWidth, newHeight);
}
public static BufferedImage getScaledBufferedImage(final Image img,
final int newWidth,
final int newHeight) {
final Dimension originalDim = new Dimension(img.getWidth(null), img.getHeight(null)),
containerDim = new Dimension(newWidth, newHeight),
resultDim = new Dimension();
collapseInside(resultDim, originalDim, containerDim);
final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice gdev = genv.getDefaultScreenDevice();
final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
final BufferedImage simg = gcnf.createCompatibleImage(resultDim.width, resultDim.height, Transparency.TRANSLUCENT);
//Painting input image to output image:
final Graphics2D g2d = simg.createGraphics();
g2d.drawImage(img, 0, 0, resultDim.width, resultDim.height, null); //#Docs "...is scaled if necessary.".
g2d.dispose();
return simg;
}
/**
* #param resultDim Output dimension (same aspect ratio as the original dimension, and inside containerDim).
* #param originalDim Original dimension.
* #param containerDim Dimension with the maximum width and maximum height.
*/
public static void collapseInside(final Dimension resultDim,
final Dimension originalDim,
final Dimension containerDim) {
resultDim.setSize(originalDim);
if (resultDim.width > containerDim.width) {
//Adjusting height for max width:
resultDim.height = ( resultDim.height * containerDim.width ) / resultDim.width;
resultDim.width = containerDim.width;
}
if (resultDim.height > containerDim.height) {
//Adjusting width for max height:
resultDim.width = ( resultDim.width * containerDim.height ) / resultDim.height;
resultDim.height = containerDim.height;
}
}
private static class CustomButton extends JPanel {
private Image img;
private String text;
public void setImage(final Image img) {
this.img = img;
final Dimension imgDim = new Dimension(img.getWidth(null), img.getHeight(null));
setMinimumSize(imgDim);
setPreferredSize(imgDim);
repaint();
}
public Image getImage() {
return img;
}
public void setText(final String text) {
this.text = text;
repaint();
}
public String getText() {
return text;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
//Drawing the image:
g.drawImage(img, 0, 0, null);
//Drawing the text:
//For centering the text, I used code from:
//https://stackoverflow.com/questions/27706197/how-can-i-center-graphics-drawstring-in-java
final FontMetrics metrics = g.getFontMetrics(g.getFont());
final int x = (img.getWidth(null) - metrics.stringWidth(text)) / 2;
final int y = (img.getHeight(null) - metrics.getHeight()) / 2 + metrics.getAscent();
g.drawString(text, x, y);
}
}
private static void createAndShowGUI() {
final JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final CustomButton button = new CustomButton();
button.setSize(new Dimension(5, 5));
button.setImage(bi);
button.setText("Hello World");
//button.setHorizontalTextPosition(SwingConstants.CENTER);
//button.setOpaque(false);
//button.setBorder(BorderFactory.createLineBorder(Color.BLACK));
button.addMouseListener(new MouseListener() {
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == button) {
//Get the mouse click position (in pixels),
//relative to the top-left corner of label:
final Point relativeClickPoint = e.getPoint();
//Obtain alpha value from the TYPE_INT_ARGB pixel:
final int pixel = bi.getRGB(relativeClickPoint.x, relativeClickPoint.y);
final int alpha = bi.getColorModel().getAlpha(pixel);
if (alpha > 0) { //Check if the pixel is not transparent.
if (count % 2 == 0) {
button.setText("Bye");
} else {
button.setText("Hello World");
}
count++;
}
}
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
});
final JPanel singleCenteredComponentJPanel = new JPanel(new GridBagLayout());
singleCenteredComponentJPanel.add(button);
frame.add(singleCenteredComponentJPanel);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Alternatively, if you want normal buttons, istead of labels, it seems here you can reshape buttons as well !
Other than that, I have also tried the following:
1) Using JLayeredPanes. There is a tutorial explaining them, and I guessed they could probably have a method to obtain the order of the visible pane for a given Point, or something like that, but they don't.
2) Using Container.getComponentAt(Point) and Container.findComponentAt(Point) in the MouseListener, but (as I found out after testing) these methods don't "see through" non-opaque (+transparent) pixels.
3) Searching for reshaping or translucency in JPanels (inspired by How to Create Translucent and Shaped Windows), but nothing found.
4) How to Decorate Components with the JLayer Class with first line saying:
... enables you to draw on components and respond to component events without modifying the underlying component directly.
seems promising, but I'm done here.
That's it for me for now. Goodbye.
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);
}
}
});
}
}
What I want my app to do:
1 - Select an area of Image and get the coordinates. This code below should do this:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
public class ScreenCaptureRectangle {
Rectangle captureRect;
ScreenCaptureRectangle(final BufferedImage screen) {
final BufferedImage screenCopy = new BufferedImage(
screen.getWidth(),
screen.getHeight(),
screen.getType());
final JLabel screenLabel = new JLabel(new ImageIcon(screenCopy));
JScrollPane screenScroll = new JScrollPane(screenLabel);
screenScroll.setPreferredSize(new Dimension(
(int)(screen.getWidth()/3),
(int)(screen.getHeight()/3)));
JPanel panel = new JPanel(new BorderLayout());
panel.add(screenScroll, BorderLayout.CENTER);
final JLabel selectionLabel = new JLabel(
"Drag a rectangle in the screen shot!");
panel.add(selectionLabel, BorderLayout.SOUTH);
repaint(screen, screenCopy);
screenLabel.repaint();
screenLabel.addMouseMotionListener(new MouseMotionAdapter() {
Point start = new Point();
#Override
public void mouseMoved(MouseEvent me) {
start = me.getPoint();
repaint(screen, screenCopy);
selectionLabel.setText("Start Point: " + start);
screenLabel.repaint();
}
#Override
public void mouseDragged(MouseEvent me) {
Point end = me.getPoint();
captureRect = new Rectangle(start,
new Dimension(end.x-start.x, end.y-start.y));
repaint(screen, screenCopy);
screenLabel.repaint();
selectionLabel.setText("Rectangle: " + captureRect);
}
});
JOptionPane.showMessageDialog(null, panel);
System.out.println("Rectangle of interest: " + captureRect);
}
public void repaint(BufferedImage orig, BufferedImage copy) {
Graphics2D g = copy.createGraphics();
g.drawImage(orig,0,0, null);
if (captureRect!=null) {
g.setColor(Color.RED);
g.draw(captureRect);
g.setColor(new Color(255,255,255,150));
g.fill(captureRect);
}
g.dispose();
}
public static void main(String[] args) throws Exception {
Robot robot = new Robot();
final Dimension screenSize = Toolkit.getDefaultToolkit().
getScreenSize();
final BufferedImage screen = robot.createScreenCapture(
new Rectangle(screenSize));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ScreenCaptureRectangle(screen);
}
});
}
}
2 - get the coordinates and use it on getSubimage method.
double w = captureRect.getWidth();
double h = captureRect.getHeight();
double x = captureRect.getX();
double y = captureRect.getY();
int W = (int) w;
int H = (int) h;
int X = (int) x;
int Y = (int) y;
BufferedImage selectImg = screen.getSubimage(x, y, w, h);
3 - this code create a new image file and copy the imageselected.
BufferedImage img = new BufferedImage ( 5000, 5000, BufferedImage.TYPE_INT_RGB );
img.createGraphics().drawImage(selectImg, 0, 0, null);
File final_image = new File("C:/Final.jpg");
ImageIO.write(img, "jpeg", final_image);
The idea of app is:
- Select an area of the image.
- Copy that image and paste in other file. ( when I pressed any button )
- The program will continue run until I press another button.
- Every image that I copy the program will paste it beside the last one.
I think I am near to the solution. Can any one help me to "connect the parts" ?
Start by taking a look at:
How to Write a Mouse Listener
How to Use Buttons, Check Boxes, and Radio Buttons
How to Write an Action Listeners
Performing Custom Painting
Writing/Saving an Image
You need to take the concepts you have and rework them into a coherent workable solution. That is, provide functionality between the areas you need (selecting a region and saving the file) so that they work cleanly together...
The following example takes a screenshot, allows you to select an area, click save and the file will be saved. The example checks to see how many files are already in the current directory and increments the count by 1 so you are not overwriting the existing files...
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScreenImage {
public static void main(String[] args) {
new ScreenImage();
}
public ScreenImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
Robot robot = new Robot();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final BufferedImage screen = robot.createScreenCapture(new Rectangle(screenSize));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(screen));
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (AWTException exp) {
exp.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
public TestPane(BufferedImage image) {
this.master = image;
setLayout(new BorderLayout());
final ImagePane imagePane = new ImagePane(image);
add(new JScrollPane(imagePane));
JButton btnSave = new JButton("Save");
add(btnSave, BorderLayout.SOUTH);
btnSave.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
BufferedImage img = imagePane.getSubImage();
master = append(master, img);
File save = new File("Capture.png");
ImageIO.write(master, "png", save);
imagePane.clearSelection();
JOptionPane.showMessageDialog(TestPane.this, save.getName() + " was saved", "Saved", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(TestPane.this, "Failed to save capture", "Error", JOptionPane.ERROR_MESSAGE);
}
}
public BufferedImage append(BufferedImage master, BufferedImage sub) {
// Create a new image which can hold both background and the
// new image...
BufferedImage newImage = new BufferedImage(
master.getWidth() + sub.getWidth(),
Math.max(master.getHeight(), sub.getHeight()),
BufferedImage.TYPE_INT_ARGB);
// Get new image's Graphics context
Graphics2D g2d = newImage.createGraphics();
// Draw the old background
g2d.drawImage(master, 0, 0, null);
// Position and paint the new sub image...
int y = (newImage.getHeight() - sub.getHeight()) / 2;
g2d.drawImage(sub, master.getWidth(), y, null);
g2d.dispose();
return newImage;
}
});
}
}
public class ImagePane extends JPanel {
private BufferedImage background;
private Rectangle selection;
public ImagePane(BufferedImage img) {
background = img;
MouseAdapter ma = new MouseAdapter() {
private Point clickPoint;
#Override
public void mousePressed(MouseEvent e) {
clickPoint = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
int x = Math.min(clickPoint.x, dragPoint.x);
int y = Math.min(clickPoint.y, dragPoint.y);
int width = Math.abs(clickPoint.x - dragPoint.x);
int height = Math.abs(clickPoint.y - dragPoint.y);
selection = new Rectangle(x, y, width, height);
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
public void clearSelection() {
selection = null;
repaint();
}
public BufferedImage getSubImage() {
BufferedImage img = null;
if (selection != null) {
img = background.getSubimage(selection.x, selection.y, selection.width, selection.height);
}
return img;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
if (selection != null) {
Color stroke = UIManager.getColor("List.selectionBackground");
Color fill = new Color(stroke.getRed(), stroke.getGreen(), stroke.getBlue(), 128);
g2d.setColor(fill);
g2d.fill(selection);
g2d.setColor(stroke);
g2d.draw(selection);
}
g2d.dispose();
}
}
}
So apart from rendering the selection the hardest part would be generating the resulting image...
Basically, this done by creating a new BufferedImage and painting the old image and the new, sub, image together.
public BufferedImage append(BufferedImage master, BufferedImage sub) {
// Create a new image which can hold both background and the
// new image...
BufferedImage newImage = new BufferedImage(
master.getWidth() + sub.getWidth(),
Math.max(master.getHeight(), sub.getHeight()),
BufferedImage.TYPE_INT_ARGB);
// Get new image's Graphics context
Graphics2D g2d = newImage.createGraphics();
// Draw the old background
g2d.drawImage(master, 0, 0, null);
// Position and paint the new sub image...
int y = (newImage.getHeight() - sub.getHeight()) / 2;
g2d.drawImage(sub, master.getWidth(), y, null);
g2d.dispose();
return newImage;
}
The example replaces the previous (master) image with the one created here, so it will constantly be appending new images to the end of it...
You need more listeners for button pressed and released.. some lines in the mouseMoved also better placed in mousePressed.
You would want to update your captureRect when you release the mouse (in mouseReleased method).
Then you just write it to the file. You may adjust other things according to your needs.
And for clarity maybe it's better to add a save button into your UI.
public class ScreenCaptureRectangle {
Rectangle captureRect;
Point start = new Point();
SimpleDateFormat sdf;
ScreenCaptureRectangle(final BufferedImage screen) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
final BufferedImage screenCopy = new BufferedImage(
screen.getWidth(),
screen.getHeight(),
screen.getType());
final JLabel screenLabel = new JLabel(new ImageIcon(screenCopy));
JScrollPane screenScroll = new JScrollPane(screenLabel);
screenScroll.setPreferredSize(new Dimension(
(int) (screen.getWidth() / 3),
(int) (screen.getHeight() / 3)));
JPanel panel = new JPanel(new BorderLayout());
panel.add(screenScroll, BorderLayout.CENTER);
JButton btnSave = new JButton("SAVE");
btnSave.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
double w = captureRect.getWidth();
double h = captureRect.getHeight();
double x = captureRect.getX();
double y = captureRect.getY();
int W = (int) w;
int H = (int) h;
int X = (int) x;
int Y = (int) y;
BufferedImage selectImg = screen.getSubimage(X, Y, W, H);
try {
String fName = generateFileName();
if (fName != null) {
File f = new File(fName);
if (f.createNewFile()) {
ImageIO.write(selectImg, "jpg", f);
}
}
} catch (IOException ex) {
Logger.getLogger(ScreenCaptureRectangle.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
panel.add(btnSave, BorderLayout.AFTER_LAST_LINE);
final JLabel selectionLabel = new JLabel(
"Drag a rectangle in the screen shot!");
panel.add(selectionLabel, BorderLayout.SOUTH);
repaint(screen, screenCopy);
screenLabel.repaint();
screenLabel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
Point end = me.getPoint();
captureRect = new Rectangle(start,
new Dimension(end.x - start.x, end.y - start.y));
repaint(screen, screenCopy);
screenLabel.repaint();
selectionLabel.setText("Rectangle: " + captureRect);
}
});
screenLabel.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent me) {
start = me.getPoint();
repaint(screen, screenCopy);
selectionLabel.setText("Start Point: " + start);
screenLabel.repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
int endX = me.getX();
int endY = me.getY();
if (endX > start.x && endY > start.y) {
captureRect = new Rectangle(start.x, start.y, endX-start.x, endY-start.y);
System.out.println("Rectangle of interest: " + captureRect);
}
}
});
JOptionPane.showMessageDialog(null, panel);
}
private String generateFileName() {
return new StringBuilder("screencrop_").append(sdf.format(new Date())).append(".jpg").toString();
}
public void repaint(BufferedImage orig, BufferedImage copy) {
Graphics2D g = copy.createGraphics();
g.drawImage(orig, 0, 0, null);
if (captureRect != null) {
g.setColor(Color.RED);
g.draw(captureRect);
g.setColor(new Color(255, 255, 255, 150));
g.fill(captureRect);
}
g.dispose();
}
public static void main(String[] args) throws Exception {
Robot robot = new Robot();
final Dimension screenSize = Toolkit.getDefaultToolkit().
getScreenSize();
final BufferedImage screen = robot.createScreenCapture(
new Rectangle(screenSize));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ScreenCaptureRectangle(screen);
}
});
}
}
I have a component extends JPanel. It saves itself as bufferedimage on every call of paintComponent method. Component is not completely transparent, only its background. Problem is background is not transparent. I am using setOpaque(false);
Here is my relevant code;
private BufferedImage bufImage = null;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// if first time call
if (bufImage == null) {
int w = this.getWidth();
int h = this.getHeight();
bufImage = (BufferedImage)this.createImage(w, h);
}
g2.drawImage(bufImage, null, 0, 0);
// draw sth
g2.draw(sth);
}
--
I also tried
bufImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
instead of
bufImage = (BufferedImage)this.createImage(w, h);
When i do that; background transperancy works but i can only draw with white color. I have no idea what causes that.
Note:
I used that code to check if it is working;
File outputfile = new File("saved.png");
ImageIO.write(bufImage, "png", outputfile);
saved.png had transparent background but drawings were only white.
This is the component, only lets drawing rectangle with mouse;
class PaintPanel extends JPanel implements MouseListener, MouseMotionListener {
private BufferedImage _bufImage = null;
private boolean dragging = false;
private Point _start = null, _end = null;
public PaintPanel() {
setOpaque(false);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if (_bufImage == null) {
int w = this.getWidth();
int h = this.getHeight();
_bufImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
//_bufImage = (BufferedImage)this.createImage(w, h);
}
g2.drawImage(_bufImage, null, 0, 0);
if (dragging) {
drawCurrentShape(g2);
}
}
private void drawCurrentShape(Graphics2D g2) {
int startx = (int) _start.getX();
int starty = (int) _start.getY();
int stopx = (int) _end.getX();
int stopy = (int) _end.getY();
int width = Math.abs(startx - stopx);
int height = Math.abs(starty - stopy);
int x = startx, y = starty;
if(x > stopx)
x = stopx;
if(y > stopy)
y = stopy;
Rectangle r = new Rectangle(x, y, width, height);
g2.draw(r);
}
public void mousePressed(MouseEvent e) {
dragging = true;
_start = e.getPoint();
_end = _start;
}
public void mouseDragged(MouseEvent e) {
_end = e.getPoint();
this.repaint();
}
public void mouseReleased(MouseEvent e) {
_end = e.getPoint();
if (dragging) {
dragging = false;
drawCurrentShape(_bufImage.createGraphics());
this.repaint();
}
}
public void mouseMoved (MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
}
try this:
bufImage = new BufferedImage(w,h,java.awt.image.BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufImage.createGraphics();
this.print(graphics);
graphics.dispose();
The key is to use print()
Edit: I tried the following and transparency works like a charm:
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test2 {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel();
p.setPreferredSize(new Dimension(400, 400));
p.setOpaque(false);
JButton button = new JButton("Hello world");
p.add(button);
frame.add(p);
frame.pack();
frame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BufferedImage bufImage = new BufferedImage(p.getWidth(), p.getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufImage.createGraphics();
p.print(graphics);
graphics.dispose();
try {
ImageIO.write(bufImage, "png", new File("d:/tmp/tmp.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
createImage(w, h) will create a "blank" image with the specified width and height. That being said, what you'll need to do is invoke createGraphics on the BufferedImage instance and draw directly to the returned Graphics object.