diff options
author | Sven Gothel <[email protected]> | 2013-05-08 22:57:17 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-05-08 22:57:17 +0200 |
commit | 0a7bf77b8c0765f8a53dc72a8edab8e0496938ff (patch) | |
tree | f7a02fad5afda28ea58f70c9fbb00edfcc2f49e9 /src/jogl/classes | |
parent | e6a234edb1318bb38f6ab65d96518471d0c7fd68 (diff) |
Refactor: TextureData's PixelAttributes/PixelBufferProvider -> GLPixelBuffer/.. and enhance usage; GLJPanel: Use GLPixelBuffer* API and SingleAWTGLPixelBufferProvider if possible.
Refactor: TextureData's PixelAttributes/PixelBufferProvider -> GLPixelBuffer/.. and enhance usage
- GLPixelBuffer, GLPixelAttributes and GLPixelBufferProvider have potential for wider audience,
hence extract them to package 'com.jogamp.opengl.util'.
- Using GLPixelBuffer, shall attempt to use pack/unpack row-stride, i.e.
GL2GL3.GL_PACK_ROW_LENGTH, or GL2GL3.GL_UNPACK_ROW_LENGTH.
See GLReadBufferUtil and GLJPanel
- AWTGLPixelBuffer*: Attribute 'row-stride' allows reusing a bigger buffer than requested.
GLJPanel: Use GLPixelBuffer* API and SingleAWTGLPixelBufferProvider if possible.
- Use GLPixelBuffer API to remove redundancies
- Attempts to use SingleAWTGLPixelBufferProvider to save JVM/CPU heap space
for BuffereImage and IntBbuffer (readBack)
Added unit new test demonstrating multiple overlapping GLJPanels reusing (or not)
a singlton SingleAWTGLPixelBufferProvider.
Diffstat (limited to 'src/jogl/classes')
7 files changed, 651 insertions, 295 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java new file mode 100644 index 000000000..b2e0af2b5 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java @@ -0,0 +1,253 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.nio.Buffer; +import java.nio.ByteBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GLContext; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.util.texture.TextureData; + +/** + * OpenGL pixel data buffer, allowing user to provide buffers via their {@link GLPixelBufferProvider} implementation. + * <p> + * {@link GLPixelBufferProvider} produces a {@link GLPixelBuffer}. + * </p> + * <p> + * You may use {@link #defaultProvider}. + * </p> + */ +public class GLPixelBuffer { + + /** Allows user to interface with another toolkit to define {@link GLPixelAttributes} and memory buffer to produce {@link TextureData}. */ + public static interface GLPixelBufferProvider { + /** Called first to determine {@link GLPixelAttributes}. */ + GLPixelAttributes getAttributes(GL gl, int componentCount); + + /** + * Allocates a new {@link GLPixelBuffer} object. + * <p> + * Being called to gather the initial {@link GLPixelBuffer}, + * or a new replacement {@link GLPixelBuffer} if {@link GLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + * </p> + * <p> + * The minimum required {@link Buffer#remaining() remaining} byte size equals to <code>minByteSize</code>, if > 0, + * otherwise utilize {@link GLBuffers#sizeof(GL, int[], int, int, int, int, int, boolean)} + * to calculate it. + * </p> + * + * @param gl the corresponding current GL context object + * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param width in pixels + * @param height in pixels + * @param depth in pixels + * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU + * @param minByteSize if > 0, the pre-calculated minimum byte-size for the resulting buffer, otherwise ignore. + */ + GLPixelBuffer allocate(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize); + } + /** + * Default {@link GLPixelBufferProvider} utilizing best match for {@link GLPixelAttributes} + * and {@link GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocating} a {@link ByteBuffer}. + */ + public static GLPixelBufferProvider defaultProvider = new GLPixelBufferProvider() { + + @Override + public GLPixelAttributes getAttributes(GL gl, int componentCount) { + final GLContext ctx = gl.getContext(); + final int dFormat, dType; + + if(gl.isGL2GL3() && 3 == componentCount) { + dFormat = GL.GL_RGB; + dType = GL.GL_UNSIGNED_BYTE; + } else { + dFormat = ctx.getDefaultPixelDataFormat(); + dType = ctx.getDefaultPixelDataType(); + } + return new GLPixelAttributes(componentCount, dFormat, dType); + } + + /** + * {@inheritDoc} + * <p> + * Returns an NIO {@link ByteBuffer}. + * </p> + */ + @Override + public GLPixelBuffer allocate(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize) { + if( minByteSize > 0 ) { + return new GLPixelBuffer(pixelAttributes, width, height, depth, pack, Buffers.newDirectByteBuffer(minByteSize)); + } else { + int[] tmp = { 0 }; + final int byteSize = GLBuffers.sizeof(gl, tmp, pixelAttributes.bytesPerPixel, width, height, depth, pack); + return new GLPixelBuffer(pixelAttributes, width, height, depth, pack, Buffers.newDirectByteBuffer(byteSize)); + } + } + }; + + /** Pixel attributes. */ + public static class GLPixelAttributes { + /** Undefined instance of {@link GLPixelAttributes}, having componentCount:=0, format:=0 and type:= 0. */ + public static final GLPixelAttributes UNDEF = new GLPixelAttributes(0, 0, 0); + + /** Pixel component count */ + public final int componentCount; + /** The OpenGL pixel data format */ + public final int format; + /** The OpenGL pixel data type */ + public final int type; + /** The OpenGL pixel size in bytes */ + public final int bytesPerPixel; + + /** + * Deriving {@link #componentCount} via GL <code>dataFormat</code>, i.e. {@link GLBuffers#componentCount(int)} if > 0. + * @param dataFormat GL data format + * @param dataType GL data type + */ + public GLPixelAttributes(int dataFormat, int dataType) { + this(0 < dataFormat ? GLBuffers.componentCount(dataFormat) : 0, dataFormat, dataType); + } + /** + * Using user specified source {@link #componentCount}. + * @param componentCount source component count + * @param dataFormat GL data format + * @param dataType GL data type + */ + public GLPixelAttributes(int componentCount, int dataFormat, int dataType) { + this.componentCount = componentCount; + this.format = dataFormat; + this.type = dataType; + this.bytesPerPixel = ( 0 < dataFormat && 0 < dataType ) ? GLBuffers.bytesPerPixel(dataFormat, dataType) : 0; + } + public String toString() { + return "PixelAttributes[comp "+componentCount+", fmt 0x"+Integer.toHexString(format)+", type 0x"+Integer.toHexString(type)+", bytesPerPixel "+bytesPerPixel+"]"; + } + } + + /** The {@link GLPixelAttributes}. */ + public final GLPixelAttributes pixelAttributes; + /** Width in pixels. */ + public final int width; + /** Height in pixels. */ + public final int height; + /** Depth in pixels. */ + public final int depth; + /** Data packing direction. If <code>true</code> for read mode GPU -> CPU, <code>false</code> for write mode CPU -> GPU. */ + public final boolean pack; + /** Byte size of the buffer. Actually the number of {@link Buffer#remaining()} bytes when passed in ctor. */ + public final int byteSize; + /** + * Buffer holding the pixel data. If {@link #rewind()}, it holds <code>byteSize</code> {@link Buffer#remaining()} bytes. + * <p> + * By default the {@link Buffer} is a {@link ByteBuffer}, due to {@link DefProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int)}. + * However, other {@link GLPixelBufferProvider} may utilize different {@link Buffer} types. + * </p> + */ + public final Buffer buffer; + /** Buffer element size in bytes. */ + public final int bufferElemSize; + + public StringBuffer toString(StringBuffer sb) { + if(null == sb) { + sb = new StringBuffer(); + } + sb.append(pixelAttributes).append(", dim ").append(width).append("x").append(height).append("x").append(depth).append(", pack ").append(pack) + .append(", buffer[sz [bytes ").append(byteSize).append(", elemSize ").append(bufferElemSize).append(", ").append(buffer).append("]"); + return sb; + } + public String toString() { + return "GLPixelBuffer["+toString(null).toString()+"]"; + } + + public GLPixelBuffer(GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, Buffer buffer) { + this.pixelAttributes = pixelAttributes; + this.width = width; + this.height = height; + this.depth = depth; + this.pack = pack; + this.buffer = buffer; + this.byteSize = Buffers.remainingBytes(buffer); + this.bufferElemSize = Buffers.sizeOfBufferElem(buffer); + } + + public boolean isValid() { + return 0 < byteSize; + } + + public Buffer rewind() { + return buffer.rewind(); + } + + /** Returns the byte position of the {@link #buffer}. */ + public int position() { + return buffer.position() * bufferElemSize; + } + + /** Sets the byte position of the {@link #buffer}. */ + public Buffer position(int bytePos) { + return buffer.position( bytePos / bufferElemSize ); + } + + public Buffer flip() { + return buffer.flip(); + } + + public Buffer clear() { + return buffer.clear(); + } + + /** + * Returns true, if implementation requires a new buffer based on the new size + * due to pixel alignment or byte size, otherwise false. + * <p> + * It is assumed that <code>pixelAttributes</code>, <code>depth</code> and <code>pack</code> stays the same! + * </p> + * <p> + * The minimum required byte size equals to <code>minByteSize</code>, if > 0, + * otherwise utilize {@link GLBuffers#sizeof(GL, int[], int, int, int, int, int, boolean) GLBuffers.sizeof(..)} + * to calculate it. + * </p> + * @param gl the corresponding current GL context object + * @param newWidth new width in pixels + * @param newHeight new height in pixels + * @param minByteSize if > 0, the pre-calculated minimum byte-size for the resulting buffer, otherwise ignore. + * @see GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) + */ + public boolean requiresNewBuffer(GL gl, int newWidth, int newHeight, int minByteSize) { + return this.byteSize < minByteSize; + } + + /** Dispose resources. */ + public void dispose() { + buffer.clear(); + } +} + diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java index dc87c7ac9..602dbb095 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java @@ -30,10 +30,9 @@ package com.jogamp.opengl.util; import java.io.File; import java.io.IOException; -import java.nio.Buffer; -import java.nio.ByteBuffer; import javax.media.opengl.GL; +import javax.media.opengl.GL2GL3; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLException; @@ -41,9 +40,9 @@ import javax.media.opengl.GLException; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; -import com.jogamp.opengl.util.texture.TextureData.DefPixelBufferProvider; -import com.jogamp.opengl.util.texture.TextureData.PixelAttributes; -import com.jogamp.opengl.util.texture.TextureData.PixelBufferProvider; +import com.jogamp.opengl.util.GLPixelBuffer; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelBufferProvider; import com.jogamp.opengl.util.texture.TextureIO; /** @@ -51,13 +50,12 @@ import com.jogamp.opengl.util.texture.TextureIO; * <p>May be used directly to write the TextureData to file (screenshot).</p> */ public class GLReadBufferUtil { - protected final PixelBufferProvider pixelBufferProvider; + protected final GLPixelBufferProvider pixelBufferProvider; protected final int componentCount, alignment; protected final Texture readTexture; protected final GLPixelStorageModes psm; - protected int readPixelSizeLast = 0; - protected Buffer readPixelBuffer = null; + protected GLPixelBuffer readPixelBuffer = null; protected TextureData readTextureData = null; /** @@ -65,10 +63,10 @@ public class GLReadBufferUtil { * @param write2Texture true if readPixel's TextureData shall be written to a 2d Texture */ public GLReadBufferUtil(boolean alpha, boolean write2Texture) { - this(new DefPixelBufferProvider(), alpha, write2Texture); + this(GLPixelBuffer.defaultProvider, alpha, write2Texture); } - public GLReadBufferUtil(PixelBufferProvider pixelBufferProvider, boolean alpha, boolean write2Texture) { + public GLReadBufferUtil(GLPixelBufferProvider pixelBufferProvider, boolean alpha, boolean write2Texture) { this.pixelBufferProvider = pixelBufferProvider; this.componentCount = alpha ? 4 : 3 ; this.alignment = alpha ? 4 : 1 ; @@ -76,11 +74,11 @@ public class GLReadBufferUtil { this.psm = new GLPixelStorageModes(); } - /** Returns the {@link PixelBufferProvider} used by this instance. */ - public PixelBufferProvider getPixelBufferProvider() { return pixelBufferProvider; } + /** Returns the {@link GLPixelBufferProvider} used by this instance. */ + public GLPixelBufferProvider getPixelBufferProvider() { return pixelBufferProvider; } public boolean isValid() { - return null!=readTextureData && null!=readPixelBuffer ; + return null!=readTextureData && null!=readPixelBuffer && readPixelBuffer.isValid(); } public boolean hasAlpha() { return 4 == componentCount ? true : false ; } @@ -88,14 +86,9 @@ public class GLReadBufferUtil { public GLPixelStorageModes getGLPixelStorageModes() { return psm; } /** - * Returns the raw pixel Buffer, filled by {@link #readPixels(GLAutoDrawable, boolean)}. - * <p> - * By default the {@link Buffer} is a {@link ByteBuffer}, due to {@link DefPixelBufferProvider#allocate(int, int, int)}. - * If the {@link PixelBufferProvider} has changed via {@link #setPixelBufferProvider(PixelBufferProvider)}. - * the {@link Buffer} type maybe different. - * </p> + * Returns the {@link GLPixelBuffer}, created and filled by {@link #readPixels(GLAutoDrawable, boolean)}. */ - public Buffer getPixelBuffer() { return readPixelBuffer; } + public GLPixelBuffer getPixelBuffer() { return readPixelBuffer; } /** * rewind the raw pixel ByteBuffer @@ -163,7 +156,7 @@ public class GLReadBufferUtil { if(GL.GL_NO_ERROR != glerr0) { System.err.println("Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x"+Integer.toHexString(glerr0)); } - final PixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); + final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); final int internalFormat; if(gl.isGL2GL3() && 3 == componentCount) { internalFormat = GL.GL_RGB; @@ -191,13 +184,12 @@ public class GLReadBufferUtil { } final int tmp[] = new int[1]; - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.format, pixelAttribs.type, width, height, 1, true); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, width, height, 1, true); boolean newData = false; - if( readPixelSize > readPixelSizeLast || pixelBufferProvider.requiresNewBuffer(width, height) ) { - readPixelBuffer = pixelBufferProvider.allocate(width, height, readPixelSize); + if( null == readPixelBuffer || readPixelBuffer.requiresNewBuffer(gl, width, height, readPixelSize) ) { + readPixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, width, height, 1, true, readPixelSize); Buffers.rangeCheckBytes(readPixelBuffer, readPixelSize); - readPixelSizeLast = readPixelSize ; try { readTextureData = new TextureData( gl.getGLProfile(), @@ -207,13 +199,12 @@ public class GLReadBufferUtil { pixelAttribs, false, false, flipVertically, - readPixelBuffer, + readPixelBuffer.buffer, null /* Flusher */); newData = true; } catch (Exception e) { readTextureData = null; readPixelBuffer = null; - readPixelSizeLast = 0; throw new RuntimeException("can not fetch offscreen texture", e); } } else { @@ -222,14 +213,17 @@ public class GLReadBufferUtil { readTextureData.setHeight(height); readTextureData.setPixelAttributes(pixelAttribs); } - boolean res = null!=readPixelBuffer; + boolean res = null!=readPixelBuffer && readPixelBuffer.isValid(); if(res) { psm.setAlignment(gl, alignment, alignment); + if(gl.isGL2GL3()) { + gl.getGL2GL3().glPixelStorei(GL2GL3.GL_PACK_ROW_LENGTH, readPixelBuffer.width); + } readPixelBuffer.clear(); try { - gl.glReadPixels(inX, inY, width, height, pixelAttribs.format, pixelAttribs.type, readPixelBuffer); + gl.glReadPixels(inX, inY, width, height, pixelAttribs.format, pixelAttribs.type, readPixelBuffer.buffer); } catch(GLException gle) { res = false; gle.printStackTrace(); } - readPixelBuffer.position( readPixelSize / Buffers.sizeOfBufferElem(readPixelBuffer) ); + readPixelBuffer.position( readPixelSize ); readPixelBuffer.flip(); final int glerr1 = gl.glGetError(); if(GL.GL_NO_ERROR != glerr1) { @@ -261,10 +255,9 @@ public class GLReadBufferUtil { readTextureData = null; } if(null != readPixelBuffer) { + readPixelBuffer.dispose(); readPixelBuffer = null; } - readPixelSizeLast = 0; - pixelBufferProvider.dispose(); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java new file mode 100644 index 000000000..2af48cefd --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java @@ -0,0 +1,203 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.awt; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.nio.Buffer; +import java.nio.IntBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2GL3; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.util.GLPixelBuffer; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; + +/** + * AWT {@link GLPixelBuffer} backed by an {@link BufferedImage} of type + * {@link BufferedImage#TYPE_INT_ARGB} or {@link BufferedImage#TYPE_INT_RGB}. + * <p> + * Implementation uses an array backed {@link IntBuffer}. + * </p> + * <p> + * {@link AWTGLPixelBuffer} can be produced via {@link AWTGLPixelBufferProvider}'s + * {@link AWTGLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocate(..)}. + * </p> + * <p> + * See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)} for {@link #allowRowStride} details. + * </p> + */ +public class AWTGLPixelBuffer extends GLPixelBuffer { + public static final GLPixelAttributes awtPixelAttributesIntRGB = new GLPixelAttributes(GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); + + /** Allow {@link GL2GL3#GL_PACK_ROW_LENGTH}, or {@link GL2GL3#GL_UNPACK_ROW_LENGTH}. See {@link #requiresNewBuffer(GL, int, int, int)}. */ + public final boolean allowRowStride; + /** The underlying {@link BufferedImage}. */ + public final BufferedImage image; + + /** + * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param width in pixels + * @param height in pixels + * @param depth in pixels + * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU + * @param image the AWT image + * @param buffer the backing array + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link #requiresNewBuffer(GL, int, int, int)}. + */ + public AWTGLPixelBuffer(GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, BufferedImage image, + Buffer buffer, boolean allowRowStride) { + super(pixelAttributes, width, height, depth, pack, buffer); + this.allowRowStride = allowRowStride; + this.image = image; + } + + /** + * {@inheritDoc} + * <p> + * If <code>{@link #allowRowStride} = false</code>, + * method returns <code>true</code> if the new size ≠ current size. + * </p> + * <p> + * If <code>{@link #allowRowStride} = true</code>, see {@link AWTGLPixelBufferProvider#AWTGLPixelBufferProvider(boolean)}, + * method returns <code>true</code> only if the new size > current size. Assuming user utilizes the row-stride + * when dealing w/ the data, i.e. {@link GL2GL3#GL_PACK_ROW_LENGTH}. + * </p> + */ + @Override + public boolean requiresNewBuffer(GL gl, int newWidth, int newHeight, int minByteSize) { + if( allowRowStride && gl.isGL2GL3() ) { + return width < newWidth || height < newHeight; + } else { + return width != newWidth || height != newHeight; + } + } + + @Override + public void dispose() { + image.flush(); + super.dispose(); + } + + public StringBuffer toString(StringBuffer sb) { + sb = super.toString(sb); + sb.append(", allowRowStride ").append(allowRowStride).append(", image [").append(image.getWidth()).append("x").append(image.getHeight()).append(", ").append(image.toString()).append("]"); + return sb; + } + public String toString() { + return "AWTGLPixelBuffer["+toString(null).toString()+"]"; + } + + /** + * Provider for {@link AWTGLPixelBuffer} instances. + */ + public static class AWTGLPixelBufferProvider implements GLPixelBufferProvider { + /** Allow {@link GL2GL3#GL_PACK_ROW_LENGTH}, or {@link GL2GL3#GL_UNPACK_ROW_LENGTH}. */ + public final boolean allowRowStride; + + /** + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + */ + public AWTGLPixelBufferProvider(boolean allowRowStride) { + this.allowRowStride = allowRowStride; + } + @Override + public GLPixelAttributes getAttributes(GL gl, int componentCount) { + return awtPixelAttributesIntRGB; + } + + /** + * {@inheritDoc} + * <p> + * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>. + * </p> + */ + @Override + public AWTGLPixelBuffer allocate(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize) { + final BufferedImage image = new BufferedImage(width, height, 4 == pixelAttributes.componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer ); + return new AWTGLPixelBuffer(pixelAttributes, width, height, depth, pack, image, ibuffer, allowRowStride); + } + } + + /** + * Provider for singleton {@link AWTGLPixelBuffer} instances. + * <p> + * Provider instance holds the last {@link AWTGLPixelBuffer} instance + * {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * A new {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocation} + * will return same instance, if a new buffer is not {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int) required}. + * The latter is true if size are compatible, hence <code>allowRowStride</code> should be enabled, if possible. + * </p> + */ + public static class SingleAWTGLPixelBufferProvider extends AWTGLPixelBufferProvider { + private AWTGLPixelBuffer single = null; + + /** + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + */ + public SingleAWTGLPixelBufferProvider(boolean allowRowStride) { + super(allowRowStride); + } + + /** + * {@inheritDoc} + * <p> + * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>. + * </p> + */ + @Override + public AWTGLPixelBuffer allocate(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize) { + if( null == single || single.requiresNewBuffer(gl, width, height, minByteSize) ) { + single = allocateImpl(pixelAttributes, width, height, depth, pack, minByteSize); + } + return single; + } + + private AWTGLPixelBuffer allocateImpl(GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize) { + final BufferedImage image = new BufferedImage(width, height, 4 == pixelAttributes.componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer ); + return new AWTGLPixelBuffer(pixelAttributes, width, height, depth, pack, image, ibuffer, allowRowStride); + } + + /** + * Initializes the single {@link AWTGLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * @return the newly initialized single {@link AWTGLPixelBuffer}, or null if already allocated. + */ + public AWTGLPixelBuffer initSingleton(int width, int height, int depth, boolean pack) { + if( null != single ) { + return null; + } + single = allocateImpl(AWTGLPixelBuffer.awtPixelAttributesIntRGB, width, height, depth, pack, 0); + return single; + } + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java index f26fec0d5..0edd53ca1 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java @@ -28,14 +28,11 @@ package com.jogamp.opengl.util.awt; import java.awt.image.BufferedImage; -import java.nio.Buffer; -import java.nio.IntBuffer; import javax.media.opengl.GL; -import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLProfile; import com.jogamp.opengl.util.GLReadBufferUtil; -import com.jogamp.opengl.util.texture.awt.AWTTextureData.AWTPixelBufferProviderInt; /** * {@link GLReadBufferUtil} specialization allowing to @@ -48,25 +45,15 @@ public class AWTGLReadBufferUtil extends GLReadBufferUtil { * * @param alpha */ - public AWTGLReadBufferUtil(boolean alpha) { - super(new AWTPixelBufferProviderInt(), alpha, false); + public AWTGLReadBufferUtil(GLProfile glp, boolean alpha) { + super(new AWTGLPixelBuffer.AWTGLPixelBufferProvider( glp.isGL2GL3() /* allowRowStride */ ), alpha, false); } - /** - * Returns the raw pixel Buffer, filled by {@link #readPixels(GLAutoDrawable, boolean)}. - * <p> - * Due to using {@link AWTPixelBufferProviderInt#allocate(int, int, int)}, - * returns an {@link IntBuffer} instance. - * </p> - */ - @Override - public Buffer getPixelBuffer() { return readPixelBuffer; } - - public BufferedImage getImage() { return ((AWTPixelBufferProviderInt)pixelBufferProvider).getImage(); } + public AWTGLPixelBuffer getAWTGLPixelBuffer() { return (AWTGLPixelBuffer)this.getPixelBuffer(); } public BufferedImage readPixelsToBufferedImage(GL gl, boolean awtOrientation) { if( readPixels(gl, awtOrientation) ) { - final BufferedImage image = getImage(); + final BufferedImage image = getAWTGLPixelBuffer().image; if( getTextureData().getMustFlipVertically() ) { ImageUtil.flipImageVertically(image); } @@ -78,7 +65,7 @@ public class AWTGLReadBufferUtil extends GLReadBufferUtil { final int[] ioWidth = new int[] { inWidth }; final int[] ioHeight= new int[] { inHeight }; if( readPixels(gl, inX, inY, ioWidth, ioHeight, awtOrientation) ) { - final BufferedImage image = getImage(); + final BufferedImage image = getAWTGLPixelBuffer().image; if( getTextureData().getMustFlipVertically() ) { ImageUtil.flipImageVertically(image); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java index 2f0c86255..dec1b43cf 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java @@ -38,14 +38,11 @@ package com.jogamp.opengl.util.texture; import java.nio.Buffer; -import java.nio.ByteBuffer; -import javax.media.opengl.GL; -import javax.media.opengl.GLContext; import javax.media.opengl.GLProfile; -import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.GLBuffers; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; /** * Represents the data for an OpenGL texture. This is separated from @@ -62,97 +59,10 @@ public class TextureData { /** ColorSpace of pixel data. */ public static enum ColorSpace { RGB, YCbCr, YCCK, CMYK }; - /** Pixel data attributes. */ - public static class PixelAttributes { - /** Undefinded instance of {@link PixelAttributes}, having format:=0 and type:= 0. */ - public static final PixelAttributes UNDEF = new PixelAttributes(0, 0); - - /** The OpenGL pixel data format */ - public final int format; - /** The OpenGL pixel data type */ - public final int type; - public PixelAttributes(int dataFormat, int dataType) { - this.format = dataFormat; - this.type = dataType; - } - public String toString() { - return "PixelAttributes[fFmt 0x"+Integer.toHexString(format)+", type 0x"+Integer.toHexString(type)+"]"; - } - } - /** Allows user to interface with another toolkit to define {@link PixelAttributes} and memory buffer to produce {@link TextureData}. */ - public static interface PixelBufferProvider { - /** Called first to determine {@link PixelAttributes}. */ - PixelAttributes getAttributes(GL gl, int componentCount); - - /** - * Returns true, if implementation requires a new buffer based on the new size - * and previous aquired {@link #getAttributes(GL, int) attributes} due to pixel alignment, otherwise false. - * @see #allocate(int, int, int) - */ - boolean requiresNewBuffer(int width, int height); - - /** - * Called after {@link #getAttributes(GL, int)} to retrieve the NIO or array backed pixel {@link Buffer}. - * <p> - * Being called to gather the initial {@link Buffer}, if the existing {@link Buffer} size is not sufficient, - * or if {@link #requiresNewBuffer(int, int)} returns false. - * </p> - * <p> - * Number of components was passed via {@link #getAttributes(GL, int)}. - * </p> - * <p> - * The returned buffer must have at least <code>minByteSize</code> {@link Buffer#remaining() remaining}. - * </p> - */ - Buffer allocate(int width, int height, int minByteSize); - - /** Dispose resources. */ - void dispose(); - } - /** - * Default {@link PixelBufferProvider} utilizing best match for {@link PixelAttributes} - * and {@link #allocate(int, int, int) allocating} a {@link ByteBuffer}. - */ - public static class DefPixelBufferProvider implements PixelBufferProvider { - @Override - public PixelAttributes getAttributes(GL gl, int componentCount) { - final GLContext ctx = gl.getContext(); - final int dFormat, dType; - - if(gl.isGL2GL3() && 3 == componentCount) { - dFormat = GL.GL_RGB; - dType = GL.GL_UNSIGNED_BYTE; - } else { - dFormat = ctx.getDefaultPixelDataFormat(); - dType = ctx.getDefaultPixelDataType(); - } - return new TextureData.PixelAttributes(dFormat, dType); - } - @Override - public boolean requiresNewBuffer(int width, int height) { - return false; - } - /** - * {@inheritDoc} - * <p> - * Returns an NIO {@link ByteBuffer} of <code>minByteSize</code>. - * </p> - */ - @Override - public final Buffer allocate(int width, int height, int minByteSize) { - return Buffers.newDirectByteBuffer(minByteSize); - } - - @Override - public void dispose() { - // nop - } - } - protected int width; protected int height; private int border; - protected PixelAttributes pixelAttributes; + protected GLPixelAttributes pixelAttributes; protected int internalFormat; // perhaps inferred from pixelFormat? protected boolean mipmap; // indicates whether mipmaps should be generated // (ignored if mipmaps are supplied from the file) @@ -225,7 +135,7 @@ public class TextureData { boolean mustFlipVertically, Buffer buffer, Flusher flusher) throws IllegalArgumentException { - this(glp, internalFormat, width, height, border, new PixelAttributes(pixelFormat, pixelType), + this(glp, internalFormat, width, height, border, new GLPixelAttributes(pixelFormat, pixelType), mipmap, dataIsCompressed, mustFlipVertically, buffer, flusher); } @@ -273,7 +183,7 @@ public class TextureData { int width, int height, int border, - PixelAttributes pixelAttributes, + GLPixelAttributes pixelAttributes, boolean mipmap, boolean dataIsCompressed, boolean mustFlipVertically, @@ -348,7 +258,7 @@ public class TextureData { boolean mustFlipVertically, Buffer[] mipmapData, Flusher flusher) throws IllegalArgumentException { - this(glp, internalFormat, width, height, border, new PixelAttributes(pixelFormat, pixelType), + this(glp, internalFormat, width, height, border, new GLPixelAttributes(pixelFormat, pixelType), dataIsCompressed, mustFlipVertically, mipmapData, flusher); } @@ -395,7 +305,7 @@ public class TextureData { int width, int height, int border, - PixelAttributes pixelAttributes, + GLPixelAttributes pixelAttributes, boolean dataIsCompressed, boolean mustFlipVertically, Buffer[] mipmapData, @@ -429,7 +339,7 @@ public class TextureData { public void setColorSpace(ColorSpace cs) { pixelCS = cs; } /** Used only by subclasses */ - protected TextureData(GLProfile glp) { this.glProfile = glp; this.pixelAttributes = PixelAttributes.UNDEF; } + protected TextureData(GLProfile glp) { this.glProfile = glp; this.pixelAttributes = GLPixelAttributes.UNDEF; } /** Returns the width in pixels of the texture data. */ public int getWidth() { return width; } @@ -439,8 +349,8 @@ public class TextureData { public int getBorder() { return border; } - /** Returns the intended OpenGL {@link PixelAttributes} of the texture data, i.e. format and type. */ - public PixelAttributes getPixelAttributes() { + /** Returns the intended OpenGL {@link GLPixelAttributes} of the texture data, i.e. format and type. */ + public GLPixelAttributes getPixelAttributes() { return pixelAttributes; } /** Returns the intended OpenGL pixel format of the texture data. */ @@ -495,27 +405,27 @@ public class TextureData { /** Sets the border in pixels of the texture data. */ public void setBorder(int border) { this.border = border; } /** Sets the intended OpenGL pixel format of the texture data. */ - public void setPixelAttributes(PixelAttributes pixelAttributes) { this.pixelAttributes = pixelAttributes; } + public void setPixelAttributes(GLPixelAttributes pixelAttributes) { this.pixelAttributes = pixelAttributes; } /** - * Sets the intended OpenGL pixel format component of {@link PixelAttributes} of the texture data. + * Sets the intended OpenGL pixel format component of {@link GLPixelAttributes} of the texture data. * <p> - * Use {@link #setPixelAttributes(PixelAttributes)}, if setting format and type. + * Use {@link #setPixelAttributes(GLPixelAttributes)}, if setting format and type. * </p> */ public void setPixelFormat(int pixelFormat) { if( pixelAttributes.format != pixelFormat ) { - pixelAttributes = new PixelAttributes(pixelFormat, pixelAttributes.type); + pixelAttributes = new GLPixelAttributes(pixelFormat, pixelAttributes.type); } } /** - * Sets the intended OpenGL pixel type component of {@link PixelAttributes} of the texture data. + * Sets the intended OpenGL pixel type component of {@link GLPixelAttributes} of the texture data. * <p> - * Use {@link #setPixelAttributes(PixelAttributes)}, if setting format and type. + * Use {@link #setPixelAttributes(GLPixelAttributes)}, if setting format and type. * </p> */ public void setPixelType(int pixelType) { if( pixelAttributes.type != pixelType) { - pixelAttributes = new PixelAttributes(pixelAttributes.format, pixelType); + pixelAttributes = new GLPixelAttributes(pixelAttributes.format, pixelType); } } /** Sets the intended OpenGL internal format of the texture data. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java index 7a0f00edf..d7e825c1d 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java @@ -66,62 +66,10 @@ import javax.media.opengl.GL2GL3; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.texture.TextureData; -import com.jogamp.opengl.util.texture.TextureData.PixelBufferProvider; public class AWTTextureData extends TextureData { - public static final PixelAttributes awtPixelAttributesIntRGB = new PixelAttributes(GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); - - /** - * AWT {@link PixelBufferProvider} backed by a {@link BufferedImage} of type - * {@link BufferedImage#TYPE_INT_ARGB} or {@link BufferedImage#TYPE_INT_RGB} - * and {@link #allocate(int, int, int) allocating} am array backed {@link IntBuffer}. - */ - public static final class AWTPixelBufferProviderInt implements PixelBufferProvider { - private BufferedImage image = null; - private int componentCount = 0; - - @Override - public PixelAttributes getAttributes(GL gl, int componentCount) { - this.componentCount = componentCount; - return awtPixelAttributesIntRGB; - } - @Override - public boolean requiresNewBuffer(int width, int height) { - return null == image || image.getWidth() != width || image.getHeight() != height; - } - /** - * {@inheritDoc} - * <p> - * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>. - * </p> - */ - @Override - public Buffer allocate(int width, int height, int minByteSize) { - image = new BufferedImage(width, height, 4 == componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); - final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); - return IntBuffer.wrap( readBackIntBuffer ); - } - - @Override - public void dispose() { - if(null != image) { - image.flush(); - image = null; - } - } - - /** Returns the number source components being used as indicated at {@link #allocate(int, int, int)}. */ - public int getComponentCount() { return componentCount; } - - /** Returns the underlying {@link BufferedImage} as allocated via {@link #allocate(int, int, int)}. */ - public BufferedImage getImage() { return image; } - - /** Returns true if an underlying {@link BufferedImage} has been allocated via {@link #allocate(int, int, int)}. */ - public boolean hasImage() { return null != image; } - } - // Mechanism for lazily converting input BufferedImages with custom // ColorModels to standard ones for uploading to OpenGL, as well as // backing off from the optimizations of hoping that either @@ -198,7 +146,7 @@ public class AWTTextureData extends TextureData { } @Override - public PixelAttributes getPixelAttributes() { + public GLPixelAttributes getPixelAttributes() { validatePixelAttributes(); return super.getPixelAttributes(); } @@ -229,7 +177,7 @@ public class AWTTextureData extends TextureData { } private void createFromImage(GLProfile glp, BufferedImage image) { - pixelAttributes = PixelAttributes.UNDEF; // Determine from image + pixelAttributes = GLPixelAttributes.UNDEF; // Determine from image mustFlipVertically = true; width = image.getWidth(); @@ -259,21 +207,21 @@ public class AWTTextureData extends TextureData { if (glp.isGL2GL3()) { switch (image.getType()) { case BufferedImage.TYPE_INT_RGB: - pixelAttributes = new PixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); + pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); rowLength = scanlineStride; alignment = 4; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_INT_ARGB_PRE: - pixelAttributes = new PixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); + pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); rowLength = scanlineStride; alignment = 4; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_INT_BGR: - pixelAttributes = new PixelAttributes(GL.GL_RGBA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); rowLength = scanlineStride; alignment = 4; expectingGL12 = true; @@ -284,7 +232,7 @@ public class AWTTextureData extends TextureData { // we can pass the image data directly to OpenGL only if // we have an integral number of pixels in each scanline if ((scanlineStride % 3) == 0) { - pixelAttributes = new PixelAttributes(GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 3; alignment = 1; } else { @@ -304,7 +252,7 @@ public class AWTTextureData extends TextureData { // the necessary byte swapping (FIXME: needs more // investigation) if ((scanlineStride % 4) == 0 && glp.isGL2() && false) { - pixelAttributes = new PixelAttributes(GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 4; alignment = 4; @@ -320,26 +268,26 @@ public class AWTTextureData extends TextureData { } } case BufferedImage.TYPE_USHORT_565_RGB: - pixelAttributes = new PixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5); + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_USHORT_555_RGB: - pixelAttributes = new PixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV); + pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_BYTE_GRAY: - pixelAttributes = new PixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride; alignment = 1; break; case BufferedImage.TYPE_USHORT_GRAY: - pixelAttributes = new PixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_SHORT); + pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_SHORT); rowLength = scanlineStride; alignment = 2; break; @@ -354,11 +302,11 @@ public class AWTTextureData extends TextureData { default: java.awt.image.ColorModel cm = image.getColorModel(); if (cm.equals(rgbColorModel)) { - pixelAttributes = new PixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 3; alignment = 1; } else if (cm.equals(rgbaColorModel)) { - pixelAttributes = new PixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 4; // FIXME: correct? alignment = 4; } else { @@ -370,7 +318,7 @@ public class AWTTextureData extends TextureData { } else { switch (image.getType()) { case BufferedImage.TYPE_INT_RGB: - pixelAttributes = new PixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride; alignment = 3; expectingGL12 = true; @@ -385,21 +333,21 @@ public class AWTTextureData extends TextureData { case BufferedImage.TYPE_4BYTE_ABGR_PRE: throw new GLException("INT_BGR n.a."); case BufferedImage.TYPE_USHORT_565_RGB: - pixelAttributes = new PixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5); + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_USHORT_555_RGB: - pixelAttributes = new PixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1); + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_BYTE_GRAY: - pixelAttributes = new PixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride; alignment = 1; break; @@ -416,11 +364,11 @@ public class AWTTextureData extends TextureData { default: java.awt.image.ColorModel cm = image.getColorModel(); if (cm.equals(rgbColorModel)) { - pixelAttributes = new PixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 3; alignment = 1; } else if (cm.equals(rgbaColorModel)) { - pixelAttributes = new PixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 4; // FIXME: correct? alignment = 4; } else { @@ -465,7 +413,7 @@ public class AWTTextureData extends TextureData { } else { throw new RuntimeException("Unexpected DataBuffer type?"); } - pixelAttributes = new PixelAttributes(pixelFormat, pixelType); + pixelAttributes = new GLPixelAttributes(pixelFormat, pixelType); } private void createFromCustom(BufferedImage image) { @@ -524,7 +472,7 @@ public class AWTTextureData extends TextureData { // and knowing we're in the process of doing the fallback code // path, re-infer a vanilla pixel format and type compatible with // OpenGL 1.1 - pixelAttributes = PixelAttributes.UNDEF; + pixelAttributes = GLPixelAttributes.UNDEF; setupLazyCustomConversion(imageForLazyCustomConversion); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 65bcab100..3f3e88977 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -86,10 +86,11 @@ import jogamp.opengl.awt.Java2D; import jogamp.opengl.util.glsl.GLSLTextureRaster; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.opengl.FBObject; -import com.jogamp.opengl.util.GLBuffers; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.GLPixelStorageModes; -import com.jogamp.opengl.util.texture.TextureData.PixelAttributes; -import com.jogamp.opengl.util.texture.awt.AWTTextureData.AWTPixelBufferProviderInt; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.AWTGLPixelBufferProvider; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.SingleAWTGLPixelBufferProvider; /** A lightweight Swing component which provides OpenGL rendering support. Provided for compatibility with Swing user interfaces @@ -133,12 +134,58 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private static final boolean DEBUG_VIEWPORT = Debug.isPropertyDefined("jogl.debug.GLJPanel.Viewport", true); private static final boolean USE_GLSL_TEXTURE_RASTERIZER = !Debug.isPropertyDefined("jogl.gljpanel.noglsl", true); + /** Indicates whether the Java 2D OpenGL pipeline is requested by user. */ + private static final boolean java2dOGLEnabledByProp; + + /** Indicates whether the Java 2D OpenGL pipeline is enabled, resource-compatible and requested by user. */ + private static final boolean useJava2DGLPipeline; + + /** Indicates whether the Java 2D OpenGL pipeline's usage is error free. */ + private static boolean java2DGLPipelineOK; + + static { + boolean enabled = false; + final String sVal = System.getProperty("sun.java2d.opengl"); + if( null != sVal ) { + enabled = Boolean.valueOf(sVal); + } + java2dOGLEnabledByProp = enabled && !Debug.isPropertyDefined("jogl.gljpanel.noogl", true); + + enabled = false; + if( java2dOGLEnabledByProp ) { + // Force eager initialization of part of the Java2D class since + // otherwise it's likely it will try to be initialized while on + // the Queue Flusher Thread, which is not allowed + if (Java2D.isOGLPipelineResourceCompatible() && Java2D.isFBOEnabled()) { + if( null != Java2D.getShareContext(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()) ) { + enabled = true; + } + } + } + useJava2DGLPipeline = enabled; + java2DGLPipelineOK = enabled; + if( DEBUG ) { + System.err.println("GLJPanel: java2dOGLEnabledByProp "+java2dOGLEnabledByProp); + System.err.println("GLJPanel: useJava2DGLPipeline "+useJava2DGLPipeline); + System.err.println("GLJPanel: java2DGLPipelineOK "+java2DGLPipelineOK); + } + } + + private static SingleAWTGLPixelBufferProvider singleAWTGLPixelBufferProvider = null; + private static synchronized SingleAWTGLPixelBufferProvider getSingleAWTGLPixelBufferProvider() { + if( null == singleAWTGLPixelBufferProvider ) { + singleAWTGLPixelBufferProvider = new SingleAWTGLPixelBufferProvider( true /* allowRowStride */ ); + } + return singleAWTGLPixelBufferProvider; + } + private GLDrawableHelper helper = new GLDrawableHelper(); private volatile boolean isInitialized; // // Data used for either pbuffers or pixmap-based offscreen surfaces // + private AWTGLPixelBufferProvider customPixelBufferProvider = null; /** Single buffered offscreen caps */ private GLCapabilitiesImmutable offscreenCaps; private GLProfile glProfile; @@ -170,10 +217,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Used by all backends either directly or indirectly to hook up callbacks private Updater updater = new Updater(); - // Indicates whether the Java 2D OpenGL pipeline is enabled and resource-compatible - private boolean oglPipelineUsable = - Java2D.isOGLPipelineResourceCompatible() && - !Debug.isPropertyDefined("jogl.gljpanel.noogl", true); + private boolean oglPipelineUsable() { + return null == customPixelBufferProvider && useJava2DGLPipeline && java2DGLPipelineOK; + } private AWTWindowClosingProtocol awtWindowClosingProtocol = new AWTWindowClosingProtocol(this, new Runnable() { @@ -183,17 +229,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } }, null); - static { - // Force eager initialization of part of the Java2D class since - // otherwise it's likely it will try to be initialized while on - // the Queue Flusher Thread, which is not allowed - if (Java2D.isOGLPipelineResourceCompatible() && Java2D.isFBOEnabled()) { - Java2D.getShareContext(GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getDefaultScreenDevice()); - } - } - /** Creates a new GLJPanel component with a default set of OpenGL capabilities and using the default OpenGL capabilities selection mechanism. @@ -252,6 +287,23 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing this.setFocusable(true); // allow keyboard input! } + public AWTGLPixelBufferProvider getCustomPixelBufferProvider() { return customPixelBufferProvider; } + + /** + * @param custom custom {@link AWTGLPixelBufferProvider} + * @throws IllegalArgumentException if <code>custom</code> is <code>null</code> + * @throws IllegalStateException if backend is already realized, i.e. this instanced already painted once. + */ + public void setPixelBufferProvider(AWTGLPixelBufferProvider custom) throws IllegalArgumentException, IllegalStateException { + if( null == custom ) { + throw new IllegalArgumentException("Null PixelBufferProvider"); + } + if( null != backend ) { + throw new IllegalStateException("Backend already realized."); + } + customPixelBufferProvider = custom; + } + @Override public final Object getUpstreamWidget() { return this; @@ -629,7 +681,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing to perform OpenGL rendering using the GLJPanel into the same OpenGL drawable as the Swing implementation uses. */ public boolean shouldPreserveColorBufferIfTranslucent() { - return oglPipelineUsable; + return oglPipelineUsable(); } @Override @@ -701,10 +753,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } if ( null == backend ) { - if (oglPipelineUsable) { + if ( oglPipelineUsable() ) { backend = new J2DOGLBackend(); } else { - backend = new OffscreenBackend(); + backend = new OffscreenBackend(glProfile, customPixelBufferProvider); } isInitialized = false; } @@ -922,14 +974,13 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // backends, both of which rely on reading back the OpenGL frame // buffer and drawing it with a BufferedImage class OffscreenBackend implements Backend { - protected AWTPixelBufferProviderInt pixelBufferProvider = new AWTPixelBufferProviderInt(); - private PixelAttributes pixelAttribs; + private final AWTGLPixelBufferProvider pixelBufferProvider; + private AWTGLPixelBuffer pixelBuffer; + private boolean pixelBufferCheckSize; // One of these is used to store the read back pixels before storing // in the BufferedImage - protected IntBuffer readBackInts; - protected int readBackWidthInPixels; - protected int readBackHeightInPixels; + protected IntBuffer readBackInts; // Implementation using software rendering private GLDrawableImpl offscreenDrawable; @@ -943,6 +994,15 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // For saving/restoring of OpenGL state during ReadPixels private final GLPixelStorageModes psm = new GLPixelStorageModes(); + OffscreenBackend(GLProfile glp, AWTGLPixelBufferProvider custom) { + if(null == custom) { + pixelBufferProvider = glp.isGL2GL3() ? getSingleAWTGLPixelBufferProvider() : + new AWTGLPixelBufferProvider( true /* allowRowStride */ ); + } else { + pixelBufferProvider = custom; + } + } + @Override public boolean isUsingOwnLifecycle() { return false; } @@ -1049,7 +1109,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public void setOpaque(boolean opaque) { if (opaque != isOpaque()) { - pixelBufferProvider.dispose(); + pixelBuffer.dispose(); + pixelBuffer = null; } } @@ -1076,30 +1137,38 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing alignment = 4; } + final GLPixelAttributes pixelAttribs; + + if( pixelBufferCheckSize ) { + pixelBufferCheckSize = false; + if( null != pixelBuffer && pixelBuffer.requiresNewBuffer(gl, panelWidth, panelHeight, 0) ) { + pixelBuffer.dispose(); + pixelBuffer = null; + } + } + // Must now copy pixels from offscreen context into surface - if ( !pixelBufferProvider.hasImage() ) { + if ( null == pixelBuffer ) { if (0 >= panelWidth || 0 >= panelHeight ) { return; } pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); - - final int[] tmp = { 0 }; - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.format, pixelAttribs.type, panelWidth, panelHeight, 1, true); - final IntBuffer intBuffer = (IntBuffer) pixelBufferProvider.allocate(panelWidth, panelHeight, readPixelSize); - if(!flipVertical || null != glslTextureRaster) { - readBackInts = intBuffer; + pixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, panelWidth, panelHeight, 1, true, 0); + if( !flipVertical || null != glslTextureRaster ) { + readBackInts = (IntBuffer) pixelBuffer.buffer; } else { - readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels); + readBackInts = IntBuffer.allocate(pixelBuffer.width * pixelBuffer.height); } if(DEBUG) { - final BufferedImage offscreenImage = pixelBufferProvider.getImage(); - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: pixelAttribs "+pixelAttribs); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: pixelBufferProvider 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment); System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster)); - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: panelSize "+panelWidth+"x"+panelHeight +", readBackSizeInPixels "+readBackWidthInPixels+"x"+readBackHeightInPixels); - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: offscreenImage "+offscreenImage.getWidth()+"x"+offscreenImage.getHeight()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: panelSize "+panelWidth+"x"+panelHeight); } - } + } else { + pixelAttribs = pixelBuffer.pixelAttributes; + } if( DEBUG_VIEWPORT ) { int[] vp = new int[] { 0, 0, 0, 0 }; @@ -1111,7 +1180,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing psm.setAlignment(gl, alignment, alignment); if(gl.isGL2GL3()) { final GL2GL3 gl2gl3 = gl.getGL2GL3(); - gl2gl3.glPixelStorei(GL2GL3.GL_PACK_ROW_LENGTH, readBackWidthInPixels); + gl2gl3.glPixelStorei(GL2GL3.GL_PACK_ROW_LENGTH, pixelBuffer.width); gl2gl3.glReadBuffer(gl2gl3.getDefaultReadBuffer()); } @@ -1127,27 +1196,25 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing gl.glBindTexture(GL.GL_TEXTURE_2D, fboTex.getName()); // gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // fboFlipped runs w/o DEPTH! glslTextureRaster.display(gl.getGL2ES2()); - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, pixelAttribs.format, pixelAttribs.type, readBackInts); + gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts); fboFlipped.unbind(gl); } else { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, pixelAttribs.format, pixelAttribs.type, readBackInts); + gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts); if ( flipVertical ) { // Copy temporary data into raster of BufferedImage for faster // blitting Note that we could avoid this copy in the cases - // where !offscreenContext.offscreenImageNeedsVerticalFlip(), - // but that's the software rendering path which is very slow - // anyway - final BufferedImage offscreenImage = pixelBufferProvider.getImage(); - final Object src = readBackInts.array(); - final Object dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); - final int srcIncr = readBackWidthInPixels; - final int destIncr = offscreenImage.getWidth(); + // where !offscreenDrawable.isGLOriented(), + // but that's the software rendering path which is very slow anyway. + final BufferedImage image = pixelBuffer.image; + final int[] src = readBackInts.array(); + final int[] dest = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + final int incr = pixelBuffer.width; int srcPos = 0; - int destPos = (offscreenImage.getHeight() - 1) * destIncr; - for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); + int destPos = (panelHeight - 1) * pixelBuffer.width; + for (; destPos >= 0; srcPos += incr, destPos -= incr) { + System.arraycopy(src, srcPos, dest, destPos, incr); } } } @@ -1164,13 +1231,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing public void doPaintComponent(Graphics g) { helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); - final BufferedImage offscreenImage = pixelBufferProvider.getImage(); - if ( null != offscreenImage ) { + if ( null != pixelBuffer ) { + final BufferedImage image = pixelBuffer.image; // Draw resulting image in one shot - g.drawImage(offscreenImage, 0, 0, - offscreenImage.getWidth(), - offscreenImage.getHeight(), - null /* Null ImageObserver since image data is ready. */); + g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null); // Null ImageObserver since image data is ready. } } @@ -1201,8 +1265,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } panelWidth = _drawable.getWidth(); panelHeight = _drawable.getHeight(); - readBackWidthInPixels = panelWidth; - readBackHeightInPixels = panelHeight; if( null != glslTextureRaster ) { if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { @@ -1216,7 +1278,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } - pixelBufferProvider.dispose(); + pixelBufferCheckSize = true; return _drawable.isRealized(); } @@ -1618,7 +1680,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } isInitialized = false; backend = null; - oglPipelineUsable = false; + java2DGLPipelineOK = false; handleReshape = true; j2dContext.destroy(); j2dContext = null; |