diff options
Diffstat (limited to 'src/jogl')
9 files changed, 564 insertions, 232 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java index a09321d75..00bbd6ce7 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java @@ -58,41 +58,100 @@ public class GLPixelBuffer { /** Allow {@link GL2ES3#GL_PACK_ROW_LENGTH}, or {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. */ boolean getAllowRowStride(); - /** Called first to determine {@link GLPixelAttributes}. */ - GLPixelAttributes getAttributes(GL gl, int componentCount); + /** + * Returns RGB[A] {@link GLPixelAttributes} matching {@link GL}, {@code componentCount} and {@code pack}. + * + * @param gl the corresponding current {@link GL} context object + * @param componentCount RGBA component count, i.e. 1 (luminance, alpha or red), 3 (RGB) or 4 (RGBA) + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. + */ + GLPixelAttributes getAttributes(GL gl, int componentCount, boolean pack); + + /** + * Returns the host {@link PixelFormat.Composition} matching {@link GL} and {@code componentCount} + * if required by implementation, otherwise {@code null}. + * + * @param glp the corresponding current {@link GL} context object + * @param componentCount RGBA component count, i.e. 1 (luminance, alpha or red), 3 (RGB) or 4 (RGBA) + */ + PixelFormat.Composition getHostPixelComp(final GLProfile glp, final 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 gl the corresponding current {@link GL} context object + * @param hostPixComp host {@link PixelFormat pixel format}, i.e. of the source or sink depending on {@code pack}, + * e.g. fetched via {@link #getHostPixelComp(GLProfile, int)}. + * If {@code null}, {@code pixelAttributes} instance maybe used or an exception is thrown, + * depending on implementation semantics. + * @param pixelAttributes the desired {@link GLPixelAttributes}, e.g. fetched via {@link #getAttributes(GL, int, boolean)} + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. * @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. + * @see #getHostPixelComp(GLProfile, int) + * @see #getAttributes(GL, int, boolean) */ - GLPixelBuffer allocate(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize); + GLPixelBuffer allocate(GL gl, PixelFormat.Composition hostPixComp, GLPixelAttributes pixelAttributes, + boolean pack, int width, int height, int depth, int minByteSize); } /** Single {@link GLPixelBuffer} provider. */ public static interface SingletonGLPixelBufferProvider extends GLPixelBufferProvider { - /** Return the last {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated} {@link GLPixelBuffer} w/ {@link GLPixelAttributes#componentCount}. */ - GLPixelBuffer getSingleBuffer(GLPixelAttributes pixelAttributes); /** - * Initializes the single {@link GLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * {@inheritDoc} + * <p> + * Being called to gather the initial {@link GLPixelBuffer}, + * or a new replacement {@link GLPixelBuffer} if {@link GLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + * </p> + */ + @Override + GLPixelBuffer allocate(GL gl, PixelFormat.Composition hostPixComp, GLPixelAttributes pixelAttributes, + boolean pack, int width, int height, int depth, int minByteSize); + + /** + * Return the last {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated} {@link GLPixelBuffer} + * matching the given parameter. + * <p> + * May return {@code null} if none has been allocated yet. + * </p> + * <p> + * Returned {@link GLPixelBuffer} may be {@link GLPixelBuffer#isValid() invalid}. + * </p> + * @param hostPixComp host {@link PixelFormat pixel format}, i.e. of the source or sink depending on {@code pack}, + * e.g. fetched via {@link #getHostPixelComp(GLProfile, int)}. + * If {@code null}, {@code pixelAttributes} instance maybe used or an exception is thrown, + * depending on implementation semantics. + * @param pixelAttributes the desired {@link GLPixelAttributes}, e.g. fetched via {@link #getAttributes(GL, int, boolean)} + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. + */ + GLPixelBuffer getSingleBuffer(PixelFormat.Composition hostPixelComp, GLPixelAttributes pixelAttributes, boolean pack); + /** + * Initializes the single {@link GLPixelBuffer} w/ a given size, + * if not yet {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated}. + * + * @param glp + * @param componentCount RGBA component count, i.e. 1 (luminance, alpha or red), 3 (RGB) or 4 (RGBA) + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. + * @param width + * @param height + * @param depth * @return the newly initialized single {@link GLPixelBuffer}, or null if already allocated. */ - GLPixelBuffer initSingleton(int componentCount, int width, int height, int depth, boolean pack); + GLPixelBuffer initSingleton(GLProfile glp, int componentCount, boolean pack, int width, int height, int depth); + + /** Dispose all resources.*/ + void dispose(); } public static class DefaultGLPixelBufferProvider implements GLPixelBufferProvider { @@ -110,36 +169,24 @@ public class GLPixelBuffer { public boolean getAllowRowStride() { return allowRowStride; } @Override - public GLPixelAttributes getAttributes(final GL gl, final int componentCount) { - final GLContext ctx = gl.getContext(); - final int dFormat, dType; - - if( 1 == componentCount ) { - if( gl.isGL3ES3() ) { - // RED is supported on ES3 and >= GL3 [core]; ALPHA is deprecated on core - dFormat = GL2ES2.GL_RED; - } else { - // ALPHA is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility - dFormat = GL.GL_ALPHA; - } - dType = GL.GL_UNSIGNED_BYTE; - } else if( 3 == componentCount ) { - dFormat = GL.GL_RGB; - dType = GL.GL_UNSIGNED_BYTE; - } else if( 4 == componentCount ) { - final int _dFormat = ctx.getDefaultPixelDataFormat(); - final int dComps = GLBuffers.componentCount(_dFormat); - if( dComps == componentCount ) { - dFormat = _dFormat; - dType = ctx.getDefaultPixelDataType(); - } else { - dFormat = GL.GL_RGBA; - dType = GL.GL_UNSIGNED_BYTE; - } - } else { + public GLPixelAttributes getAttributes(final GL gl, final int componentCount, final boolean pack) { + final GLPixelAttributes res = GLPixelAttributes.convert(gl, componentCount, pack); + if( null == res ) { throw new GLException("Unsupported componentCount "+componentCount+", contact maintainer to enhance"); + } else { + return res; } - return new GLPixelAttributes(componentCount, dFormat, dType); + } + + /** + * {@inheritDoc} + * <p> + * Returns {@code null}! + * </p> + */ + @Override + public PixelFormat.Composition getHostPixelComp(final GLProfile glp, final int componentCount) { + return null; } /** @@ -149,13 +196,15 @@ public class GLPixelBuffer { * </p> */ @Override - public GLPixelBuffer allocate(final GL gl, final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { + public GLPixelBuffer allocate(final GL gl, final PixelFormat.Composition hostPixComp, final GLPixelAttributes pixelAttributes, + final boolean pack, final int width, final int height, final int depth, final int minByteSize) { + // unused: hostPixComp if( minByteSize > 0 ) { - return new GLPixelBuffer(pixelAttributes, width, height, depth, pack, Buffers.newDirectByteBuffer(minByteSize), getAllowRowStride()); + return new GLPixelBuffer(pixelAttributes, pack, width, height, depth, Buffers.newDirectByteBuffer(minByteSize), getAllowRowStride()); } else { final 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), getAllowRowStride()); + final int byteSize = GLBuffers.sizeof(gl, tmp, pixelAttributes.pfmt.comp.bytesPerPixel(), width, height, depth, pack); + return new GLPixelBuffer(pixelAttributes, pack, width, height, depth, Buffers.newDirectByteBuffer(byteSize), getAllowRowStride()); } } } @@ -163,74 +212,190 @@ public class GLPixelBuffer { /** * Default {@link GLPixelBufferProvider} with {@link GLPixelBufferProvider#getAllowRowStride()} == <code>false</code>, * utilizing best match for {@link GLPixelAttributes} - * and {@link GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocating} a {@link ByteBuffer}. + * and {@link GLPixelBufferProvider#allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocating} a {@link ByteBuffer}. */ public static final GLPixelBufferProvider defaultProviderNoRowStride = new DefaultGLPixelBufferProvider(false); /** * Default {@link GLPixelBufferProvider} with {@link GLPixelBufferProvider#getAllowRowStride()} == <code>true</code>, * utilizing best match for {@link GLPixelAttributes} - * and {@link GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocating} a {@link ByteBuffer}. + * and {@link GLPixelBufferProvider#allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocating} a {@link ByteBuffer}. */ public static final GLPixelBufferProvider defaultProviderWithRowStride = new DefaultGLPixelBufferProvider(true); /** 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, false); - - /** Pixel <i>source</i> component count, i.e. number of meaningful components. */ - 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; + public static final GLPixelAttributes UNDEF = new GLPixelAttributes(null, PixelFormat.LUMINANCE, 0, 0, true, false); /** - * 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 + * Returns the matching {@link PixelFormat} for the given GL format and type if exists, + * otherwise returns <code>null</code>. */ - public GLPixelAttributes(final int dataFormat, final int dataType) { - this(0 < dataFormat ? GLBuffers.componentCount(dataFormat) : 0, dataFormat, dataType); + public static final PixelFormat getPixelFormat(final int glFormat, final int glDataType) { + PixelFormat pixFmt = null; + + switch(glFormat) { + case GL.GL_ALPHA: + case GL.GL_LUMINANCE: + case GL2ES2.GL_RED: + pixFmt = PixelFormat.LUMINANCE; + break; + case GL.GL_RGB: + switch(glDataType) { + case GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV: + pixFmt = PixelFormat.RGB565; + break; + case GL.GL_UNSIGNED_SHORT_5_6_5: + pixFmt = PixelFormat.BGR565; + break; + case GL.GL_UNSIGNED_BYTE: + pixFmt = PixelFormat.RGB888; + break; + } + break; + case GL.GL_RGBA: + switch(glDataType) { + case GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV: + pixFmt = PixelFormat.RGBA5551; + break; + case GL.GL_UNSIGNED_SHORT_5_5_5_1: + pixFmt = PixelFormat.ABGR1555; + break; + case GL.GL_UNSIGNED_BYTE: + pixFmt = PixelFormat.RGBA8888; + break; + case GL2GL3.GL_UNSIGNED_INT_8_8_8_8: + pixFmt = PixelFormat.ABGR8888; + break; + } + break; + case GL2GL3.GL_BGR: + if( GL.GL_UNSIGNED_BYTE == glDataType ) { + pixFmt = PixelFormat.BGR888; + } + break; + case GL.GL_BGRA: + switch(glDataType) { + case GL2GL3.GL_UNSIGNED_INT_8_8_8_8: + pixFmt = PixelFormat.ARGB8888; + break; + case GL.GL_UNSIGNED_BYTE: + pixFmt = PixelFormat.BGRA8888; + break; + } + break; + } + return pixFmt; } + /** - * Using user specified source {@link #componentCount}. - * @param componentCount source component count - * @param dataFormat GL data format - * @param dataType GL data type + * Returns the matching {@link GLPixelAttributes} for the given byte sized RGBA {@code componentCount} and {@link GL} if exists, + * otherwise returns {@code null}. + * + * @param gl the corresponding current {@link GL} context object + * @param componentCount RGBA component count, i.e. 1 (luminance, alpha or red), 3 (RGB) or 4 (RGBA) + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. */ - public GLPixelAttributes(final int componentCount, final int dataFormat, final int dataType) { - this(componentCount, dataFormat, dataType, true); + public static GLPixelAttributes convert(final GL gl, final int componentCount, final boolean pack) { + final int dFormat, dType; + final boolean glesReadMode = pack && gl.isGLES(); + + if( 1 == componentCount && !glesReadMode ) { + if( gl.isGL3ES3() ) { + // RED is supported on ES3 and >= GL3 [core]; ALPHA is deprecated on core + dFormat = GL2ES2.GL_RED; + } else { + // ALPHA is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility + dFormat = GL.GL_ALPHA; + } + dType = GL.GL_UNSIGNED_BYTE; + } else if( 3 == componentCount && !glesReadMode ) { + dFormat = GL.GL_RGB; + dType = GL.GL_UNSIGNED_BYTE; + } else if( 4 == componentCount || glesReadMode ) { + final GLContext ctx = gl.getContext(); + final int _dFormat = ctx.getDefaultPixelDataFormat(); + final int dComps = GLBuffers.componentCount(_dFormat); + if( dComps == componentCount || 4 == dComps ) { // accept if desired component count or 4 components + dFormat = _dFormat; + dType = ctx.getDefaultPixelDataType(); + } else { + dFormat = GL.GL_RGBA; + dType = GL.GL_UNSIGNED_BYTE; + } + } else { + return null; + } + return new GLPixelAttributes(dFormat, dType); } /** - * Returns the matching {@link GLPixelAttributes} for the given {@link PixelFormat} and {@link GLProfile} if exists, - * otherwise returns <code>null</code>. + * Returns the matching {@link GLPixelAttributes} for the given {@link GLProfile}, {@link PixelFormat} and {@code pack} if exists, + * otherwise returns {@code null}. + * @param glp the corresponding {@link GLProfile} + * @param pixFmt the to be matched {@link PixelFormat pixel format} + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. */ - public static final GLPixelAttributes convert(final PixelFormat pixFmt, final GLProfile glp) { + public static final GLPixelAttributes convert(final GLProfile glp, final PixelFormat pixFmt, final boolean pack) { + final int[] df = new int[1]; + final int[] dt = new int[1]; + convert(glp, pixFmt, pack, df, dt); + if( 0 != df[0] ) { + return new GLPixelAttributes(null, pixFmt, df[0], dt[0], true /* not used */, true); + } + return null; + } + private static final int convert(final GLProfile glp, final PixelFormat pixFmt, final boolean pack, + final int[] dfRes, final int[] dtRes) { + final boolean glesReadMode = pack && glp.isGLES(); int df = 0; // format int dt = GL.GL_UNSIGNED_BYTE; // data type switch(pixFmt) { case LUMINANCE: - if( glp.isGL3ES3() ) { - // RED is supported on ES3 and >= GL3 [core]; ALPHA/LUMINANCE is deprecated on core - df = GL2ES2.GL_RED; - } else { - // ALPHA/LUMINANCE is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility - df = GL.GL_LUMINANCE; + if( !glesReadMode ) { + if( glp.isGL3ES3() ) { + // RED is supported on ES3 and >= GL3 [core]; ALPHA/LUMINANCE is deprecated on core + df = GL2ES2.GL_RED; + } else { + // ALPHA/LUMINANCE is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility + df = GL.GL_LUMINANCE; + } } break; - case BGR888: + case RGB565: if( glp.isGL2GL3() ) { - df = GL2GL3.GL_BGR; + df = GL.GL_RGB; dt = GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV; + } + break; + case BGR565: + if( glp.isGL2GL3() ) { + df = GL.GL_RGB; dt = GL.GL_UNSIGNED_SHORT_5_6_5; + } + break; + case RGBA5551: + if( glp.isGL2GL3() ) { + df = GL.GL_RGBA; dt = GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV; + } + break; + case ABGR1555: + if( glp.isGL2GL3() ) { + df = GL.GL_RGBA; dt = GL.GL_UNSIGNED_SHORT_5_5_5_1; } break; case RGB888: - df = GL.GL_RGB; + if( !glesReadMode ) { + df = GL.GL_RGB; + } + break; + case BGR888: + if( glp.isGL2GL3() ) { + df = GL2GL3.GL_BGR; + } break; + case RGBx8888: case RGBA8888: df = GL.GL_RGBA; break; @@ -239,85 +404,107 @@ public class GLPixelBuffer { df = GL.GL_RGBA; dt = GL2GL3.GL_UNSIGNED_INT_8_8_8_8; } break; - case BGRA8888: - df = GL.GL_BGRA; - break; case ARGB8888: if( glp.isGL2GL3() ) { df = GL.GL_BGRA; dt = GL2GL3.GL_UNSIGNED_INT_8_8_8_8; } break; - default: + case BGRx8888: + case BGRA8888: + if( glp.isGL2GL3() ) { // FIXME: or if( !glesReadMode ) ? BGRA n/a on GLES + df = GL.GL_BGRA; + } break; } - if( 0 != df ) { - return new GLPixelAttributes(pixFmt.componentCount, df, dt, true); - } - return null; + dfRes[0] = df; + dtRes[0] = dt; + return df; } - private GLPixelAttributes(final int componentCount, final int dataFormat, final int dataType, final boolean checkArgs) { - this.componentCount = componentCount; - this.format = dataFormat; - this.type = dataType; - this.bytesPerPixel = ( 0 < dataFormat && 0 < dataType ) ? GLBuffers.bytesPerPixel(dataFormat, dataType) : 0; - if( checkArgs ) { - if( 0 == componentCount || 0 == format || 0 == type ) { - throw new GLException("Zero components, format and/or type: "+this); - } - if( 0 == bytesPerPixel ) { - throw new GLException("Zero bytesPerPixel: "+this); - } + + /** The OpenGL pixel data format */ + public final int format; + /** The OpenGL pixel data type */ + public final int type; + + /** {@link PixelFormat} describing the {@link PixelFormat.Composition component} layout */ + public final PixelFormat pfmt; + + @Override + public final int hashCode() { + // 31 * x == (x << 5) - x + int hash = pfmt.hashCode(); + hash = ((hash << 5) - hash) + format; + return ((hash << 5) - hash) + type; + } + + @Override + public final boolean equals(final Object obj) { + if(this == obj) { return true; } + if( obj instanceof GLPixelAttributes ) { + final GLPixelAttributes other = (GLPixelAttributes) obj; + return format == other.format && + type == other.type && + pfmt.equals(other.pfmt); + } else { + return false; } } /** - * Returns the matching {@link PixelFormat} of this {@link GLPixelAttributes} if exists, - * otherwise returns <code>null</code>. + * Create a new {@link GLPixelAttributes} instance based on GL format and type. + * @param dataFormat GL data format + * @param dataType GL data type + * @throws GLException if {@link PixelFormat} could not be determined, see {@link #getPixelFormat(int, int)}. */ - public final PixelFormat getPixelFormat() { - final PixelFormat pixFmt; - // FIXME: Take 'type' into consideration and complete mapping! - switch(format) { - case GL.GL_ALPHA: - case GL.GL_LUMINANCE: - case GL2ES2.GL_RED: - pixFmt = PixelFormat.LUMINANCE; - break; - case GL.GL_RGB: - pixFmt = PixelFormat.RGB888; - break; - case GL.GL_RGBA: - pixFmt = PixelFormat.RGBA8888; - break; - case GL2GL3.GL_BGR: - pixFmt = PixelFormat.BGR888; - break; - case GL.GL_BGRA: - pixFmt = PixelFormat.BGRA8888; - break; - default: - switch( bytesPerPixel ) { - case 1: - pixFmt = PixelFormat.LUMINANCE; - break; - case 3: - pixFmt = PixelFormat.RGB888; - break; - case 4: - pixFmt = PixelFormat.RGBA8888; - break; - default: - pixFmt = null; - break; - } - break; + public GLPixelAttributes(final int dataFormat, final int dataType) throws GLException { + this(null, null, dataFormat, dataType, true /* not used */, true); + } + + /** + * Create a new {@link GLPixelAttributes} instance based on {@link GLProfile}, {@link PixelFormat} and {@code pack}. + * @param glp the corresponding {@link GLProfile} + * @param pixFmt the to be matched {@link PixelFormat pixel format} + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. + * @throws GLException if GL format or type could not be determined, see {@link #convert(GLProfile, PixelFormat, boolean)}. + */ + public GLPixelAttributes(final GLProfile glp, final PixelFormat pixFmt, final boolean pack) throws GLException { + this(glp, pixFmt, 0, 0, pack, true); + } + + private GLPixelAttributes(final GLProfile glp, final PixelFormat pixFmt, + final int dataFormat, final int dataType, final boolean pack, final boolean checkArgs) throws GLException { + if( checkArgs && ( 0 == dataFormat || 0 == dataType ) ) { + if( null == pixFmt || null == glp ) { + throw new GLException("Zero format and/or type w/o pixFmt or glp: "+this); + } + final int[] df = new int[1]; + final int[] dt = new int[1]; + if( 0 == convert(glp, pixFmt, pack, df, dt) ) { + throw new GLException("Could not find format and type for "+pixFmt+" and "+glp+", "+this); + } + this.format = df[0]; + this.type = dt[0]; + this.pfmt = pixFmt; + } else { + this.format = dataFormat; + this.type = dataType; + this.pfmt = null != pixFmt ? pixFmt : getPixelFormat(dataFormat, dataType); + if( null == this.pfmt ) { + throw new GLException("Could not find PixelFormat for format and/or type: "+this); + } + } + if( checkArgs ) { + final int bytesPerPixel = GLBuffers.bytesPerPixel(this.format, this.type); + if( 0 == bytesPerPixel ) { + throw new GLException("Zero bytesPerPixel: "+this); + } } - return pixFmt; } @Override public String toString() { - return "PixelAttributes[comp "+componentCount+", fmt 0x"+Integer.toHexString(format)+", type 0x"+Integer.toHexString(type)+", bytesPerPixel "+bytesPerPixel+"]"; + return "PixelAttributes[fmt 0x"+Integer.toHexString(format)+", type 0x"+Integer.toHexString(type)+", "+pfmt+"]"; } } @@ -339,14 +526,18 @@ public class GLPixelBuffer { 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. */ + /** + * Data packing direction. + * <p>{@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}.</p> + * <p>{@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}.</p> + */ 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)}. + * By default the {@link Buffer} is a {@link ByteBuffer}, due to {@link DefProvider#allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int)}. * However, other {@link GLPixelBufferProvider} may utilize different {@link Buffer} types. * </p> */ @@ -375,14 +566,16 @@ public class GLPixelBuffer { /** * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. * @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 buffer the backing array * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link #requiresNewBuffer(GL, int, int, int)}. + * @param hostPixelComp the host {@link PixelFormat.Composition} */ - public GLPixelBuffer(final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final Buffer buffer, final boolean allowRowStride) { + public GLPixelBuffer(final GLPixelAttributes pixelAttributes, final boolean pack, final int width, final int height, final int depth, final Buffer buffer, final boolean allowRowStride) { this.pixelAttributes = pixelAttributes; this.width = width; this.height = height; @@ -462,7 +655,7 @@ public class GLPixelBuffer { * @param newWidth new width in pixels * @param newHeight new height in pixels * @param newByteSize if > 0, the pre-calculated minimum byte-size for the resulting buffer, otherwise ignore. - * @see GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) + * @see GLPixelBufferProvider#allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) */ public boolean requiresNewBuffer(final GL gl, final int newWidth, final int newHeight, int newByteSize) { if( !isValid() ) { @@ -470,7 +663,7 @@ public class GLPixelBuffer { } if( 0 >= newByteSize ) { final int[] tmp = { 0 }; - newByteSize = GLBuffers.sizeof(gl, tmp, pixelAttributes.bytesPerPixel, newWidth, newHeight, 1, true); + newByteSize = GLBuffers.sizeof(gl, tmp, pixelAttributes.pfmt.comp.bytesPerPixel(), newWidth, newHeight, 1, true); } if( allowRowStride ) { return byteSize < newByteSize; diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java index e84a1d874..597498c9d 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java @@ -31,6 +31,7 @@ package com.jogamp.opengl.util; import java.io.File; import java.io.IOException; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2ES3; import javax.media.opengl.GLAutoDrawable; @@ -51,10 +52,10 @@ import com.jogamp.opengl.util.texture.TextureIO; */ public class GLReadBufferUtil { protected final GLPixelBufferProvider pixelBufferProvider; - protected final int componentCount, alignment; protected final Texture readTexture; protected final GLPixelStorageModes psm; + protected boolean hasAlpha; protected GLPixelBuffer readPixelBuffer = null; protected TextureData readTextureData = null; @@ -68,10 +69,9 @@ public class GLReadBufferUtil { public GLReadBufferUtil(final GLPixelBufferProvider pixelBufferProvider, final boolean alpha, final boolean write2Texture) { this.pixelBufferProvider = pixelBufferProvider; - this.componentCount = alpha ? 4 : 3 ; - this.alignment = alpha ? 4 : 1 ; this.readTexture = write2Texture ? new Texture(GL.GL_TEXTURE_2D) : null ; this.psm = new GLPixelStorageModes(); + this.hasAlpha = alpha; // preset } /** Returns the {@link GLPixelBufferProvider} used by this instance. */ @@ -81,7 +81,7 @@ public class GLReadBufferUtil { return null!=readTextureData && null!=readPixelBuffer && readPixelBuffer.isValid(); } - public boolean hasAlpha() { return 4 == componentCount ? true : false ; } + public boolean hasAlpha() { return hasAlpha; } public GLPixelStorageModes getGLPixelStorageModes() { return psm; } @@ -173,13 +173,13 @@ public class GLReadBufferUtil { if(GL.GL_NO_ERROR != glerr0) { System.err.println("Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x"+Integer.toHexString(glerr0)); } - final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); - final int internalFormat; - if(gl.isGL2GL3() && 3 == componentCount) { - internalFormat = GL.GL_RGB; - } else { - internalFormat = (4 == componentCount) ? GL.GL_RGBA : GL.GL_RGB; - } + final int reqCompCount = hasAlpha ? 4 : 3; + final PixelFormat.Composition hostPixelComp = pixelBufferProvider.getHostPixelComp(gl.getGLProfile(), reqCompCount); + final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, reqCompCount, true); + final int componentCount = pixelAttribs.pfmt.comp.componenCount(); + hasAlpha = 0 <= pixelAttribs.pfmt.comp.find(PixelFormat.CType.A); + final int alignment = 4 == componentCount ? 4 : 1 ; + final int internalFormat = 4 == componentCount ? GL.GL_RGBA : GL.GL_RGB; final boolean flipVertically; if( drawable.isGLOriented() ) { @@ -189,11 +189,11 @@ public class GLReadBufferUtil { } final int tmp[] = new int[1]; - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, width, height, 1, true); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.pfmt.comp.bytesPerPixel(), width, height, 1, true); boolean newData = false; if( null == readPixelBuffer || readPixelBuffer.requiresNewBuffer(gl, width, height, readPixelSize) ) { - readPixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, width, height, 1, true, readPixelSize); + readPixelBuffer = pixelBufferProvider.allocate(gl, hostPixelComp, pixelAttribs, true, width, height, 1, readPixelSize); Buffers.rangeCheckBytes(readPixelBuffer.buffer, readPixelSize); try { readTextureData = new TextureData( diff --git a/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java index 64da547c2..29e1cf353 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java +++ b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java @@ -137,7 +137,7 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { } else { destFmt = ddestFmt; // user choice } - final int destStrideInBytes = Math.max(destMinStrideInBytes, destFmt.bytesPerPixel() * width); + final int destStrideInBytes = Math.max(destMinStrideInBytes, destFmt.comp.bytesPerPixel() * width); final ByteBuffer destPixels = destDirectBuffer ? Buffers.newDirectByteBuffer(destStrideInBytes * height) : ByteBuffer.allocate(destStrideInBytes * height); { @@ -153,7 +153,7 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { System.err.println("PNGPixelRect: indexed "+indexed+", alpha "+hasAlpha+", grayscale "+imgInfo.greyscale+", channels "+channels+"/"+imgInfo.channels+ ", bytesPerPixel "+bytesPerPixel+"/"+imgInfo.bytesPixel+ ", grayAlpha "+isGrayAlpha+", pixels "+width+"x"+height+", dpi "+dpiX+"x"+dpiY+", format "+srcFmt); - System.err.println("PNGPixelRect: destFormat "+destFmt+" ("+ddestFmt+", bytesPerPixel "+destFmt.bytesPerPixel()+", fast-path "+(destFmt==srcFmt)+"), destDirectBuffer "+destDirectBuffer+", destIsGLOriented (flip) "+destIsGLOriented); + System.err.println("PNGPixelRect: destFormat "+destFmt+" ("+ddestFmt+", fast-path "+(destFmt==srcFmt)+"), destDirectBuffer "+destDirectBuffer+", destIsGLOriented (flip) "+destIsGLOriented); System.err.println("PNGPixelRect: destStrideInBytes "+destStrideInBytes+" (destMinStrideInBytes "+destMinStrideInBytes+")"); } @@ -227,7 +227,7 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { (byte)scanline[lineOff+1], // G (byte)scanline[lineOff+2], // B srcHasAlpha ? (byte)scanline[lineOff+3] : (byte)0xff); // A - final int dbpp = dest_fmt.bytesPerPixel(); + final int dbpp = dest_fmt.comp.bytesPerPixel(); d.put(dOff++, (byte) ( p )); // 1 if( 1 < dbpp ) { d.put(dOff++, (byte) ( p >>> 8 )); // 2 @@ -261,7 +261,7 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { if(hasAlpha) { line.scanline[lineOff + 3] = 0xff & ( p >>> 24 ); // A } - return srcOff + pixelformat.bytesPerPixel(); + return srcOff + pixelformat.comp.bytesPerPixel(); } private static void setPixelRGBA8(final PixelFormat pixelformat, final ImageLine line, final int lineOff, final int srcPix, final int bytesPerPixel, final boolean hasAlpha) { @@ -304,7 +304,7 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { public void write(final OutputStream outstream, final boolean closeOutstream) throws IOException { final int width = size.getWidth(); final int height = size.getHeight(); - final int bytesPerPixel = pixelformat.bytesPerPixel(); + final int bytesPerPixel = pixelformat.comp.bytesPerPixel(); final ImageInfo imi = new ImageInfo(width, height, 8 /* bitdepth */, (4 == bytesPerPixel) ? true : false /* alpha */, (1 == bytesPerPixel) ? true : false /* grayscale */, @@ -349,7 +349,7 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { final OutputStream outstream, final boolean closeOutstream) throws IOException { final int width = size.getWidth(); final int height = size.getHeight(); - final int bytesPerPixel = pixelformat.bytesPerPixel(); + final int bytesPerPixel = pixelformat.comp.bytesPerPixel(); final ImageInfo imi = new ImageInfo(width, height, 8 /* bitdepth */, (4 == bytesPerPixel) ? true : false /* alpha */, (1 == bytesPerPixel) ? true : false /* grayscale */, diff --git a/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java index 3b65b0824..bf7cf668c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java @@ -176,7 +176,7 @@ public class RandomTileRenderer extends TileRendererBase { final int srcY = 0; final int srcWidth = currentTileWidth; final int srcHeight = currentTileHeight; - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.pfmt.comp.bytesPerPixel(), srcWidth, srcHeight, 1, true); tileBuffer.clear(); if( tileBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { throw new IndexOutOfBoundsException("Required " + readPixelSize + " bytes of buffer, only had " + tileBuffer); @@ -200,9 +200,9 @@ public class RandomTileRenderer extends TileRendererBase { psm.setPackRowLength(gl2es3, rowLength); /* read the tile into the final image */ - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.pfmt.comp.bytesPerPixel(), srcWidth, srcHeight, 1, true); - final int ibPos = ( currentTileXPos + ( currentTileYPos * rowLength ) ) * pixelAttribs.bytesPerPixel; // skipPixels + skipRows + final int ibPos = ( currentTileXPos + ( currentTileYPos * rowLength ) ) * pixelAttribs.pfmt.comp.bytesPerPixel(); // skipPixels + skipRows final int ibLim = ibPos + readPixelSize; imageBuffer.clear(); if( imageBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { diff --git a/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java index d8410a102..fee2e5933 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java @@ -479,7 +479,7 @@ public class TileRenderer extends TileRendererBase { final int srcY = tileBorder; final int srcWidth = tileSizeNB.getWidth(); final int srcHeight = tileSizeNB.getHeight(); - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.pfmt.comp.bytesPerPixel(), srcWidth, srcHeight, 1, true); tileBuffer.clear(); if( tileBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { throw new IndexOutOfBoundsException("Required " + readPixelSize + " bytes of buffer, only had " + tileBuffer); @@ -503,11 +503,11 @@ public class TileRenderer extends TileRendererBase { psm.setPackRowLength(gl2es3, rowLength); /* read the tile into the final image */ - final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.pfmt.comp.bytesPerPixel(), srcWidth, srcHeight, 1, true); final int skipPixels = currentColumn * tileSizeNB.getWidth(); final int skipRows = currentRow * tileSizeNB.getHeight(); - final int ibPos = ( skipPixels + ( skipRows * rowLength ) ) * pixelAttribs.bytesPerPixel; + final int ibPos = ( skipPixels + ( skipRows * rowLength ) ) * pixelAttribs.pfmt.comp.bytesPerPixel(); final int ibLim = ibPos + readPixelSize; imageBuffer.clear(); if( imageBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java index 04bc0a15d..81cb34239 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java @@ -36,10 +36,14 @@ import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.nio.Buffer; import java.nio.IntBuffer; +import java.util.Iterator; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; +import javax.media.opengl.GLProfile; import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.opengl.util.GLPixelBuffer; /** @@ -50,7 +54,7 @@ import com.jogamp.opengl.util.GLPixelBuffer; * </p> * <p> * {@link AWTGLPixelBuffer} can be produced via {@link AWTGLPixelBufferProvider}'s - * {@link AWTGLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocate(..)}. + * {@link AWTGLPixelBufferProvider#allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocate(..)}. * </p> * <p> * See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)} for {@link #allowRowStride} details. @@ -61,30 +65,49 @@ import com.jogamp.opengl.util.GLPixelBuffer; * </p> */ public class AWTGLPixelBuffer extends GLPixelBuffer { - public static final GLPixelAttributes awtPixelAttributesIntRGBA4 = new GLPixelAttributes(4, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); - public static final GLPixelAttributes awtPixelAttributesIntRGB3 = new GLPixelAttributes(3, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); + /** + * Ignoring componentCount, since otherwise no AWT/GL matching types are found. + * <p> + * Due to using RGBA and BGRA, pack/unpack usage has makes no difference. + * </p> + */ + private static final GLPixelAttributes awtPixelAttributesIntBGRA = new GLPixelAttributes(GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); + private static final GLPixelAttributes awtPixelAttributesIntRGBA = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); /** The underlying {@link BufferedImage}. */ public final BufferedImage image; + private final PixelFormat.Composition hostPixelComp; + private final int awtFormat; + /** - * + * @param hostPixelComp the host {@link PixelFormat.Composition} * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}. + * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}. + * @param awtFormat the used AWT format, i.e. {@link AWTGLPixelBufferProvider#getAWTFormat(GLProfile, int)} * @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)}. * If <code>true</code>, user shall decide whether to use a {@link #getAlignedImage(int, int) width-aligned image}. */ - public AWTGLPixelBuffer(final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final BufferedImage image, - final Buffer buffer, final boolean allowRowStride) { - super(pixelAttributes, width, height, depth, pack, buffer, allowRowStride); + public AWTGLPixelBuffer(final PixelFormat.Composition hostPixelComp, + final GLPixelAttributes pixelAttributes, + final boolean pack, + final int awtFormat, final int width, final int height, final int depth, + final BufferedImage image, final Buffer buffer, final boolean allowRowStride) { + super(pixelAttributes, pack, width, height, depth, buffer, allowRowStride); this.image = image; + this.hostPixelComp = hostPixelComp; + this.awtFormat = awtFormat; } + public final PixelFormat.Composition getHostPixelComp() { return hostPixelComp; } + public final int getAWTFormat() { return awtFormat; } + @Override public void dispose() { image.flush(); @@ -147,12 +170,57 @@ public class AWTGLPixelBuffer extends GLPixelBuffer { public AWTGLPixelBufferProvider(final boolean allowRowStride) { this.allowRowStride = allowRowStride; } + @Override public boolean getAllowRowStride() { return allowRowStride; } @Override - public GLPixelAttributes getAttributes(final GL gl, final int componentCount) { - return 4 == componentCount ? awtPixelAttributesIntRGBA4 : awtPixelAttributesIntRGB3; + public GLPixelAttributes getAttributes(final GL gl, final int componentCount, final boolean pack) { + return gl.isGLES() ? awtPixelAttributesIntRGBA : awtPixelAttributesIntBGRA; + } + + public GLPixelAttributes getAttributes(final GLProfile glp, final int componentCount) { + return glp.isGLES() ? awtPixelAttributesIntRGBA : awtPixelAttributesIntBGRA; + } + + /** + * {@inheritDoc} + * <p> + * Returns a valid {@link PixelFormat.Composition} instance from {@link #getAWTPixelFormat(GLProfile, int)}. + * </p> + */ + @Override + public PixelFormat.Composition getHostPixelComp(final GLProfile glp, final int componentCount) { + return getAWTPixelFormat(glp, componentCount).comp; + } + + /** + * Returns one of + * <ul> + * <li>GL__, 4c -> 4c: {@link BufferedImage#TYPE_INT_ARGB} <-> {@link GL#GL_BGRA}</li> + * <li>GLES, 4c -> 4c: {@link BufferedImage#TYPE_INT_BGR} <-> {@link GL#GL_RGBA}</li> + * <li>GL__, 3c -> 4c: {@link BufferedImage#TYPE_INT_RGB} <-> {@link GL#GL_BGRA}</li> + * <li>GLES, 3c -> 4c: {@link BufferedImage#TYPE_INT_BGR} <-> {@link GL#GL_RGBA}</li> + * </ul> + * @param glp + * @param componentCount + * @return + */ + public int getAWTFormat(final GLProfile glp, final int componentCount) { + if( 4 == componentCount ) { + // FIXME: 4 component solution BufferedImage.TYPE_INT_ARGB: GLES format missing (i.e. GL_BGRA) + return glp.isGLES() ? BufferedImage.TYPE_INT_BGR : BufferedImage.TYPE_INT_ARGB; + } else { + return glp.isGLES() ? BufferedImage.TYPE_INT_BGR : BufferedImage.TYPE_INT_RGB; + } + } + + public PixelFormat getAWTPixelFormat(final GLProfile glp, final int componentCount) { + if( 4 == componentCount ) { + return glp.isGLES() ? PixelFormat.RGBx8888 : PixelFormat.BGRA8888; + } else { + return glp.isGLES() ? PixelFormat.RGBx8888 : PixelFormat.BGRx8888; + } } /** @@ -162,11 +230,17 @@ public class AWTGLPixelBuffer extends GLPixelBuffer { * </p> */ @Override - public AWTGLPixelBuffer allocate(final GL gl, final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { - final BufferedImage image = new BufferedImage(width, height, 4 == pixelAttributes.componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + public AWTGLPixelBuffer allocate(final GL gl, final PixelFormat.Composition hostPixComp, final GLPixelAttributes pixelAttributes, final boolean pack, + final int width, final int height, final int depth, final int minByteSize) { + if( null == hostPixComp ) { + throw new IllegalArgumentException("Null hostPixComp"); + } + final int awtFormat = getAWTFormat(gl.getGLProfile(), hostPixComp.componenCount()); + final BufferedImage image = new BufferedImage(width, height, awtFormat); 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); + return new AWTGLPixelBuffer(hostPixComp, pixelAttributes, pack, + awtFormat, width, height, depth, image, ibuffer, allowRowStride); } } @@ -174,15 +248,22 @@ public class AWTGLPixelBuffer extends GLPixelBuffer { * 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} + * {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated}. + * A new {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, 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 implements SingletonGLPixelBufferProvider { - private AWTGLPixelBuffer singleRGBA4 = null; - private AWTGLPixelBuffer singleRGB3 = null; + private final IntObjectHashMap bufferMap = new IntObjectHashMap(8); + + private static int getHashCode(final PixelFormat.Composition hostPixelComp, final GLPixelAttributes pixelAttributes, final boolean pack) { + // 31 * x == (x << 5) - x + int hash = hostPixelComp.hashCode(); + hash = ((hash << 5) - hash) + pixelAttributes.hashCode(); + // hash = ((hash << 5) - hash) + (pack ? 100 : 0); // no difference due to RGBA/BGRA only modes. + return hash; + } /** * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. @@ -198,52 +279,72 @@ public class AWTGLPixelBuffer extends GLPixelBuffer { * </p> */ @Override - public AWTGLPixelBuffer allocate(final GL gl, final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { - if( 4 == pixelAttributes.componentCount ) { - if( null == singleRGBA4 || singleRGBA4.requiresNewBuffer(gl, width, height, minByteSize) ) { - singleRGBA4 = allocateImpl(pixelAttributes, width, height, depth, pack, minByteSize); - } - return singleRGBA4; - } else { - if( null == singleRGB3 || singleRGB3.requiresNewBuffer(gl, width, height, minByteSize) ) { - singleRGB3 = allocateImpl(pixelAttributes, width, height, depth, pack, minByteSize); + public AWTGLPixelBuffer allocate(final GL gl, PixelFormat.Composition hostPixComp, final GLPixelAttributes pixelAttributes, + final boolean pack, final int width, final int height, final int depth, final int minByteSize) { + if( null == hostPixComp ) { + hostPixComp = pixelAttributes.pfmt.comp; + } + final int bufferKey = getHashCode(hostPixComp, pixelAttributes, pack); + AWTGLPixelBuffer r = (AWTGLPixelBuffer) bufferMap.get(bufferKey); + if( null == r || r.requiresNewBuffer(gl, width, height, minByteSize) ) { + if( null != r ) { + r.dispose(); } - return singleRGB3; + r = allocateImpl(hostPixComp, pixelAttributes, pack, + getAWTFormat(gl.getGLProfile(), hostPixComp.componenCount()), width, height, depth, minByteSize); + bufferMap.put(bufferKey, r); } + return r; } - private AWTGLPixelBuffer allocateImpl(final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { - final BufferedImage image = new BufferedImage(width, height, 4 == pixelAttributes.componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + private AWTGLPixelBuffer allocateImpl(final PixelFormat.Composition hostPixComp, + final GLPixelAttributes pixelAttributes, + final boolean pack, + final int awtFormat, final int width, final int height, final int depth, + final int minByteSize) { + final BufferedImage image = new BufferedImage(width, height, awtFormat); final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer ); - return new AWTGLPixelBuffer(pixelAttributes, width, height, depth, pack, image, ibuffer, getAllowRowStride()); + return new AWTGLPixelBuffer(hostPixComp, pixelAttributes, pack, + awtFormat, width, height, depth, image, ibuffer, getAllowRowStride()); } - /** Return the last {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated} {@link AWTGLPixelBuffer} w/ {@link GLPixelAttributes#componentCount}. */ + /** + * Return the last {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated} + * {@link AWTGLPixelBuffer}, if compatible w/ the given {@link PixelFormat.Composition} and {@link GLPixelAttributes}. + **/ @Override - public AWTGLPixelBuffer getSingleBuffer(final GLPixelAttributes pixelAttributes) { - return 4 == pixelAttributes.componentCount ? singleRGBA4 : singleRGB3; + public AWTGLPixelBuffer getSingleBuffer(final PixelFormat.Composition hostPixelComp, final GLPixelAttributes pixelAttributes, final boolean pack) { + return (AWTGLPixelBuffer) bufferMap.get(getHashCode(hostPixelComp, pixelAttributes, pack)); } /** - * Initializes the single {@link AWTGLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * Initializes the single {@link AWTGLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated}. * @return the newly initialized single {@link AWTGLPixelBuffer}, or null if already allocated. */ @Override - public AWTGLPixelBuffer initSingleton(final int componentCount, final int width, final int height, final int depth, final boolean pack) { - if( 4 == componentCount ) { - if( null != singleRGBA4 ) { - return null; - } - singleRGBA4 = allocateImpl(AWTGLPixelBuffer.awtPixelAttributesIntRGBA4, width, height, depth, pack, 0); - return singleRGBA4; - } else { - if( null != singleRGB3 ) { - return null; - } - singleRGB3 = allocateImpl(AWTGLPixelBuffer.awtPixelAttributesIntRGB3, width, height, depth, pack, 0); - return singleRGB3; + public AWTGLPixelBuffer initSingleton(final GLProfile glp, final int componentCount, + final boolean pack, final int width, final int height, final int depth) { + final GLPixelAttributes pixelAttributes = getAttributes(glp, componentCount); + final PixelFormat awtPixelFormat = getAWTPixelFormat(glp, componentCount); + final int awtFormat = getAWTFormat(glp, componentCount); + final int bufferKey = getHashCode(awtPixelFormat.comp, pixelAttributes, pack); + AWTGLPixelBuffer r = (AWTGLPixelBuffer) bufferMap.get(bufferKey); + if( null != r ) { + return null; + } + r = allocateImpl(awtPixelFormat.comp, pixelAttributes, pack, awtFormat, width, height, depth, 0); + bufferMap.put(bufferKey, r); + return r; + } + + @Override + public void dispose() { + for(final Iterator<IntObjectHashMap.Entry> i=bufferMap.iterator(); i.hasNext(); ) { + final AWTGLPixelBuffer b = (AWTGLPixelBuffer)i.next().value; + b.dispose(); } + bufferMap.clear(); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java index 6011afe7b..14fb90662 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java @@ -1125,12 +1125,12 @@ public class TextureIO { final String fileSuffix) throws IOException { if (PNG.equals(fileSuffix)) { final PNGPixelRect image = PNGPixelRect.read(stream, null, true /* directBuffer */, 0 /* destMinStrideInBytes */, true /* destIsGLOriented */); - final GLPixelAttributes glpa = GLPixelAttributes.convert(image.getPixelformat(), glp); + final GLPixelAttributes glpa = new GLPixelAttributes(glp, image.getPixelformat(), false /* pack */); if ( 0 == pixelFormat ) { pixelFormat = glpa.format; } // else FIXME: Actually not supported w/ preset pixelFormat! if ( 0 == internalFormat ) { - final boolean hasAlpha = 4 == glpa.bytesPerPixel; + final boolean hasAlpha = 4 == glpa.pfmt.comp.bytesPerPixel(); if(glp.isGL2ES3()) { internalFormat = hasAlpha ? GL.GL_RGBA8 : GL.GL_RGB8; } else { @@ -1351,8 +1351,8 @@ public class TextureIO { final GLPixelAttributes pixelAttribs = data.getPixelAttributes(); final int pixelFormat = pixelAttribs.format; final int pixelType = pixelAttribs.type; - final int bytesPerPixel = pixelAttribs.bytesPerPixel; - final PixelFormat pixFmt = pixelAttribs.getPixelFormat(); + final int bytesPerPixel = pixelAttribs.pfmt.comp.bytesPerPixel(); + final PixelFormat pixFmt = pixelAttribs.pfmt; if ( ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel) && ( pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { Buffer buf0 = data.getBuffer(); diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 8d23d79ff..edd5266b9 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -63,6 +63,7 @@ import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.ScalableSurface; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GL2ES3; @@ -265,12 +266,14 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private final int[] hasPixelScale = new int[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; private final int[] reqPixelScale = new int[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; - // For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width + /** For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width in pixel units (scaled) */ private int reshapeWidth; + /** For handling reshape events lazily: reshapeHeight -> panelHeight -> backend.height in pixel units (scaled) */ private int reshapeHeight; - // Width of the actual GLJPanel: reshapeWidth -> panelWidth -> backend.width + /** Scaled pixel width of the actual GLJPanel: reshapeWidth -> panelWidth -> backend.width */ private int panelWidth = 0; + /** Scaled pixel height of the actual GLJPanel: reshapeHeight -> panelHeight -> backend.height */ private int panelHeight = 0; // These are always set to (0, 0) except when the Java2D / OpenGL @@ -1882,27 +1885,36 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing alignment = 4; } - final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); + final PixelFormat awtPixelFormat = pixelBufferProvider.getAWTPixelFormat(gl.getGLProfile(), componentCount); + final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount, true); if( useSingletonBuffer ) { // attempt to fetch the latest AWTGLPixelBuffer - pixelBuffer = (AWTGLPixelBuffer) ((SingletonGLPixelBufferProvider)pixelBufferProvider).getSingleBuffer(pixelAttribs); + pixelBuffer = (AWTGLPixelBuffer) ((SingletonGLPixelBufferProvider)pixelBufferProvider).getSingleBuffer(awtPixelFormat.comp, pixelAttribs, true); } if( null != pixelBuffer && pixelBuffer.requiresNewBuffer(gl, panelWidth, panelHeight, 0) ) { pixelBuffer.dispose(); pixelBuffer = null; alignedImage = null; } + final boolean DEBUG_INIT; if ( null == pixelBuffer ) { if (0 >= panelWidth || 0 >= panelHeight ) { return; } - pixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, panelWidth, panelHeight, 1, true, 0); + pixelBuffer = pixelBufferProvider.allocate(gl, awtPixelFormat.comp, pixelAttribs, true, panelWidth, panelHeight, 1, 0); if(DEBUG) { System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBufferProvider isSingletonBufferProvider "+useSingletonBuffer+", 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName()); System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment); - System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster)); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster)+", isGL2ES3 "+gl.isGL2ES3()); System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelAttribs "+pixelAttribs); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" awtPixelFormat "+awtPixelFormat); + DEBUG_INIT = true; + } else { + DEBUG_INIT = false; } + } else { + DEBUG_INIT = false; } if( offscreenDrawable.getSurfaceWidth() != panelWidth || offscreenDrawable.getSurfaceHeight() != panelHeight ) { throw new InternalError("OffscreenDrawable panelSize mismatch (reshape missed): panelSize "+panelWidth+"x"+panelHeight+" != drawable "+offscreenDrawable.getSurfaceWidth()+"x"+offscreenDrawable.getSurfaceHeight()+", on thread "+getThreadName()); @@ -1937,6 +1949,13 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing final GL2ES3 gl2es3 = gl.getGL2ES3(); psm.setPackRowLength(gl2es3, panelWidth); gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); + if( DEBUG_INIT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: fboDrawable "+offscreenDrawable); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: isGL2ES3, readBuffer 0x"+Integer.toHexString(gl2es3.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: def-readBuffer 0x"+Integer.toHexString(gl2es3.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: def-readFBO 0x"+Integer.toHexString(gl2es3.getDefaultReadFramebuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: bound-readFBO 0x"+Integer.toHexString(gl2es3.getBoundFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER))); + } } if(null != glslTextureRaster) { // implies flippedVertical @@ -1965,9 +1984,26 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // fboFlipped runs w/o DEPTH! glslTextureRaster.display(gl.getGL2ES2()); + if( DEBUG_INIT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: fboDrawable "+fboDrawable); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: read from fbo-rb "+fboFlipped.getReadFramebuffer()+", fbo "+fboFlipped); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: isGL2ES3, readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: def-readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: def-readFBO 0x"+Integer.toHexString(gl.getDefaultReadFramebuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: bound-readFBO 0x"+Integer.toHexString(gl.getBoundFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER))); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: "+GLJPanel.this.getName()+" pixelAttribs "+pixelAttribs); + } gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts); fboFlipped.unbind(gl); + if( DEBUG_INIT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: fboDrawable "+fboDrawable); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: read from fbo-rb "+fboFlipped.getReadFramebuffer()+", fbo "+fboFlipped); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: isGL2ES3, readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: def-readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: def-readFBO 0x"+Integer.toHexString(gl.getDefaultReadFramebuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: bound-readFBO 0x"+Integer.toHexString(gl.getBoundFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER))); + } if( viewportChange ) { gl.glViewport(usrViewport[0], usrViewport[1], usrViewport[2], usrViewport[3]); } diff --git a/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java b/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java index a5f5b4702..b6fa68600 100644 --- a/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java +++ b/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java @@ -46,6 +46,7 @@ import java.util.Map.Entry; import javax.imageio.ImageIO; import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilitiesImmutable; @@ -288,8 +289,9 @@ public class AWTTilePainter { final int tWidth = renderer.getParam(TileRenderer.TR_TILE_WIDTH); final int tHeight = renderer.getParam(TileRenderer.TR_TILE_HEIGHT); final AWTGLPixelBufferProvider printBufferProvider = new AWTGLPixelBufferProvider( true /* allowRowStride */ ); - final GLPixelAttributes pixelAttribs = printBufferProvider.getAttributes(gl, componentCount); - tBuffer = printBufferProvider.allocate(gl, pixelAttribs, tWidth, tHeight, 1, true, 0); + final PixelFormat.Composition hostPixelComp = printBufferProvider.getHostPixelComp(gl.getGLProfile(), componentCount); + final GLPixelAttributes pixelAttribs = printBufferProvider.getAttributes(gl, componentCount, true); + tBuffer = printBufferProvider.allocate(gl, hostPixelComp, pixelAttribs, true, tWidth, tHeight, 1, 0); renderer.setTileBuffer(tBuffer); if( flipVertical ) { vFlipImage = new BufferedImage(tBuffer.width, tBuffer.height, tBuffer.image.getType()); |