To be clear, I've been researching for more than five hours now, I read all the related questions and more than 20 google searches, none of them worked for me and none of them described my case specifically.
First of all here's my code :
import java.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.io.IOException;
import static Debug.StaticVar.*;
/*
<applet code="ImageTest" width=300 height=100>
</applet>
*/
public class ImageTest extends Applet {
Image img;
MediaTracker tracker;
public void init() {
tracker = new MediaTracker(this);
Thread Loader = new Thread(() -> {
img = getImage(getCodeBase(), "1.jpg");
tracker.addImage(img, 1);
});
Loader.setPriority(10);
Loader.start();
}
public void start() {
try {
tracker.waitForAll();
repaint();
} catch (InterruptedException e) {
}
}
public void paint(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
My Problem is the repaint method not calling paint method. To be more specific the paint method executes if I call repaint from another thread, or if I add the paint method to a child class and call repaint but it doesn't work in my code, where I directly call it from the applet main thread. Please HEEEEELP, I'm tired
Related
I am working on a simple 2D game. Each tick, I want to check an effects queue that will start a thread for a certain effect(fading transitions, audio fade in and out, etc). For example, pressing "Play" on the menu screen will add a "FadeOut" message to this queue, which will be processed and start a thread to draw a black rectangle with an increasing alpha value over my GamePanel.
I'm overriding paintComponent() and sending my Graphics object to my GameStateManager, which passes along the Graphics object to the current states' draw(). I currently don't have an effects state (which maybe I should) to route the paintComponent() graphics object to, but I do pass my gamepanel to my effects thread, where I can use getGraphics() to draw on it. Drawing a rectangle to the GamePanel directly just causes flickering, as the gameloop is still rendering the game.
I found I can draw a black rectangle with increasing alpha to a BufferedImage, set the composite to AlphaComposite.Src (which causes the new draw to replace the old) then draw the BufferedImage over the game panel. The problem is the BufferedImages drawn to the game panel don't get overridden each draw, so the fade out happens really quickly because these black BufferedImages of various alphas just stack on each other.
I wrote this short program to test composite settings and see what is getting overridden. All drawing is done in the draw(), which would be my run() in the effects thread.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ScratchPad extends JPanel implements Runnable
{
private JFrame oFrame = null;
private Thread oGameThread = null;
private Graphics2D oPanelGraphics = null;
private Graphics2D oImageGraphics = null;
private BufferedImage oImage = null;
public static void main(String args[]) throws Exception
{
new ScratchPad();
}
public ScratchPad()
{
createFrame();
initPanel();
addAndShowComponents();
oGameThread = new Thread(this, "Game_Loop");
oGameThread.start();
}
private void addAndShowComponents()
{
oFrame.add(this);
oFrame.setVisible(true);
}
private void initPanel()
{
this.setOpaque(true);
this.setBackground(Color.cyan);
}
private void createFrame()
{
oFrame = new JFrame("Fade");
oFrame.setSize(700, 300);
oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
oFrame.setLocationRelativeTo(null);
}
public void run()
{
oImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
while(true)
{
try
{
draw();
Thread.sleep(100);
}
catch(InterruptedException e)
{
}
}
}
private void draw()
{
oPanelGraphics = (Graphics2D)this.getGraphics();
oImageGraphics = oImage.createGraphics();
oImageGraphics.setComposite(AlphaComposite.Src);
oImageGraphics.setColor(new Color(0,0,0,90));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 10, 10, null);
oImageGraphics.setColor(new Color(0,0,0,60));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 220, 10, null);
oImageGraphics.setColor(new Color(0,0,0,30));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 430, 10, null);
// Drawing this image over location of first image, should overwrite first
// after setting composite to 'Src'
oPanelGraphics.setComposite(AlphaComposite.Src);
oImageGraphics.setColor(new Color(0,0,0,10));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 10, 10, null);
oImageGraphics.dispose();
oPanelGraphics.dispose();
}
} // end class
What's interesting is setting the composite on 'oPanelGraphics' causes any alpha to the BufferedImage to go away, resulting in a fully opaque black image being drawn over the image that was previously there. Even setting the color to something other than black doesn't have an effect.
What's also interesting is setting the composite for the BufferedImage to:
oImageGraphics.setComposite(AlphaComposite.SrcIn);
causes nothing to be shown. The Oracle documentation on compositing graphics in Java2D states this for 'SrcIn':
"If pixels in the source and the destination overlap, only the source pixels in the overlapping area are rendered."
So, I would expect this to have the same behavior I get with AlphaComposite.Src.
Maybe someone out there can shed some light on whats going on with these composites, and how I could achieve my desired effect.
There are a number issues with what you "seem" to be trying to do
Don't call getGraphics on a component. This can return null and only returns a snapshot of what was last painted during a Swing paint cycle. Anything you paint to it will be erased on the next paint cycle
You should also never dispose of Graphics context you did not create, doing so could effect other components that are painted by Swing
Painting is compounding, this means that painting to the same Graphics context (or BufferedImage) over and over again, will continue to apply those changes over the top of what was previously painted
You also don't seem to have a concept of how animation should work. Instead of trying to paint your fade effect in a single pass, where the results can't be applied to the screen, you need to apply a phase on each cycle and allow that to be updated to the screen before the next pass runs.
The following is a really basic example of what I'm talking about. It takes a "base" image (this could be the "base" state of the game, but I've used a static image) and the paints effects over the top.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
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.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Engine engine;
private Image frame;
public TestPane() {
engine = new Engine();
engine.setEngineListener(new EngineListener() {
#Override
public void updateDidOccur(Image img) {
frame = img;
repaint();
}
});
engine.start();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
engine.addEffect(new FadeOutEffect(Color.BLACK));
}
});
}
#Override
public Dimension getPreferredSize() {
return engine.getSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (frame != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(frame, 0, 0, null);
g2d.dispose();
}
}
}
public interface EngineListener {
public void updateDidOccur(Image img);
}
public class Engine {
// This is the "base" image, without effects
private BufferedImage base;
private Timer timer;
private EngineListener listener;
private List<Effect> effects = new ArrayList<Effect>(25);
public Engine() {
try {
base = ImageIO.read(new File("/Volumes/Big Fat Extension/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int width = base.getWidth();
int height = base.getHeight();
BufferedImage frame = new BufferedImage(width, height, base.getType());
Graphics2D g2d = frame.createGraphics();
g2d.drawImage(base, 0, 0, null);
Iterator<Effect> it = effects.iterator();
while (it.hasNext()) {
Effect effect = it.next();
if (!effect.applyEffect(g2d, width, height)) {
it.remove();
}
}
g2d.dispose();
if (listener != null) {
listener.updateDidOccur(frame);
}
}
});
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
public void addEffect(Effect effect) {
effects.add(effect);
}
public void setEngineListener(EngineListener listener) {
this.listener = listener;
}
public Dimension getSize() {
return base == null ? new Dimension(200, 200) : new Dimension(base.getWidth(), base.getHeight());
}
}
public interface Effect {
public boolean applyEffect(Graphics2D context, int width, int height);
}
public class FadeOutEffect implements Effect {
private int tick = 0;
private Color fadeToColor;
public FadeOutEffect(Color fadeToColor) {
this.fadeToColor = fadeToColor;
}
#Override
public boolean applyEffect(Graphics2D context, int width, int height) {
tick++;
float alpha = (float) tick / 100.0f;
if (alpha > 1.0) {
return false;
}
Graphics2D g2d = (Graphics2D) context.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.setColor(fadeToColor);
g2d.fillRect(0, 0, width, height);
g2d.dispose();
return true;
}
}
}
Remember, every effect or change should be applied within the same "main loop", this means you shouldn't have multiple threads, in fact, since Swing is not thread safe, you should avoid having any additional threads if possible. This example make use of a Swing Timer to act as the "main loop" because the ActionListers actionPerformed method is called within the context of the EDT, making it safe to update the UI from. It also provides a simple synchronisation method, as the UI can't be painted while the actionPerformed method is been called
I am trying to create a program using applets in NetBeans and I am having a problem. Whenever I try to run a program it says "Project does not have main class set". I was able to get around this by adding
public static void main (String[] args) but my program doesn't do anything. I tried just a generic program to see if it was my coding and it still didn't work. Do I need to reset my NetBeans or is it a coding error?
Here is the code I got from the Oracle website that doesn't work
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
public class HelloWorld extends JApplet {
//Called when this applet is loaded into the browser.
public void init() {
//Execute a job on the event-dispatching thread; creating this applet's GUI.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
JLabel lbl = new JLabel("Hello World");
add(lbl);
}
});
} catch (Exception e) {
System.err.println("createGUI didn't complete successfully");
}
}
}
This is the code for my program:
package robot;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JApplet;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.lang.Object;
public class Robot extends JApplet {
private Rectangle rect;
public void init(){
rect = new Rectangle (0, 0, 20,20);
this.addKeyListener((KeyListener) this);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.white);
g.fillRect(0,0,500,500);
{
this.setSize(355, 355);
}
Graphics2D g1 = (Graphics2D) g;
g1.setColor(Color.black);
g1.fill(rect);
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode()== KeyEvent.VK_UP) {
rect.setLocation ( rect.x,rect.y=+20 );
repaint();
}
if (e.getKeyCode()== KeyEvent.VK_DOWN) {
rect.setLocation ( rect.x,rect.y=-20 );
repaint();
}
if (e.getKeyCode()== KeyEvent.VK_RIGHT) {
rect.setLocation ( rect.x =+20, rect.y );
repaint();
}
if (e.getKeyCode()== KeyEvent.VK_LEFT) {
rect.setLocation ( rect.x =-20,rect.y);
repaint();
}
}
}
I was able to run the program if I right clicked in my code then hit "Run Project" but whenever I try and use the run project button it doesn't work.
The second way used to run it simply isn't valid for an applet. That is for an application.
You can set main class by left click on project -> run and select main class:
I made the following code to move a rectangle using arrowkeys of keyboard. The "keyPressed" function does not seem to be working properly.Infact, i don't think it is even getting called when a key is pressed bcz when i tried to print some text when a key is pressed, it was not getting printed.All i see in the output window is a stationary rectangle fixed at the top left corner of the window.Here is my code....pls help me...i need it desperately
import javax.swing.JFrame;
public class Main
{
public static void main(String args[])
{
JFrame window=new JFrame();
window.setSize(600,400);
window.setTitle("window");
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingComponent DC=new drawingComponent();
window.add(DC);
}
}
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComponent;
import javax.swing.Timer;
public class drawingComponent extends JComponent implements ActionListener,KeyListener
{
Timer t=new Timer(2000,this);//moving after 5 milliseconds
static int x=0;
static int y=0;
private static int velx=0;
private static int vely=0;
public drawingComponent()
{
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
System.out.println("tr1");
}
public void paintComponent(Graphics g)
{
Graphics2D g2=(Graphics2D) g;
Rectangle rect1=new Rectangle(x,y,50,30);
g2.setColor(Color.RED);
g2.fill(rect1);
System.out.println("tr2");
}
public void actionPerformed(ActionEvent e) //inbuilt fncn f actionListener(interface) which needs to be created
{
x+=velx; //changing values
y+=vely;
System.out.println("tr3");
repaint(); //inbuilt fncn to repeat the paintComponent method
}
public void keyPressed(KeyEvent e)
{
int code=e.getKeyCode();
if(code==KeyEvent.VK_UP)
{ velx=0; vely=-1;repaint(); }
if(code==KeyEvent.VK_DOWN)
{ velx=0; vely=1; repaint(); }
if(code==KeyEvent.VK_LEFT)
{vely=0; velx=-1; repaint(); }
if(code==KeyEvent.VK_RIGHT)
{vely=0; velx=1; repaint();}
}
public void keyTyped(KeyEvent e)
{}
public void keyReleased(KeyEvent e)
{}
}
Welcome to the wonderful world of KeyListeners...
While you have set the component focusable, you've not requested that the component be focused.
You could try calling requestFocusInWindow, but he this raises the question of when to call it.
You could call it within the constructor, but because the component doesn't belong to a valid visible component yet, the call may fail. You could override the components addNotify method and add the call to it, after you call super.addNotify, but the requestFocusInWindow method doesn't gurentee that focus will be given the component
Instead, you could simply avoid all this hassle and use the key bindings API instead, which will give you control over the level of focus require for key events to be triggered
As a side note, you should call setVisible on your frame after you've set up the UI completely
Hi I have googled and can't figured out why my paintComp method isnt being called
I have the following code
package com.vf.zepto.view;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import com.vf.zepto.view.interfaces.ProcessorPanel;
public class CountryDetailsPanel extends JPanel implements ProcessorPanel, Runnable {
private GridBagConstraints c = new GridBagConstraints();
private String countryName;
private Properties prop = new Properties();
private BufferedImage image;
public CountryDetailsPanel() {
try {
prop.load(new FileInputStream("country.props"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//this.setLayout(new GridBagLayout());
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(5, 5, 5, 5);
this.setPreferredSize(new Dimension(200, 200));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
try {
if(countryName != null) {
String asset = prop.getProperty(countryName+".flag");
if(!asset.equals(null)) {
image = ImageIO.read(new File(asset));
g.drawImage(image, 0, 0, null);
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void updateDetails(Object o) {
countryName = (String)o;
SwingUtilities.invokeLater(this);
}
#Override
public void run() {
this.repaint();
}
}
and when calling this.repaint() expect the paintComponent method to be called but for love nor money it isnt.
Have tried to force it to use the EDT incase that was the issue but its not.
any ideas?
You should never call paintComponent() the repaint() method consolidates all requests to change the component (there may be several repaint requests between screen refreshes). It adds an update request to the GUI event queue so that the update will be properly coordinated with other GUI actions (Swing and AWT are not thread-safe). This update request, when processed, calls update(), which calls paint(), which calls your paintComponent()
Why have this:
#Override
public void run() {
this.repaint();
}
It does not seem of any use (creating a new thread to repaint once? Not to mention the thread is not on EDT rather call repaint() on the JPanel instance (if modified externally other than that you shouldnt even worry). However to start a thread which modifeis UI componets use s SwingTimer/SwingWorker or SwingUtilities#invokeXXX()
This might not be related but in your code I see this:
if(!asset.equals(null)) {
image = ImageIO.read(new File(asset));
g.drawImage(image, 0, 0, null);
}
dont use equals() to compare to a null value as this might throw a NullPointerException because you are attempting to
deference a null pointer, for example this code throws a NPE:
String s=null;
if(!s.equals(null)) {//throws NPE
System.out.println("Here");//is never printed
}
Also as mKorbel has said (+1 to him) dont do long running tasks in paintComponent() declare your Image globally, assign it in the constructor and then use it in paintComponent(). F.i
rather do:
public class TestPanel extends JPanel {
private Image image;
public TestPanel() {
image = ImageIO.read(new File(asset));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(asset!=null) {
g.drawImage(image, 0, 0, null);
}
}
}
Do not load image or another hard or long running code in paintComponent
Load this Object as a local variable and only one time
paintComponent() is called
implicitly, when JComponent requires repaint, or
explicitly, for example on every of mouse event if one were to invoke paintComponent() from a MouseMotionListener.
If there is animation, then to use Swing Timer and call repaint().
In the following code, I call JOptionPane.showMessageDialog, inside a try/catch block. But when the error is caught, my JOptionPane is visible but without any message !!! Does someone knows why and how I can correct the problem ?
Regards
MyBoardJPannel.java
package experimentations.gui;
import java.awt.Graphics;
import java.awt.Image;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class MyBoardPannel extends JPanel {
#Override
public void paint(Graphics grahics) {
if (imageToShow == null)
imageToShow = loadImage("sampleImage");
}
/**
* In fact, there are not any image in project => will go to catch clause.
* #param imageName
*/
private void loadImage(String imageName) {
InputStream imageStream = getClass().getResourceAsStream("/"+imageName+".png");
try {
imageToShow = ImageIO.read(imageStream);
}
catch (Exception e) {
String errorMessage = "Failed to load image "+imageName;
System.err.println(errorMessage);
JOptionPane.showMessageDialog(this, errorMessage,
"Image loading error", JOptionPane.ERROR_MESSAGE);
imageToShow = null;
System.exit(1);
}
}
private Image imageToShow;
}
JOptionPaneErrorShowing.java
package experimentations.gui;
import javax.swing.JFrame;
public class JOptionPaneErrorShowing extends JFrame {
public JOptionPaneErrorShowing(){
setTitle("JOptionPane experimentation");
setSize(300, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
add(new MyBoardPannel());
}
/**
* #param args
*/
public static void main(String[] args) {
new JOptionPaneErrorShowing().setVisible(true);
}
}
It's likely a Swing concurrency issue. But more importantly, you should never load an image from within a paint or paintComponent method, ever. Read it in the constructor or elsewhere but paint/paintComponent need to be lean and blazingly fast.
To solve your issue, consider reading in the image in SwingWorker object. If you call a JOptionPane from within the SwingWorker's doInBackground method though, be sure to call it on the Swing event thread, the EDT, using SwingUtilities.invokeLater(Runnable).
Also, you will hardly ever want to draw in a JPanel's paint method unless you are taking care of painting borders and children. Instead paint in a paintComponent method, and don't forget to call the super.paintComponent(g) method in that paintComponent override. You'll want to read the Swing graphics tutorials as this is all spelled out there.
For example:
import java.awt.Graphics;
import java.awt.Image;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class MyBoardPannel extends JPanel {
protected static final String SAMPLE_IMAGE = "sampleImage";
Image imageToShow = null;
public MyBoardPannel() {
SwingWorker<Image, Void> mySW = new SwingWorker<Image, Void>() {
#Override
protected Image doInBackground() throws Exception {
return loadImage(SAMPLE_IMAGE);
}
#Override
protected void done() {
try {
imageToShow = get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
mySW.execute();
}
#Override
public void paintComponent(Graphics grahics) {
super.paintComponent(grahics);
if (imageToShow != null) {
grahics.drawImage(imageToShow, 0, 0, null);
}
}
private Image loadImage(String imageName) {
InputStream imageStream = getClass().getResourceAsStream(
"/" + imageName + ".png");
try {
return ImageIO.read(imageStream);
} catch (Exception e) {
final String errorMessage = "Failed to load image " + imageName;
System.err.println(errorMessage);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(MyBoardPannel.this, errorMessage,
"Image loading error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
});
}
return null;
}
}
I don't really know, but maybe your panel you use as parent of the JOptionPane (by passing this) is invisible or there is something else wrong. Try adding pack(); at the end of your JOptionPaneErrorShowing constructor.
What I know is that I had this problem when I was using an old Ubuntu and old Nvidia driver for my GPU, when the desktop effects were turned on (the Compiz Fusion of today. I don't know if it was already called Compiz, that long ago).
Aha! I found it, you are displaying the error inside the repaint method. Never do that! Load your image inside the constructor of the MyBoardPanel class and show error messages over there.