Let me ask my question with a photo :)
The left-hand side picture is the transparent photo in Adobe Photoshop, and the right-hand side is the result in my java program.
I've used the "drawImage" method, in body of a overridden "paint(Graphics g)" method.
How can I make it look transparent?
looks like only 0 or 100 transparency is acceptable here...
Maybe something like this is what you are looking for:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.event.*;
public class TransparentImage extends JPanel
{
private BufferedImage backImage, frontImage;
private float alpha = 1;
public TransparentImage()
{
try
{
// backImage = ImageIO.read(new File("mong.jpg") );
backImage = ImageIO.read(new File("grass.jpg") );
frontImage = ImageIO.read(new File("dukeWaveRed.gif") );
}
catch(Exception e)
{
System.out.println(e);
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(backImage.getWidth(), backImage.getHeight());
}
public void setAlpha(float alpha)
{
this.alpha = alpha;
repaint();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// Paint background image
Graphics2D g2 = (Graphics2D) g;
int x = (getWidth() - backImage.getWidth())/2;
int y = (getHeight()- backImage.getHeight())/2;
g2.drawRenderedImage(backImage, AffineTransform.getTranslateInstance(x, y));
// Paint foreground image with appropriate alpha value
Composite old = g2.getComposite();
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
x = (getWidth() - frontImage.getWidth())/2;
y = (getHeight()- frontImage.getHeight())/2;
g2.drawRenderedImage(frontImage, AffineTransform.getTranslateInstance(x, y));
g2.setComposite(old);
}
private static void createAndShowUI()
{
final TransparentImage app = new TransparentImage();
JSlider slider = new JSlider();
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
JSlider source = (JSlider) e.getSource();
app.setAlpha(source.getValue()/100f);
}
});
slider.setValue(100);
JFrame frame = new JFrame("Transparent Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( app );
frame.add(slider, BorderLayout.SOUTH);
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Provide a background and foreground image. Use the slider to adjust the alpha value of the foreground image.
Related
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);
}
}
});
}
}
Aim: to place JButton on a top of an image that is loaded from a different class.
Problem: only JButton or image can be displayed
//main class
JPanel mPanel.add(new JButton("ddd") );
mPanel.add(drawbg);
class DrawBg extends JPanel {
public Dimension getPreferredSize() {
return new Dimension(1280, 720);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// background
Image img = new ImageIcon(getClass().getResource("/images/test_bg.jpg")).getImage();
int imgX = img.getWidth(null);
int imgY = img.getHeight(null);
g.drawImage(img, (getWidth() - imgX), (getHeight() - imgY), imgX, imgY, null);
}
Edit Ah yes, as ControlAltDel points out, you're not adding your JButton to your drawing JPanel instance. Just do that, and your problem is solved.
I don't see the bug in your code, so it might lie in code not shown. I do see a problem in that you're reading the image in, inside of paintComponent, something that should never be done as it will slow down painting and thus slow down the perceived responsiveness of your program. Also, why re-read in the image with each paint. Instead read it in once.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ButtonOnImg extends JPanel {
public static final String IMG_PATH = "https://duke.kenai.com/gun/Gun.jpg";
private BufferedImage img;
public ButtonOnImg() throws IOException {
URL url = new URL(IMG_PATH);
img = ImageIO.read(url);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
#Override
public Dimension getPreferredSize() {
if (img == null) {
return super.getPreferredSize();
} else {
int w = img.getWidth();
int h = img.getHeight();
return new Dimension(w, h);
}
}
private static void createAndShowGui() {
try {
ButtonOnImg mainPanel = new ButtonOnImg();
mainPanel.add(new JButton("Button!"));
JFrame frame = new JFrame("ButtonOnImg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which displays as:
I'm trying to make a paint program, and this class is the main area where you drag your mouse to paint. The problem is the clip has to be rectangular, so any other lines within that rectangle of the clip (the clip gets bigger the faster you move) will get covered by the new clip, however the new clip isn't all needed.
My ideas of solutions are:
To somehow set the clip to a line (but I think that clip would have to be set in the repaint method, not the setClip() in paint component)
To save the image currently on the paint component and set it to the backgroud
Possibly set the ocupancy of the clip lower in the areas without the line?
Thank you for looking at it, here is the code (with some parts left out for simpler reading) and if you know a solution I would love to hear it. Thanks!
public class Canvas extends JPanel implements MouseMotionListener, MouseListener{
int sizeX, sizeY;
String title;
int[] backColor = new int[3];
int brushSize=20;
Point currentP = new Point();
Point pastP = new Point();
Point paintP = new Point();
int diffX, diffY;
boolean initialize=true;
boolean initClip=true;
Canvas(){
backColor[0] = newProject.colorA;
backColor[1] = newProject.colorB;
backColor[2] = newProject.colorC;
if(backColor[0]>=255){
backColor[0]=255;
}
if(backColor[1]>=255){
backColor[1]=255;
}
if(backColor[2]>=255){
backColor[2]=255;
}
sizeX = newProject.sizeX;
sizeY = newProject.sizeY;
//System.out.println(sizeX + " " + sizeY);
setSize(sizeX,sizeY);
setBackground(Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(brushSize));
if(initialize){
g.setColor(new Color(backColor[0], backColor[1], backColor[2]));
g.fillRect(0, 0, sizeX, sizeY);
g.setColor(Color.red);
g.drawRect(0,0,50,50);
System.out.println("Initialize");
}
else{
g2.drawLine(currentP.x, currentP.y, pastP.x,pastP.y);
}
//System.out.println("Paint");
}
#Override
public void mouseDragged(MouseEvent e) {
if(initClip) //if mouse has been released since last dragged
currentP = e.getPoint(); //This causes PastP and CurrentP to be equal
initClip=false; //since pastP is set equal to CurrentP afterward
pastP = currentP;
currentP = e.getPoint();
diffX=Math.abs(currentP.x-pastP.x); //find the differences to find how big of
diffY=Math.abs(currentP.y-pastP.y); //a clip it needs
if(diffX==0){ //if no movement, set it to brush size so the
diffX=brushSize; //clip shows up
}
if(diffY==0){
diffY=brushSize;
}
initialize=false;
if(currentP.x-pastP.x>0){ //figures out which direction it moved
paintP.x=pastP.x; //sets the clip variable to the correct corner
}
else{
paintP.x=currentP.x;
}
if(currentP.y-pastP.y>0){
paintP.y=pastP.y;
}
else{
paintP.y=currentP.y;
}
System.out.println(paintP);
repaint(paintP.x, paintP.y, diffX, diffY); //repaint with point PaintP and the
//difference it moved
}
#Override
public void mouseReleased(MouseEvent arg0) {
initClip=true;
}
I'm not sure why you would bother. Each time paintComponent is called by the paint system, you are expected to repaint the entire component anyway.
Instead, simply paint what you need to paint, then paint the selection on top of it...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
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.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawSelection {
public static void main(String[] args) {
new DrawSelection();
}
public DrawSelection() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
private Rectangle clipRect;
public TestPane() {
try {
background = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter ma = new MouseAdapter() {
private Point cp;
#Override
public void mousePressed(MouseEvent e) {
cp = e.getPoint();
clipRect = null;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
cp = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
int x = Math.min(p.x, cp.x);
int y = Math.min(p.y, cp.y);
int width = Math.max(p.x, cp.x) - x;
int height = Math.max(p.y, cp.y) - y;
clipRect = new Rectangle(x, y, width, height);
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
}
if (clipRect != null) {
g2d.setColor(UIManager.getColor("List.selectionBackground"));
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(clipRect);
}
g2d.dispose();
}
}
}
If you want to optimise the paint process, why not draw the parts of the image to a backing buffer and simply paint the buffer, then paint the selection or other dynamic parts on top of it within the paintComponent?
So, I have a JFrame with a menu, a toolbar and panel. I load images inside the panel but for some strange reason (to me at least) they are not correctly displayed in the panel. Sometimes they start under the toolbar, sometimes above. Furthermore the image is cut on bottom. The code is a cleaned example from the full one that can be compiled and tested. Thanks in advance.
Class of containing frame:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.zip.ZipInputStream;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
public class SSCE extends JFrame {
private JComicPanel panel;
private JToolBar toolbar;
private JButton buttonZoom;
private JButton buttonPrev;
private JButton buttonNext;
private JMenuBar menuBar;
private Dimension dim;
private BufferedImage img;
private int currentPage;
private JFrame parentFrame;
public SSCE(){
super("JComic");
BorderLayout layout = new BorderLayout();
setLayout(layout);
dim = Toolkit.getDefaultToolkit().getScreenSize();
setSize((int)(dim.width /2.5),dim.height);
this.setPreferredSize(new Dimension((int) (dim.width /2.5),dim.height));
createToolbar();
createPanel();
add(toolbar,BorderLayout.NORTH);
add(panel,BorderLayout.CENTER);
createMenu();
add(menuBar);
setJMenuBar(menuBar);
panel.setVisible(true);
img = null;
pack();
parentFrame = this;
}
private void createPanel(){
Border raisedbevel, loweredbevel;
raisedbevel = BorderFactory.createRaisedBevelBorder();
loweredbevel = BorderFactory.createLoweredBevelBorder();
panel = new JComicPanel(img);
panel.setBorder(BorderFactory.createCompoundBorder(raisedbevel,loweredbevel));
}
private void createToolbar(){
toolbar = new JToolBar();
toolbar.setPreferredSize(new Dimension(dim.width,45));
toolbar.setFloatable(false);
buttonZoom = new JButton("+");
toolbar.add(buttonZoom);
buttonPrev = new JButton("<-");
toolbar.add(buttonPrev);
buttonNext = new JButton("->");
toolbar.add(buttonNext);
toolbar.setBackground(Color.RED);
}
private void createMenu(){
JMenu menuFile,menuJComic;
JMenuItem fileOpen;
JMenuItem quitJComic,aboutJComic;
menuBar = new JMenuBar();
menuJComic = new JMenu("JComic");
aboutJComic = new JMenuItem("About JComic...");
menuJComic.add(aboutJComic);
quitJComic = new JMenuItem("Quit");
quitJComic.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
);
menuJComic.add(quitJComic);
menuBar.add(menuJComic);
menuFile = new JMenu("File");
fileOpen = new JMenuItem("Open...");
fileOpen.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e) {
try {
img = ImageIO.read(new File("superman.jpg"));
currentPage = 1;
int offset = menuBar.getHeight() + toolbar.getHeight();
panel.setImage(img,parentFrame,offset);
}
catch (IOException e1) {
System.out.println(e1.getMessage());
}
}
}
);
menuFile.add(fileOpen);
menuBar.add(menuFile);
}
/**
* #param args
*/
#SuppressWarnings("deprecation")
public static void main(String[] args) {
// TODO Auto-generated method stub
SSCE f = new SSCE();
f.show();
}
}
Class of panel
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class JComicPanel extends JPanel{
private BufferedImage img;
private int offset;
private final float scaling = 0.5f;
public JComicPanel(BufferedImage img){
super();
this.img = img;
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e)
{
repaint();
}
});
}
public void setImage(BufferedImage img,JFrame parentFrame,int offset){
try{
int w = img.getWidth();
int h = img.getHeight();
int newW = (int)(w * scaling);
int newH = (int)(h * scaling);
BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
Graphics2D g = dimg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
this.img = img;
System.out.printf("dim %d x %d",newW,newH);
this.setSize(new Dimension(newW,newH+offset));
parentFrame.pack();
}
catch(Exception e){
}
}
public Dimension getPreferredSize(){
return new Dimension(img.getWidth(),img.getHeight());
}
public void paintComponent(Graphics g){
// Draw our Image object.
super.paintComponent(g);
g.drawImage(img,0,0,getSize().width,getSize().height, this); // at location 50,10
System.out.println("painting 2");
}
}
Image superman.jpg can be found here http://i50.tinypic.com/2yxnc3n.jpg. As you can see the image is below the toolbar, but in my full code it goes also above.
Let's start here...
public void setImage(BufferedImage img,JFrame parentFrame,int offset){
try{
// You're scaling the incoming image, which means you no longer
// have a reference to the original image should you want to
// change that scale...
// You're previous code...
// Don't do this. The parent container's layout manager will simple
// override it, so it's useless...
this.setSize(newW,newH);
repaint();
// This is a bad idea (personally)...
parentFrame.setSize(newW,newH+offset);
} catch(Exception e){
}
}
Calling setSize in this way will temporarily allow the component to assume the size you have set. If you were to call invalidate() after it, the component would actually be re-sized back to meet the requirements of the layout manager.
The larger problem is the fact that you are setting the parent frame's size, but you have no idea of the frame's layout requirements with regards to it's other components, such as the tool bar, menu bar and frame (for example)
This is drawing the image without consideration to the ratio of the original image
g.drawImage(img,0,0,getSize().width,getSize().height, this);
As it has being suggested, after scaling the image, it might be easier to simply set the image as the icon of a JLabel which has been added to the image pane using a BorderLayout
You're assuming that the size of the panel is correct, but it won't be as the panel's layout manager will take over.
This is redundant as Swing will automatically repaint the component when it's resized.
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e)
{
repaint();
}
});
You're best bet is to either drop the image pane into a scroll pane and or rescale the image to fit the size of the pane dynamically.
For example...
Scalable Page
public class ComicPage {
public static void main(String[] args) {
new ComicPage();
}
public ComicPage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
try {
BufferedImage page = ImageIO.read(getClass().getResource("/1346.gif"));
ComicPagePane comicPagePane = new ComicPagePane();
comicPagePane.setComicPage(page);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(comicPagePane);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
}
});
}
public class ComicPagePane extends JPanel {
private BufferedImage comicPage;
private Image scaledInstance;
public void setComicPage(BufferedImage page) {
if (page != comicPage) {
comicPage = page;
scaledInstance = null;
repaint();
}
}
#Override
public void invalidate() {
scaledInstance = null;
super.invalidate();
}
public BufferedImage getComicPage() {
return comicPage;
}
public double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
public double getScaleFactorToFit(BufferedImage img) {
double dScale = 1d;
double dScaleWidth = getScaleFactor(img.getWidth(), getWidth());
double dScaleHeight = getScaleFactor(img.getHeight(), getHeight());
dScale = Math.min(dScaleHeight, dScaleWidth);
return dScale;
}
protected Image getScaledInstance(BufferedImage master) {
if (scaledInstance == null) {
double scaleFactor = getScaleFactorToFit(master);
System.out.println("scaleFactor = " + NumberFormat.getNumberInstance().format(scaleFactor));
// This is not the best scaling alorithm
scaledInstance = master.getScaledInstance(
(int) Math.round(master.getWidth() * scaleFactor),
(int) Math.round(master.getHeight() * scaleFactor), Image.SCALE_SMOOTH);
}
return scaledInstance;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage comicPage = getComicPage();
if (comicPage != null) {
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
// Normally, I would put this into a background worker as this
// operation can be expensive....
Image scaledInstance = getScaledInstance(comicPage);
int x = (width - scaledInstance.getWidth(this)) / 2;
int y = (height - scaledInstance.getHeight(this)) / 2;
g2d.drawImage(scaledInstance, x, y, this);
g2d.dispose();
}
}
}
}
Scrollable Page
public class ScrollableComicPage {
public static void main(String[] args) {
new ScrollableComicPage();
}
public ScrollableComicPage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
try {
BufferedImage page = ImageIO.read(getClass().getResource("/1346.gif"));
ComicPagePage comicPagePane = new ComicPagePage();
comicPagePane.setComicPage(page);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(comicPagePane));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
}
});
}
public class ComicPagePage extends JPanel {
private BufferedImage comicPage;
#Override
public Dimension getPreferredSize() {
return comicPage != null ? new Dimension(comicPage.getWidth(), comicPage.getHeight()) : new Dimension(0, 0);
}
public void setComicPage(BufferedImage page) {
if (page != comicPage) {
comicPage = page;
invalidate();
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (comicPage != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(comicPage, 0, 0, this);
g2d.dispose();
}
}
}
}
You can have a read through Java: maintaining aspect ratio of JPanel background image for more information on scaling techniques.
The technical reason is that you are temporarily adding the menuBar to the center of the contentPane, that is the same logical location as the previously added panel:
add(panel, BorderLayout.CENTER);
// following line is wrong - must be removed!!!
add(menuBar); // no constraint == BorderLayout.CENTER
setJMenuBar(menuBar);
Doing so pushes the panel out off the control of the LayoutManager, but not out of the panel - the net effect is that it has no constraint.
That's not special to being a menuBar (which needs to be added to the layeredPane of the rootPane, not the contentPane - that's why it has its own specialized method setJMenuBar), the same mess would happen with any other arbitrary component at the center.
Apart from that, I would recommend you cleaning up your code:
remove all setXXSize (some reasons)
decide whether the frame should come up at its preferred (using pack) or an arbitrary fixed size (using setSize). The former is the correct thingy to do in most cases, the latter has its use-cases - but doing both doesn't make sense
components are visible by default
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.