Bug 93

Summary: Cannot grab alpha channel
Product: [JogAmp] Jogl Reporter: Sven Gothel <sgothel>
Component: coreAssignee: Sven Gothel <sgothel>
Status: VERIFIED WORKSFORME    
Severity: normal    
Priority: P3    
Version: 1   
Hardware: All   
OS: windows   
Type: FEATURE SCM Refs:
Workaround: ---

Description Sven Gothel 2010-03-24 07:46:42 CET


---- Reported by mikofclassx 2004-06-05 08:39:36 ----

I used this source for my testing purposes: my need is to render something with
jogl, grab it and save as a int argb bufferedimage to a PNG file. The problem is
I keep getting a fully opaque alpha channel. That's wrong, according to the
opengl specs.

Here's the SRC:

----------------------------------------------------

/*
 * Created on 4-giu-2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package JOGL;

import net.java.games.jogl.impl.*;
import net.java.games.jogl.*;
import java.awt.*;
import java.awt.image.*;

public class OffscreenPanel extends Component implements GLDrawable
{

	private GLDrawableHelper drawableHelper = new GLDrawableHelper();
	private GLContext context;
	private BufferedImage offscreenImage;
	private int awtFormat;
	private int glFormat;
	private int glType;
	private int glComps;
	private DataBufferByte dbByte;
	private DataBufferInt dbInt;

	// For saving/restoring of OpenGL state during [b]readpixels[/b]
	private int[] swapbytes = new int[1];
	private int[] lsbfirst = new int[1];
	private int[] rowlength = new int[1];
	private int[] skiprows = new int[1];
	private int[] skippixels = new int[1];
	private int[] alignment = new int[1];

	private final int fx = 0;
	private final int fy = 0;

	OffscreenPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, int
width, int height)
	{
		context = GLContextFactory.getFactory().createGLContext(null, capabilities,
chooser, null);
		context.resizeOffscreenContext(width, height);
		resizeContext(width, height);
	}

	public void resizeContext(int w, int h)
	{
		final int fwidth = w;
		final int fheight = h;
		context.invokeGL(new Runnable()
		{
			public void run()
			{
				getGL().glViewport(fx, fy, fwidth, fheight);
				drawableHelper.reshape(OffscreenPanel.this, fx, fy, fwidth, fheight);
				if (offscreenImage != null
					&& (offscreenImage.getWidth() != context.getOffscreenContextWidth()
						|| offscreenImage.getHeight() != context.getOffscreenContextHeight()))
				{
					offscreenImage.flush();
					offscreenImage = null;
				}
			}
		}, true, initAction);
	}

	public void display()
	{
		context.invokeGL(displayAction, false, initAction);
	}

	public BufferedImage getSnapshot()
	{
		return offscreenImage;
	}

	public void addGLEventListener(GLEventListener listener)
	{
		drawableHelper.addGLEventListener(listener);
	}

	public void removeGLEventListener(GLEventListener listener)
	{
		drawableHelper.removeGLEventListener(listener);
	}

	public GL getGL()
	{
		return context.getGL();
	}

	public void setGL(GL gl)
	{
		context.setGL(gl);
	}

	public GLU getGLU()
	{
		return context.getGLU();
	}

	public void setGLU(GLU glu)
	{
		context.setGLU(glu);
	}

	public void setRenderingThread(Thread currentThreadOrNull) throws GLException
	{
		throw new GLException("Not supported");
	}

	public Thread getRenderingThread()
	{
		return context.getRenderingThread();
	}

	public void setNoAutoRedrawMode(boolean noAutoRedraws)
	{
	}

	public boolean getNoAutoRedrawMode()
	{
		return false;
	}

	public boolean canCreateOffscreenDrawable()
	{
		return false;
	}

	public GLPbuffer createOffscreenDrawable(GLCapabilities capabilities, int
initialWidth, int initialHeight)
	{
		throw new GLException("Not supported");
	}

	GLContext getContext()
	{
		return context;
	}

	//----------------------------------------------------------------------  
	// Internals only below this point
	//

	class InitAction implements Runnable
	{
		public void run()
		{
			drawableHelper.init(OffscreenPanel.this);
		}
	}
	private InitAction initAction = new InitAction();

	class DisplayAction implements Runnable
	{

		public void run()
		{
			drawableHelper.display(OffscreenPanel.this);
			// Must now copy pixels from offscreen context into surface
			if (offscreenImage == null)
			{
				int awtFormat = context.getOffscreenContextBufferedImageType();
				offscreenImage =
					new BufferedImage(context.getOffscreenContextWidth(),
context.getOffscreenContextHeight(), awtFormat);
				switch (awtFormat)
				{
					case BufferedImage.TYPE_3BYTE_BGR :
						glFormat = GL.GL_BGR;
						glType = GL.GL_UNSIGNED_BYTE;
						glComps = 3;
						dbByte = (DataBufferByte) offscreenImage.getRaster().getDataBuffer();
						break;

					case BufferedImage.TYPE_INT_RGB :
						glFormat = GL.GL_BGRA;
						glType = GL.GL_UNSIGNED_BYTE;
						glComps = 4;
						dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer();
						break;

					case BufferedImage.TYPE_INT_ARGB :
						glFormat = GL.GL_BGRA;
						glType = context.getOffscreenContextPixelDataType();
						glComps = 4;
						dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer();
						break;

					default :
						throw new GLException("Unsupported offscreen image type " + awtFormat);
				}
			}

			GL gl = getGL();
			// Save current modes
			gl.glGetIntegerv(GL.GL_PACK_SWAP_BYTES, swapbytes);
			gl.glGetIntegerv(GL.GL_PACK_LSB_FIRST, lsbfirst);
			gl.glGetIntegerv(GL.GL_PACK_ROW_LENGTH, rowlength);
			gl.glGetIntegerv(GL.GL_PACK_SKIP_ROWS, skiprows);
			gl.glGetIntegerv(GL.GL_PACK_SKIP_PIXELS, skippixels);
			gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, alignment);

			gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, GL.GL_FALSE);
			gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, GL.GL_TRUE);
			gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, offscreenImage.getWidth());
			gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0);
			gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0);
			gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1);

			// Actually read the pixels.
			gl.glReadBuffer(context.getOffscreenContextReadBuffer());
			if (dbByte != null)
			{
				gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(),
glFormat, glType, dbByte.getData());
			}
			else
				if (dbInt != null)
				{
					gl.glReadPixels(0, 0, offscreenImage.getWidth(),
offscreenImage.getHeight(), glFormat, glType, dbInt.getData());
				}

			// Restore saved modes.
			gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, swapbytes[0]);
			gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, lsbfirst[0]);
			gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, rowlength[0]);
			gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, skiprows[0]);
			gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, skippixels[0]);
			gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, alignment[0]);

			gl.glFlush();
			gl.glFinish();

		}
	}
	private DisplayAction displayAction = new DisplayAction();
	/* (non-Javadoc)
	 * @see net.java.games.jogl.GLDrawable#setAutoSwapBufferMode(boolean)
	 */
	public void setAutoSwapBufferMode(boolean arg0)
	{
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see net.java.games.jogl.GLDrawable#getAutoSwapBufferMode()
	 */
	public boolean getAutoSwapBufferMode()
	{
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see net.java.games.jogl.GLDrawable#swapBuffers()
	 */
	public void swapBuffers()
	{
		// TODO Auto-generated method stub
		
	}

}



-----------------------------------


/*
 * Created on 4-giu-2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package JOGL;

import net.java.games.jogl.*;
import net.java.games.jogl.util.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;

public class OffscreenGLEL implements GLEventListener
{

	public static void main(String[] args)
	{

		try
		{
			OffscreenGLEL test = new OffscreenGLEL();
			test.canvas.display();
			BufferedImage im = test.canvas.getSnapshot();
			ImageIO.write(im, "png", new File("image.png"));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	OffscreenPanel canvas;
	int px = 256, py = 256;

	/** Creates a new instance of OffscreenGLEL */
	public OffscreenGLEL()
	{
		GLCapabilities cCaps = new GLCapabilities();
		cCaps.setHardwareAccelerated(true);
		cCaps.setDoubleBuffered(false);
		cCaps.setOffscreenRenderToTexture(false);
		cCaps.setDoubleBuffered(false);
		cCaps.setAlphaBits(8);

		canvas = new OffscreenPanel(cCaps, null, px, py);
		canvas.addGLEventListener(this);
		try
		{
			canvas.display();
		}
		catch (GLException e)
		{
			System.err.println(e.toString());
		}
	}

	public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean
deviceChanged)
	{
		// do the display changed stuff
	}

	public void reshape(GLDrawable drawable, int x, int y, int width, int height)
	{
		// do the reshaping stuff
		
		float h = (float) px / (float) py;
		gl.glViewport(0, 0, px, py);
		System.out.println("WINDOW SIZE " + width + " " + width);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(45.0f, 1.0f, 0.1f, 1000.0f);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
	}

	public void display(GLDrawable drawable)
	{
		// I have to call init() here to get that code to execute. This is bad
		// because I don't want to have to do the init stuff every time
		// I call display.

		init(drawable);

		// do all the drawing  
		gl.glEnable(GL.GL_BLEND);  
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);  		
	
		gl.glClearColor(0, 0, 0, 0);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glPushMatrix();
	
		
		gl.glEnable(GL.GL_TEXTURE_2D);

		gl.glBegin(GL.GL_QUADS);
		gl.glColor4f(1f, 1f, 1f, 0f);
		gl.glTexCoord2f(0, 0);
		gl.glVertex3i(-5, -5, -15);
		gl.glTexCoord2f(1, 0);
		gl.glVertex3i(5, -5, -15);
		gl.glTexCoord2f(1, 1);
		
		gl.glColor4f(1f, 0f, 0f,1f);
		gl.glVertex3i(5, 5, -15);
		gl.glTexCoord2f(0, 1);
		gl.glVertex3i(-5, 5, -15);
		gl.glEnd();
		gl.glDisable(GL.GL_TEXTURE_2D);
		gl.glPopMatrix();


	}

	public void init(GLDrawable drawable)
	{
		
		gl = canvas.getGL();
		glu = canvas.getGLU();
		
		// this call turns out to be critical, even though I'm not sure what it does
		reshape(drawable, 0, 0, px, py);

		// Add a light, etc.
		//gl = drawable.getGL();
		//glu = drawable.getGLU();

		this.gldrawable = drawable;
		System.err.println("INIT GL IS: " + gl.getClass().getName());

		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);

		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
	}
	
	
	private GL gl;
	private GLU glu;
	private GLDrawable gldrawable;
}


-----------------------------



---- Additional Comments From kbr 2005-05-12 13:16:52 ----

Readback of the frame buffer, including alpha channels, is known to work. There
must be some problem with the example code. You may find it helpful to look at
the GLJPanel's current implementation, which has a pbuffer
(hardware-accelerated) pipeline and which also sidesteps the "offscreen image
format" hack in the offscreen contexts. If you are still having problems please
post on the JOGL forum on javagaming.org.




---- Additional Comments From mikofclassx 2005-05-13 00:27:05 ----

It was just an old issue. There was some limited PBuffer support at that time in
Jogl. With the help of Pbuffers everything works perfectly.

Cheers,
Mik




--- Bug imported by sgothel@jausoft.com 2010-03-24 07:46 EDT  ---

This bug was previously known as _bug_ 93 at https://jogl.dev.java.net/bugs/show_bug.cgi?id=93

The original assignee of this bug does not have
   an account here. Reassigning to the default assignee
   for the component, sgothel@jausoft.com.
   Previous assignee was kbr.