Argh, having trouble with transparency in with Overlay's Graphics2D

Hello, folks. I’m trying to make a fading effect in between different parts of my program using the Overlay class. Unfortunately, it doesn’t seem to be working…

Here’s the code:

public class TitleScreen implements FilterInterface {
	private BufferedImage image;
	private double counter;
	/**
	 * Create a new TitleScreen.
	 * @param c the component to refer to when loading images
	 */
	public TitleScreen(Component c){
		
		image = ImageUtilities.getBufferedImage("./image/title.gif", c);
	}
	
	/**
	 * Filter the graphics to add in the desired details.  In this case, I'm
	 * drawing the title screen and drawing a transparent square over it to
	 * create a fading effect.
	 *
	 * Note that this is using a new Graphics every time it's called, due to 
	 * nature of using Overlay's createGraphics() method in my GLEventListener's
	 * display method.
	 *
	 * @param target the graphics to add the details to
	 */
	public synchronized void filter(Graphics2D target) {		
		
		
		target.drawImage(image, null, 0, 0);

		//this is used to make it easier to distinguish transparency changes
		target.setColor(Color.GREEN);
		target.fillRect(40,40,120,120);
		
		//store the current composite
		Composite temp = target.getComposite();
		
		//create fade effect
		target.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) counter));
		
		//draw the fade effect
		target.fillRect(0,0,512,512);
		
		//restore the target's old composite
		target.setComposite(temp);
		
		//increase counter
		if((float) counter < 1f){ //note that I have to typecast counter; it was originally a float, but it seemed to be losing accuracy as I increased it
			try{
				Thread.sleep(time);
			}
			catch(InterruptedException ex){}
			
			counter = counter+0.05;
			System.out.println("Counter = " +counter);
		}

		System.out.println("Composite: " +((AlphaComposite) (target.getComposite())).getAlpha());
		
	}
}

Everything works correctly; there are no compilation errors, inaccuracies, and the println calls indicate everything is increasing approprately.

The only catch is that the screen doesn’t reflect this.

It changes its composite the first time, and draws appropriately. If I started with counter at .5, everything would be half transparent. The problem is that it doesn’t change again after that, despite what the println output says.

What’s going on? How do I fix this? It’s been holding me back for weeks…

I’m not sure, are you doing a markDirty() somewhere on the Overlay? Because I imagine that you need to let the system know you are making changes to the image you are displaying.

Hmm, I’m not sure how markDirty() works, but I’ll give it a shot. I’m sorry for not outlining my program more. Here’s how it works…

This is where the program starts.

public class Temp implements KeyListener{
	
	private static GLEventListener focus;
	private static GraphicOverlay GO;
	private static SceneUnderlay Scene;
	
	public Temp(String[] args) {
	    Frame frame = new Frame("R u s t");
	    GLCapabilities caps = new GLCapabilities();
	    caps.setAlphaBits(8);
	    
	    
	    GLCanvas canvas = new GLCanvas(caps);
	    //System.out.println("AutoSwap on: "+canvas.getAutoSwapBufferMode());
	    Scene = new SceneUnderlay();
	    GO = new GraphicOverlay();
	    
	    canvas.addGLEventListener(scene); //this adds a 3D object to the background; everything works here
	    canvas.addGLEventListener(GO); //this adds a 2D overlay to the canvas; here is where my troubles begin
	    
	    
	    
	    frame.add(canvas);
	    frame.setSize(512, 512);
	    final Animator animator = new Animator(canvas);
	    frame.addWindowListener(new WindowAdapter() {
	    	
//	Run this on another thread than the AWT event queue to
//	make sure the call to Animator.stop() completes before
//	exiting
	        public void windowClosing(WindowEvent e) {
	          new Thread(new Runnable() {
	              public void run() {
	                animator.stop();
	                System.exit(0);
	              }
	            }).start();
	        }
	      });
	    frame.show();
	    animator.start();
	    frame.addKeyListener(this);
	    beginScript();
	}

	public void keyPressed(KeyEvent arg0) {
		System.out.println("Temp detected KeyEvent "+arg0);
		if(focus == GO)
			GO.passKeyPressedEvent(arg0);
		else if(focus == Scene)
			Scene.passKeyPressedEvent(arg0);
	}
	public void keyReleased(KeyEvent arg0) {}
	public void keyTyped(KeyEvent arg0) {}
	
	public static void main(String[] args){
		new Temp(args);
	}
/**************************************************************
 ***************************SCRIPT*****************************
 *************************************************************/
	
	/**
	 * Starts the title sequence, then enters the main game loop
	 */
	public static void beginScript(){
		System.out.println("started script");
		GO.createTitle();
		System.out.println("title created");
		focus = GO;
		System.out.println("Focus set to graphicOverlay");
		//etc
	}
}

[b]This is GraphicOverlay, where the overlay object creates Graphics2D with createGraphics(). This behavior is found in display().

The architecture here is supposed to support multiple menus and such to make a GUI. I use an ArrayList to store “filters”, which take the Graphics2D created by Overlay’s createGraphics() and modifies it to create menus and stuff. TitleScreen, as shown in the first post, is a filter.[/b]

public class GraphicOverlay implements GLEventListener {

	private Overlay overlay;
	private ArrayList componentList;
	private static GLAutoDrawable c;
	private Color TRANSPARENT_BLACK = new Color(0.0f, 0.0f, 0.0f, 0.0f); 
	
	/**
	 * A list used to contain all the elements of the Heads-Up
	 * Display.  The element in the last slot of the list
	 * recieves keyboard input.
	 */

	public void display(GLAutoDrawable arg0) {
		
		
		//System.out.println("GraphicOverlay displayed");
		Graphics2D g2d = overlay.createGraphics();
		g2d.setBackground(TRANSPARENT_BLACK);
		
		//clear the screen
		g2d.setColor(TRANSPARENT_BLACK);
		g2d.fillRect(0,0,512,512);

		//send g2d through modifying classes
		for(int i = 0; i < componentList.size(); i++){
			((FilterInterface) componentList.get(i)).filter(g2d);
		}
		
		
		if (overlay.contentsLost()) //it does this once in the beginning, but that's it
		{
			System.out.println("Overlay lost its contents");
			g2d.setColor(TRANSPARENT_BLACK);
			g2d.fillRect(0, 0, 512,512);
		}
		
		//draw the overlay
		overlay.drawAll();
		g2d.dispose();
	}



	public void init(GLAutoDrawable drawable) {
		c = drawable;
		GL gl = drawable.getGL();
		gl.setSwapInterval(0);
		
		//create componentList
		componentList = new ArrayList();


		//create the title here.  Normally the title would be made via external methods like the other components, but for some reason init runs slower than those.
		//Note that in any other situation, filters like TitleScreen are added outside of init()
		System.out.println("About to create title in overlay init...");
		componentList.add(new TitleScreen((Component) c));
		
		
		overlay = new Overlay(drawable);

	}

	/**
	 * Unused, for now
	 */
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {} 

	/**
	  * Unused, for now
	  */
	public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) {}

	/**
	  * Used to pass key events to the highest component, which typically has the user's focus.
	  * 
	public void passKeyPressedEvent(KeyEvent e){
		System.out.println("GraphicOverlay recieved KeyEvent " +e);
		((FilterInterface) componentList.get(componentList.size() - 1)).keyPressed(e);
	}
}

Note that I made a typo in the first post that I have since fixed…

	public TitleScreen(Component c){
		
		image = ImageUtilities.getBufferedImage("./image/title.gif", c);
	}

should be

	public TitleScreen(Component c){
		
		image = ImageUtilities.getBufferedImage("./image/title.gif", c);
		counter = 0.0;
	}

Sorry for dumping my entire code on you folks; I know how darned hard it can be to read someone else’s work, especially if they’re as sloppy as I am.

I greatly appreciate your responses. I’ve been stuck on this transparency issue for two weeks, which has been a huge problem for my project as a whole.

I’m having trouble following your code as a whole, but I think your problem is probably in the way that you clear the Overlay. You can’t just do:
g2.setColor(TRANSPARENT_BLACK);
in the default SrcOver mode, because that would have the effect of blending completely transparent pixels over what’s already in the Overlay.

Instead, you’d have to do:
g2.setComposite(AlphaComposite.Src);
g2.setColor(TRANSPARENT_BLACK);

But there’s a better way to do this without creating/using that special transparent Color. Simply use:
g2.setComposite(AlphaComposite.Clear);

That’s the easiest and fastest way to clear a translucent surface.

Chris

Thanks for your feedback, but I fixed it by simply adding MarkDirty here:


public void display(GLAutoDrawable arg0) {
		
		
		//System.out.println("GraphicOverlay displayed");
		Graphics2D g2d = overlay.createGraphics();
		g2d.setBackground(TRANSPARENT_BLACK);
		overlay.markDirty(0,0,512,512);
		...etc

Such a simple fix! I feel like such an idiot now. Sorry for troubling you all with that!

Oh, but one more question. Since I’m calling markDirty every loop in display, and the documentation seems to imply that dirty regions need to be cleared manually, am I making a loop that continuously wastes memory? Or am I just further reinforcing my stupidity by misinterpreting the documentation?

Thanks for helping me get around this issue. I’ve been stuck here far too long, and it’s immensely satisfying to finally get around this obstacle.

The markDirty() calls in the Overlay “only” result in copies of the BufferedImage which backs the Overlay down into the associated OpenGL texture. So doing this unnecessarily will result in reduced performance, but no memory waste or additional GCs.