Java, AWT Image not Appearing

Started by
7 comments, last by BKrenz 8 years, 6 months ago

Not too sure this was the right place for this.

Code Repo Here

I'm writing under the WorldReconfiguration branch.

I'm refactoring and rewriting bits of code for someone else. They were originally using AWT for their rendering basis. I've taken their code, and mostly abstracted it so that I can start moving things over to LWJGL or LibGDX painlessly in the near future. Couple issues left there, but I think it's mostly working.

Anyways, I almost finished the basis to the AWTRenderer, but have run into an issue. I'm attempting to render an image onto the panel, though it's not appearing. The code is running, not throwing any errors, but I can't figure out why it's not actually drawing.

The code in question is this couple of lines (located here) :



Graphics l_Graphics = ((AWTRenderer) p_Renderer).getGraphics();
l_Graphics.drawImage( this.m_Image , p_XCoord, p_YCoord, 
	GameOptions.TILE_SIZE, GameOptions.TILE_SIZE, null);

Whenever I currently go to draw a scene, I tell the current GameScreen to draw, which calls the AWTRenderer and it repaints, which should tell the GameScreen to start drawing everything with the reference to the Renderer. The GameScreen tells the World to do its drawing, which currently passes the reference to a TileRegion, which passes the Reference to each Tile. The Tile then gets its GraphicsComponent and tells it to draw, which gets the associated GraphicsAsset and tells it to finally draw using the original reference to the Renderer. Bit convoluted in my opinion, but this is my first time doing this stuff.

Tried my first implementation of a CBES, too.

At this point, I've verified that l_Graphics is not null, that the code is running, and earlier up the call branch I can draw a rectangle which appears and should be using the same Graphics reference. Google hasn't really given me any answers, though I've always been bad at search strings. Also tried drawing a rectangle inside that function, but it doesn't appear either.

The issue, as far as I can tell, is with the Graphics. I don't have a solid understanding of AWT, and wasn't planning on it since I want to move libraries/APIs soon. I think it may be related to AWT paint function being called by what I think is a different thread when repaint is called.

Before I'm told to move to an engine or different library entirely, I'd like to just get this working.

Advertisement

Not sure if thats the problem, but you are aware that you should only call AWT and Swing methods from the AWT thread, right?

Not sure if thats the problem, but you are aware that you should only call AWT and Swing methods from the AWT thread, right?

Was not aware of this. However, I feel like the draw calls I'm making should be on that thread, as the calls are done when the Render is told to repaint. The draw call is done in the paintComponent method, as seen here:


@Override
public void paintComponent(Graphics g_Old)
{
	super.paintComponent(g_Old);
	this.m_Graphics = g_Old;
	if (this.m_Screen != null)
		this.m_Screen.draw(this);
}

Edit: I realize the repaint method isn't called within the AWT thread. I'd have to implement the draw loop into that thread, I suppose, to make that happen. For now, that was just happening in the main loop. Could that be the cause?

Are you calling it yourself? Usually you make some call to inform AWT that some component needs to be repainted and then it automagically calls the components painting.

If you are in some other thread you can use SwingUtilities.invokeLater or invokeAndWait. The event loop is already inside the AWT and grabs events there. If you made your own loop you could stall it.

Are you calling it yourself? Usually you make some call to inform AWT that some component needs to be repainted and then it automagically calls the components painting.
If you are in some other thread you can use SwingUtilities.invokeLater or invokeAndWait.


Here's the full AWTRenderer class:

[spoiler]


package com.stranded.graphics.awt;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.stranded.graphics.interfaces.IRenderer;
import com.stranded.reference.GameOptions;
import com.stranded.reference.GameStrings;
import com.stranded.screens.GameScreen;



/**
 * Shell for the Render Engine
 * To be implemented later
 *
 */
public class AWTRenderer extends JPanel implements IRenderer{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -4275037581723785182L;
	
	/* Class Members */
	private Graphics m_Graphics;
	private GameScreen m_Screen;
	private JFrame m_Frame;
	private int m_TestLocAdd;
	
	public AWTRenderer ()
	{
		this.m_Frame = new JFrame(GameStrings.WINDOW_TITLE + " " + GameStrings.VESION_NUMBER);
		this.m_Frame.add(this);
		this.m_TestLocAdd = 0;
	}
	
	public void init()
	{
		this.m_Frame.setSize(GameOptions.DISPLAY_RESOLUTION_X, GameOptions.DISPLAY_RESOLUTION_Y);
		this.m_Frame.setDefaultCloseOperation(3);
		this.m_Frame.setLocationRelativeTo(null);
		this.m_Frame.setFocusable(true);
		this.m_Frame.setVisible(true);
		this.setVisible(true);
	}
	
	@Override
	public void destroy() {
		
		
	}
	
	@Override
	public void draw(GameScreen p_Screen)
	{
		this.m_Screen = p_Screen;
		this.repaint();
	}
	
	public Graphics getGraphics()
	{
		return this.m_Graphics;
	}
	
	public JFrame getFrame()
	{
		return this.m_Frame;
	}
	
	public void setGraphics(Graphics p_Graphics)
	{
		this.m_Graphics = p_Graphics;
	}
	
	@Override
	public void paintComponent(Graphics g_Old)
	{
		super.paintComponent(g_Old);
		this.m_Graphics = g_Old;
		if (this.m_Screen != null)
			this.m_Screen.draw(this);
		
		this.m_Graphics.setColor(Color.BLACK);
		this.m_Graphics.fillRect(7, 7 + 72 + 9 + this.m_TestLocAdd, 201, 25);
		this.m_Graphics.setColor(Color.BLUE);
		this.m_Graphics.fillRect(8, 8 + 72 + 9 + this.m_TestLocAdd, 201, 24);
		this.m_TestLocAdd += 1;
	}

}

[/spoiler]

Whenever I want to redraw (which is currently done in the main loop, sad.png ) I call the draw(GameScreen) function in AWTRenderer. As the AWTRenderer is a JPanel itself, it tells itself to repaint.

EDIT: I did a little further investigation that I could think of, and tried have a rectangle on screen that moves around with just a simple move its location every time its drawn. However, it gets drawn the first time, and the screen doesnt seem to refresh. However, print statements there (crappy debugging method, I know sad.png ) and later down the call chain show the function is being called.

EDIT 2: Just remembered that resizing the screen makes it update. This has caused the image to be drawn, and the rectangle moves, but the screen never refreshes on its own.

Just try to wrap the redraw call (the one in your main loop) in a runnable and use SwingUtilities?

Just try to wrap the redraw call (the one in your main loop) in a runnable and use SwingUtilities?

I'll look into doing this. I sort of see how, but I need to think through how to hide it behind the interfaces I use since it's AWT specific. Will report back.

EDIT: Oh, wait duh. Just changed the draw(GameScreen) method in AWTRenderer to:


@Override
	public void draw(GameScreen p_Screen)
	{
		this.m_Screen = p_Screen;
		
		SwingUtilities.invokeLater( new Runnable() {
			@Override
			public void run() {
				m_Frame.repaint();
			}
			
		});
	}

Seems to be working now!

Good to know.

I'd just invent my own method name and put the invokeLater in there, not override a component method which may be called from other places. Or actually a cleaner solution would be, just create your own renderer class that does not extend a component, just have it as a member and delegate calls to it.

Good to know.

I'd just invent my own method name and put the invokeLater in there, not override a component method which may be called from other places. Or actually a cleaner solution would be, just create your own renderer class that does not extend a component, just have it as a member.

I suppose I can try to hide it a bit better. The only real thing I've overwritten is the AWTRenderer's (as a JPanel) paintComponent() method, which I don't see how I could have avoided. The draw method is inherited from a Renderer interface.

The AWTRenderer is really the only thing that needs to know about JFrames and JPanels. The actual drawing should happen like the AWTImage does. I've tried my best to create a layer of Abstraction over everything that's AWT specific. I think the only thing that's not currently are the Assets, which I'm planning on changing over to the Renderer knowing what kind of AssetFactory to create and having the AssetManager pull from the Renderer to get that.

EDIT: To reiterate, as I'm not sure I was clear, the invokeLater isn't part of a function that's overriding a Component function.

EDIT 2: The reason I extended the JPanel originally was because in order to draw on it, I needed a reference to its Graphics component, which is given in the paint component function.

This topic is closed to new replies.

Advertisement