I discovered the Robot class yesterday, and thought it was pretty cool. Today I wanted to experiment with it and see what was possible; so I decided I wanted to make a program that took a screenshot of the entire screen, and rendered out an image pixel by pixel on a JPanel. I have the program finished (two classes), but it isn't working and I can't find out why (I HAVE looked over the code a few times). Here's the code:
(FIRST CLASS)
import java.awt.AWTException;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class One {
public static void main(String[] args) {
BufferedImage screenCap = null;
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
try {
screenCap = new Robot().createScreenCapture(screenRect);
Two imageRenderer = new Two(screenCap, screenRect);
imageRenderer.doRender();
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(imageRenderer);
frame.pack();
} catch (AWTException e) {
e.printStackTrace();
}
}
}
(SECOND CLASS)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class Two extends JPanel {
private BufferedImage screenCap;
private Rectangle screenRect;
private Color pixelRGB;
//c1 and c2 are the x and y co-ordinates of the selected pixel.
private int c1, c2;
public Two(BufferedImage sC, Rectangle rect) {
screenCap = sC;
screenRect = rect;
setPreferredSize(new Dimension(rect.width, rect.height));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
g.setColor(pixelRGB);
g.drawRect(c1, c2, 1, 1);
}
public void doRender() {
for(int i=0; i<screenRect.width; i++) {
for(int j=0; j<screenRect.height; j++) {
pixelRGB = new Color(screenCap.getRGB(i, j));
c1 = i;
c2 = j;
repaint();
}
}
}
}
I have googled around this problem to no avail.
Can anyone tell me what I'm doing wrong?
In order to make it work, just replace your paintComponent() method in your class Two with the following:
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
g.drawImage(screenCap, 0, 0, getWidth(), getHeight(), null);
}
You can also get rid of the doRender() method.
Two should probably be an instance of a JLabel that is displaying screenCap. E.G.
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Screenshot {
public static void main(String[] args) throws Exception {
Rectangle screenRect = new Rectangle(
Toolkit.getDefaultToolkit().getScreenSize());
final BufferedImage screenCap =
new Robot().createScreenCapture(screenRect);
Runnable r = new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(null, new ImageIcon(screenCap));
}
};
SwingUtilities.invokeLater(r);
}
}
Drop it in a scroll pane if you wish to be really neat about it. Batteries not included.
In case it is not obvious: A JOptionPane uses a JLabel to render an ImageIcon.
Each time you repaint, you paint white over the whole panel, then do only a single pixel. So after each one, you'll only get one pixel. It also shouldn't be necessary to call repaint many times. In fact, when you call repaint, it does not immediately actually call paintComponent. It simply submits a request to repaint, which swing will eventually do. And it might not be one-to-one. E.g., many calls to repaint might result in only one call to paintComponent. You should try to write code so that a single call to paintComponent will completely display the component.
To do this, you can use g.drawImage to display a BufferedImage. See this post for more information on displaying an image in a JPanel.
Related
I want to have an image fade in onto a panel that is part of a card layout. When i'm at a certain place in the program, this panel will show on top and I then want the image to be loaded in with a fade-in effect.
This is a big project so I will only paste the relevant code.
I have a GUI class which contains the jFrame, all the jPanels etc. When a certain event is triggered, this code runs:
cardMain.show(pMain, "cLeprechaun");
FadeIn.run(pLeprechaun);
It loads up the correct panel and then runs a static method in the FadeIn class, that is supposed to add the image onto the panel pLeprechaun.
Here is the FadeIn class:
import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class FadeIn extends JPanel implements ActionListener {
Image imagem;
Timer timer;
private float alpha = 0f;
public FadeIn() {
imagem = new ImageIcon("darkforest.jpg").getImage();
timer = new Timer(100, this);
timer.start();
}
// here you define alpha 0f to 1f
public FadeIn(float alpha) {
imagem = new ImageIcon("darkforest.jpg").getImage();
this.alpha = alpha;
}
#Override
public void paintComponent(Graphics g) {
System.out.println("paint");
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
alpha));
g2d.drawImage(imagem, 0, 0, null);
}
public static void run(JPanel jPanel) {
jPanel.add(new FadeIn());
}
public void actionPerformed(ActionEvent e) {
alpha += 0.05f;
if (alpha >1) {
alpha = 1;
timer.stop();
}
repaint();
}
}
Nothing happens, no image is show on the panel. Just to try it out, instead of jPanel.add(new FadeIn()); I have also tried to create a new jFrame and adding a new FadeIn onto that, and it works then. Of course, instead of an image being painted on the jPanel, a new jFrame pops up ontop of the main one, with the image nicely fading in. But that's not what I want happening.
Is there a way to solve this?
Er go:
assuming you have the following sequence of calls:
JFrame f=new JFrame();
f.setSize(500, 500);
JPanel j=new JPanel();
f.add(j);
f.setVisible(true);
FadeIn.run(j);
you need to change the size as follows:
public FadeIn(Dimension d) {
setSize(d);
.... // the rest of set up
}
then in run:
run(Jpanel jPanel) {
jPanel.add(new FadeIn(jPanel.getSize()));
}
Hi I want to make a window, a GUI, and put an image in it.
I watched a YT tutorial (https://www.youtube.com/watch?v=Ap20Qw77TzY) and copied everything similar but the Window I make has no image at all. I tried different file types like .jpg and different window sizes, matching the picture size but it doesn't help.
That's my code, I get no real errors, except a warning of:
The serializable class main does not declare a static final serialVersionUID field of type long,line 8
This method has a constructor,line 25
Code
package main;
import java.awt.Graphics;
import java.awt.Toolkit;
import javax.swing.*;
public class main extends JFrame {
/**
* author jan
*/
public main(String title){
super (title);
}
public void paint(Graphics gr) {
super.paint(gr);
gr.drawImage(Toolkit.getDefaultToolkit().getImage("Koppenhagen\\Pictures\\Herz.png"), 0, 0, this);
}
public static void main(String[] args) {
main window = new main("Mein Test!");
window.setSize(160,160);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
Use ImageIO.read over Toolkit.getImage, it will throw a IOException of the image can't be load for some reason
Check the location of the image. Your example is looking for a file in Koppenhagen\\Pictures, relative to the execution context of the program. You could use File#exists to check if the file actually exists where you think it is
Don't load resources within the any paint method, loading images can take time and painting should run as fast as possible
I'd discourage you from overriding paint of top level containers like JFrame. A JFrame contains a JRootPane, which contains, amongst other things, a contentPane all of which can be painted independently of its parent container. Instead, start with a JPanel and override its paintComponent method instead, then add this to an instance of JFrame
Here's a simple Swing application that draws an image.
You have to put the image in the same directory as the Java code.
package com.ggl.testing;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class DrawImage implements Runnable {
#Override
public void run() {
JFrame frame = new JFrame("Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane(new ImagePanel(getImage()));
frame.add(scrollPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private Image getImage() {
try {
return ImageIO.read(getClass().getResourceAsStream(
"StockMarket.png"));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawImage());
}
public class ImagePanel extends JPanel {
private static final long serialVersionUID = -2668799915861031723L;
private Image image;
public ImagePanel(Image image) {
this.image = image;
this.setPreferredSize(new Dimension(image.getWidth(null), image
.getHeight(null)));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
}
So I am trying to get into simple animations and virtual physics and whatnot. I am trying to animate a ball so that it slowly grows as time passes by. The code I have here is pretty much exactly as it is in a Java For Dummies book I have with the exception of a few things such as: getting rid of constants for the size of the applet (this.setSize(500, 500) vs this.setSize(WIDTH, HEIGHT) and declaring WIDTH and HEIGHT earlier). The changes were simple and would not effect the program. (I would know as I've taken a Java course in school). Anyway, I'm starting here with Applets and I can't get the program to run past two iterations. Down in the paint function I have a System.out.println(d) to check how many times the diameter of the ellipse grows. However the only output I see is "21" then "22". The applet continues to run via the applet viewer however nothing else is printed even though it should continue to grow. Anyone know what's wrong?
As a side note I should mention I am using NetBeans 7.2 and selecting "Run File" to run it.
package GraphicsTesting;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.applet.*;
import java.util.concurrent.*;
public class Main extends JApplet
{
private PaintSurface canvas;
#Override
public void init()
{
this.setSize(500,500);
canvas = new PaintSurface();
this.add(canvas, BorderLayout.CENTER);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(3);
executor.scheduleAtFixedRate(new AnimationThread(this), 0L, 20L, TimeUnit.MILLISECONDS);
}
}
class AnimationThread implements Runnable
{
JApplet c;
public AnimationThread(JApplet C)
{
this.c = c;
}
public void run()
{
c.repaint();
}
}
class PaintSurface extends JComponent
{
int d = 20;
#Override
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint
(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
d+=1;
System.out.println(d);//This is to test
Shape ball = new Ellipse2D.Float(200, 200, d, d);
g2.setColor(Color.RED);
g2.fill(ball);
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package javaapplication3;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.Timer;
import javax.swing.JApplet;
import javax.swing.JComponent;
public class Main extends JApplet {
private PaintSurface canvas;
private Timer timer;
#Override
public void init() {
this.setSize(500, 500);
canvas = new PaintSurface();
this.add(canvas, BorderLayout.CENTER);
// ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(3);
// executor.scheduleAtFixedRate(new AnimationThread(this), 0L, 20L, TimeUnit.MILLISECONDS);
timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
canvas.repaint();
}
});
timer.start();
}
}
class PaintSurface extends JComponent {
int d = 20;
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
d += 1;
System.out.println(d);//This is to test
Shape ball = new Ellipse2D.Float(0, 0, d, d);
g2.setColor(Color.RED);
g2.fill(ball);
}
}
You are calling repaint() on a thread that is not the Event Dispatch Thread
so the UI is not updated. There are other ways to do this, but internally javax.swing.Timer calls the actionPerformed method inside the Event Dispatch Thread so the UI is updated.
UPDATE: You could see the applet in action using java webstart: https://tetris-battle-bot.googlecode.com/files/launch.jnlp
The above answer does work. However, looking at your original code there is one little itty bitty misconception that, it appears, neither of you caught. In the constructor of the animation thread you have JApplet C as a parameter rather than JApplet c. To clarify, you accidently capitalized the c. The capitlization of the C caused you to set this.c = c which basically assigned it to itself. It wasn't needed to rewrite the entire code at all.
Consider this small runnable example:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test2 extends JFrame implements MouseWheelListener{
ArrayList<JLabel> lista = new ArrayList<JLabel>();
JPanel p;
double d = 0.1;
Test2(){
p=new JPanel();
_JLabel j = new _JLabel("Hello");
j.setOpaque(true);
j.setBackground(Color.yellow);
p.add(j);
p.setBackground(Color.blue);
add(p);
this.setVisible(true);
this.setSize(400,400);
addMouseWheelListener(this);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String args[]){
new Test2();
}
private class _JLabel extends JLabel{
_JLabel(String s){
super(s);
}
protected void paintComponent(Graphics g) {
d+=0.01;
Graphics2D g2d = (Graphics2D) g;
g2d.scale(d, d);
setMaximumSize(null);
setPreferredSize(null);
setMinimumSize(null);
super.paintComponent(g2d);
System.out.println("d= " +d);
}
}
public void mouseWheelMoved(MouseWheelEvent e) {
this.repaint();
}
}
When I scroll the mousewheel the JLabel increases in size and the variable d is printed out. However, when it reaches the actual size (d=1) only the text continues zooming. How can I make the background continue to zoom?
You shouldn't be modifying the preferred/min/max sizes in the paint method, this coud have unexpected results (cause another repaint).
The problem is that the parent layout has no reference from which to determine size of the component. That is, the preferred/in/max size is actually calculated based on the font information & this information is not been changed.
So, while it "appears" that the component is being resized, it's actual size has not changed.
Try instead to scale against the original font size.
AffineTransformation af = AffineTranfrmation.getScaleInstance(scale, scale);
Font font = originalFont.deriveFont(af);
setFont(font);
invalidate();
repaint();
Of course you run into the problem of what happens if the user changes the font, but with a little bit of flagging, you should be able to over come that
This is my first non-school related program. I have a few questions that you guys can hopefully answer with ease. I have 3 questions. How can I add my button to my JFrame even though it's in a different class than the button?
Also, how would I go about making my shape and ten others like it about a quarter second after each other so I had a line of them.
Then, how would I force them to follow a predetermined path that scales to somebody dragging the box around?
Thanks a lot for reading and helping me out guys. Here are my three classes:
gameRunner.java
import javax.swing.JFrame;
public class gameRunner {
public static void main(String args []){
Enemy e = new Enemy();
Buttons b = new Buttons();
JFrame f = new JFrame();
f.add(b);
f.add(e);
f.setVisible(true);
f.setSize(1300, 700);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Tower Defense");
}
}
Enemy.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Enemy extends JPanel implements ActionListener {
Timer t = new Timer(5, this);
double x = 0;
double y = 0;
double velX = 3;
double velY = .5;
int health = 10;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D square = new Rectangle2D.Double(x, y, 10, 10);
g2.fill(square);
t.start();
}
public double adjustHorizontalSpeed() {
y += velY;
return y;
}
public double adjustVerticalSpeed() {
x += velX;
return x;
}
public void actionPerformed(ActionEvent e) {
adjustHorizontalSpeed();
adjustVerticalSpeed();
repaint();
}
}
Buttons.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Buttons extends JFrame implements ActionListener{
private JButton shoot;
public Buttons(){
shoot = new JButton("Shoot!");
shoot.setBounds(50,60,50,100);
shoot.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
Buttons shouldn't extend JFrame if all you want to do is use it to create a JButton that you wish to add to another GUI. Instead perhaps give it a public method called getShoot() that returns the button created:
public JButton getShoot() {
return shoot;
}
Next, to do things in a timed fashion, you should use a Swing Timer. The tutorials will tell you how to do this: How to use Swing Timers
Next, you'll want to read the Swing tutorial section on how to use layout managers so you can add a complex mix of components to the GUI and have them all fit well together: Laying out Components in a Container
Finally, as for this:
Then, how would I force them to follow a predetermined path that scales to somebody dragging the box around?
You'll have to describe this better for me to understand what you're trying to do.
For the predetermined path, you should probably have them move/size themselves proportional to the containing pane. With a layout manager, assuming they are inside their own JPanel, the pane should scale automatically, so when the window is resized, the shapes will resize and move properly.