diff options
Diffstat (limited to 'src/jogl/classes/jogamp/opengl')
64 files changed, 2469 insertions, 1941 deletions
diff --git a/src/jogl/classes/jogamp/opengl/Debug.java b/src/jogl/classes/jogamp/opengl/Debug.java index 4287c1960..b88a09b71 100644 --- a/src/jogl/classes/jogamp/opengl/Debug.java +++ b/src/jogl/classes/jogamp/opengl/Debug.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 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 @@ -39,6 +40,9 @@ package jogamp.opengl; +import java.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -49,7 +53,12 @@ public class Debug extends PropertyAccess { private static final boolean debugAll; static { - PropertyAccess.addTrustedPrefix("jogl.", Debug.class); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + PropertyAccess.addTrustedPrefix("jogl."); + return null; + } } ); + verbose = isPropertyDefined("jogl.verbose", true); debugAll = isPropertyDefined("jogl.debug", true); if (verbose) { @@ -59,28 +68,19 @@ public class Debug extends PropertyAccess { System.err.println("JOGL implementation vendor " + p.getImplementationVendor()); } } - - public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) { - return PropertyAccess.isPropertyDefined(property, jnlpAlias, null); - } - - public static String getProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getProperty(property, jnlpAlias, null); - } - - public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) { - return PropertyAccess.getBooleanProperty(property, jnlpAlias, null); - } - public static boolean verbose() { + /** Ensures static init block has been issues, i.e. if calling through to {@link PropertyAccess#isPropertyDefined(String, boolean)}. */ + public static final void initSingleton() {} + + public static final boolean verbose() { return verbose; } - public static boolean debugAll() { + public static final boolean debugAll() { return debugAll; } - public static boolean debug(String subcomponent) { + public static final boolean debug(String subcomponent) { return debugAll() || isPropertyDefined("jogl.debug." + subcomponent, true); } } diff --git a/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLibraryBundleInfo.java index f77f1135b..ef9477a31 100644 --- a/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLibraryBundleInfo.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.ArrayList; public abstract class DesktopGLDynamicLibraryBundleInfo extends GLDynamicLibraryBundleInfo { - private static List<String> glueLibNames; + private static final List<String> glueLibNames; static { glueLibNames = new ArrayList<String>(); @@ -49,7 +49,7 @@ public abstract class DesktopGLDynamicLibraryBundleInfo extends GLDynamicLibrary } @Override - public boolean useToolGetProcAdressFirst(String funcName) { + public final boolean useToolGetProcAdressFirst(String funcName) { return true; } diff --git a/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLookupHelper.java b/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLookupHelper.java index ff49303ca..8eb3468ed 100644 --- a/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLookupHelper.java +++ b/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLookupHelper.java @@ -37,9 +37,9 @@ public class DesktopGLDynamicLookupHelper extends GLDynamicLookupHelper { super(info); } - public DesktopGLDynamicLibraryBundleInfo getDesktopGLBundleInfo() { return (DesktopGLDynamicLibraryBundleInfo) getBundleInfo(); } + public final DesktopGLDynamicLibraryBundleInfo getDesktopGLBundleInfo() { return (DesktopGLDynamicLibraryBundleInfo) getBundleInfo(); } - public synchronized boolean loadGLULibrary() { + public final synchronized boolean loadGLULibrary() { /** hacky code .. where all platform GLU libs are tried ..*/ if(null==gluLib) { List<String> gluLibNames = new ArrayList<String>(); diff --git a/src/jogl/classes/jogamp/opengl/ExtensionAvailabilityCache.java b/src/jogl/classes/jogamp/opengl/ExtensionAvailabilityCache.java index 7c7ea1508..94acf93b0 100644 --- a/src/jogl/classes/jogamp/opengl/ExtensionAvailabilityCache.java +++ b/src/jogl/classes/jogamp/opengl/ExtensionAvailabilityCache.java @@ -219,17 +219,17 @@ final class ExtensionAvailabilityCache { System.err.println(getThreadName() + ":ExtensionAvailabilityCache: ALL EXTENSIONS: "+availableExtensionCache.size()); } - if(!context.isGLES()) { - final VersionNumber version = context.getGLVersionNumber(); - int major[] = new int[] { version.getMajor() }; - int minor[] = new int[] { version.getMinor() }; - while (GLContext.isValidGLVersion(major[0], minor[0])) { - availableExtensionCache.add("GL_VERSION_" + major[0] + "_" + minor[0]); - if (DEBUG) { - System.err.println(getThreadName() + ":ExtensionAvailabilityCache: Added GL_VERSION_" + major[0] + "_" + minor[0] + " to known extensions"); - } - if(!GLContext.decrementGLVersion(major, minor)) break; + final int ctxOptions = context.getCtxOptions(); + final VersionNumber version = context.getGLVersionNumber(); + int major[] = new int[] { version.getMajor() }; + int minor[] = new int[] { version.getMinor() }; + while (GLContext.isValidGLVersion(ctxOptions, major[0], minor[0])) { + final String GL_XX_VERSION = ( context.isGLES() ? "GL_ES_VERSION_" : "GL_VERSION_" ) + major[0] + "_" + minor[0]; + availableExtensionCache.add(GL_XX_VERSION); + if (DEBUG) { + System.err.println(getThreadName() + ":ExtensionAvailabilityCache: Added "+GL_XX_VERSION+" to known extensions"); } + if(!GLContext.decrementGLVersion(ctxOptions, major, minor)) break; } // put a dummy var in here so that the cache is no longer empty even if diff --git a/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java b/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java index 78ab7cc93..17646cc7b 100644 --- a/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java +++ b/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java @@ -87,6 +87,13 @@ import com.jogamp.common.util.IntLongHashMap; */ public class GLBufferSizeTracker { + protected static final boolean DEBUG; + + static { + Debug.initSingleton(); + DEBUG = Debug.isPropertyDefined("jogl.debug.GLBufferSizeTracker", true); + } + // Map from buffer names to sizes. // Note: should probably have some way of shrinking this map, but // can't just make it a WeakHashMap because nobody holds on to the @@ -95,8 +102,7 @@ public class GLBufferSizeTracker { // pattern of buffer objects indicates that the fact that this map // never shrinks is probably not that bad. private IntLongHashMap bufferSizeMap; - protected static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.GLBufferSizeTracker", true); - + public GLBufferSizeTracker() { bufferSizeMap = new IntLongHashMap(); bufferSizeMap.setKeyNotFoundValue(0xFFFFFFFFFFFFFFFFL); diff --git a/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java b/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java index 7f5316fbd..890c82c90 100644 --- a/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java +++ b/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java @@ -76,7 +76,13 @@ import com.jogamp.common.util.IntIntHashMap; */ public class GLBufferStateTracker { - protected static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.GLBufferStateTracker", true); + protected static final boolean DEBUG; + + static { + Debug.initSingleton(); + DEBUG = Debug.isPropertyDefined("jogl.debug.GLBufferStateTracker", true); + } + // Maps binding targets to buffer objects. A null value indicates // that the binding is unknown. A zero value indicates that it is // known that no buffer is bound to the target, according to the diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index cab629c3a..996a47590 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -40,8 +40,11 @@ package jogamp.opengl; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; @@ -65,6 +68,7 @@ import javax.media.nativewindow.NativeWindowFactory; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GL3ES3; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDebugListener; @@ -259,6 +263,17 @@ public abstract class GLContextImpl extends GLContext { } @Override + public final GL getRootGL() { + GL _gl = gl; + GL _parent = _gl.getDownstreamGL(); + while ( null != _parent ) { + _gl = _parent; + _parent = _gl.getDownstreamGL(); + } + return _gl; + } + + @Override public final GL getGL() { return gl; } @@ -275,6 +290,11 @@ public abstract class GLContextImpl extends GLContext { return gl; } + @Override + public final int getDefaultVAO() { + return defaultVAO; + } + /** * Call this method to notify the OpenGL context * that the drawable has changed (size or position). @@ -394,9 +414,10 @@ public abstract class GLContextImpl extends GLContext { associateDrawableException = t; } if ( 0 != defaultVAO ) { - int[] tmp = new int[] { defaultVAO }; - gl.getGL2GL3().glBindVertexArray(0); - gl.getGL2GL3().glDeleteVertexArrays(1, tmp, 0); + final int[] tmp = new int[] { defaultVAO }; + final GL3ES3 gl3es3 = gl.getRootGL().getGL3ES3(); + gl3es3.glBindVertexArray(0); + gl3es3.glDeleteVertexArrays(1, tmp, 0); defaultVAO = 0; } glDebugHandler.enable(false); @@ -631,16 +652,12 @@ public abstract class GLContextImpl extends GLContext { final boolean created; try { created = createImpl(shareWith); // may throws exception if fails! - if( created && isGL3core() ) { - // Due to GL 3.1 core spec: E.1. DEPRECATED AND REMOVED FEATURES (p 296), - // GL 3.2 core spec: E.2. DEPRECATED AND REMOVED FEATURES (p 331) - // there is no more default VAO buffer 0 bound, hence generating and binding one - // to avoid INVALID_OPERATION at VertexAttribPointer. - // More clear is GL 4.3 core spec: 10.4 (p 307). + if( created && hasNoDefaultVAO() ) { final int[] tmp = new int[1]; - gl.getGL2GL3().glGenVertexArrays(1, tmp, 0); + final GL3ES3 gl3es3 = gl.getRootGL().getGL3ES3(); + gl3es3.glGenVertexArrays(1, tmp, 0); defaultVAO = tmp[0]; - gl.getGL2GL3().glBindVertexArray(defaultVAO); + gl3es3.glBindVertexArray(defaultVAO); } } finally { if (null != shareWith) { @@ -669,7 +686,7 @@ public abstract class GLContextImpl extends GLContext { if( 0 == ( ctxOptions & GLContext.CTX_PROFILE_ES) ) { // not ES profile final int reqMajor; final int reqProfile; - if( ctxVersion.compareTo(Version30) <= 0 ) { + if( ctxVersion.compareTo(Version300) <= 0 ) { reqMajor = 2; } else { reqMajor = ctxVersion.getMajor(); @@ -834,6 +851,7 @@ public abstract class GLContextImpl extends GLContext { boolean hasGL2 = false; boolean hasGL4 = false; boolean hasGL3 = false; + boolean hasES3 = false; // Even w/ PROFILE_ALIASING, try to use true core GL profiles // ensuring proper user behavior across platforms due to different feature sets! @@ -902,6 +920,13 @@ public abstract class GLContextImpl extends GLContext { resetStates(); // clean this context states, since creation was temporary } } + if(!hasES3) { + hasES3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_ES); // ES3 + success |= hasES3; + if(hasES3) { + resetStates(); // clean this context states, since creation was temporary + } + } if(success) { // only claim GL versions set [and hence detected] if ARB context creation was successful GLContext.setAvailableGLVersionsSet(device); @@ -923,12 +948,7 @@ public abstract class GLContextImpl extends GLContext { **/ private final boolean createContextARBMapVersionsAvailable(int reqMajor, int reqProfile) { long _context; - int ctp = CTX_IS_ARB_CREATED; - if(CTX_PROFILE_COMPAT == reqProfile) { - ctp |= CTX_PROFILE_COMPAT ; - } else { - ctp |= CTX_PROFILE_CORE ; - } + int ctp = CTX_IS_ARB_CREATED | reqProfile; // To ensure GL profile compatibility within the JOGL application // we always try to map against the highest GL version, @@ -938,10 +958,10 @@ public abstract class GLContextImpl extends GLContext { int major[] = new int[1]; int minor[] = new int[1]; if( 4 == reqMajor ) { - majorMax=4; minorMax=GLContext.getMaxMinor(majorMax); + majorMax=4; minorMax=GLContext.getMaxMinor(ctp, majorMax); majorMin=4; minorMin=0; } else if( 3 == reqMajor ) { - majorMax=3; minorMax=GLContext.getMaxMinor(majorMax); + majorMax=3; minorMax=GLContext.getMaxMinor(ctp, majorMax); majorMin=3; minorMin=1; } else /* if( glp.isGL2() ) */ { // our minimum desktop OpenGL runtime requirements are 1.1, @@ -1001,7 +1021,7 @@ public abstract class GLContextImpl extends GLContext { minor[0]=minorMax; long _context=0; - while ( GLContext.isValidGLVersion(major[0], minor[0]) && + while ( GLContext.isValidGLVersion(ctxOptionFlags, major[0], minor[0]) && ( major[0]>majorMin || major[0]==majorMin && minor[0] >=minorMin ) ) { if (DEBUG) { System.err.println(getThreadName() + ": createContextARBVersions: share "+share+", direct "+direct+", version "+major[0]+"."+minor[0]); @@ -1017,7 +1037,7 @@ public abstract class GLContextImpl extends GLContext { } } - if(!GLContext.decrementGLVersion(major, minor)) { + if(!GLContext.decrementGLVersion(ctxOptionFlags, major, minor)) { break; } } @@ -1038,11 +1058,11 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp)); } - if (!GLContext.isValidGLVersion(major, minor)) { + if (!GLContext.isValidGLVersion(ctp, major, minor)) { throw new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp)); } ctxVersion = new VersionNumber(major, minor, 0); - ctxVersionString = getGLVersion(major, minor, ctxOptions, glVersion); + ctxVersionString = getGLVersion(major, minor, ctp, glVersion); ctxVendorVersion = glVendorVersion; ctxOptions = ctp; if(useGL) { @@ -1085,6 +1105,27 @@ public abstract class GLContextImpl extends GLContext { */ return gl; } + + /** + * Finalizes GL instance initialization after this context has been initialized. + * <p> + * Method calls 'void finalizeInit()' of instance 'gl' as retrieved by reflection, if exist. + * </p> + */ + private void finalizeInit(GL gl) { + Method finalizeInit = null; + try { + finalizeInit = ReflectionUtil.getMethod(gl.getClass(), "finalizeInit", new Class<?>[]{ }); + } catch ( Throwable t ) { + if(DEBUG) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + if( null != finalizeInit ) { + ReflectionUtil.callMethod(gl, finalizeInit, new Object[]{ }); + } + } public final ProcAddressTable getGLProcAddressTable() { return glProcAddressTable; @@ -1095,24 +1136,24 @@ public abstract class GLContextImpl extends GLContext { * ie for GLXExt, EGLExt, .. */ public abstract ProcAddressTable getPlatformExtProcAddressTable(); - + /** - * Pbuffer support; given that this is a GLContext associated with a - * pbuffer, binds this pbuffer to its texture target. - * @throws GLException if not implemented (default) - * @deprecated use FBO/GLOffscreenAutoDrawable instead of pbuffer + * Part of <code>GL_NV_vertex_array_range</code>. + * <p> + * Provides platform-independent access to the <code>wglAllocateMemoryNV</code> / + * <code>glXAllocateMemoryNV</code>. + * </p> */ - public void bindPbufferToTexture() { throw new GLException("not implemented"); } + public abstract ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority); /** - * Pbuffer support; given that this is a GLContext associated with a - * pbuffer, releases this pbuffer from its texture target. - * @throws GLException if not implemented (default) - * @deprecated use FBO/GLOffscreenAutoDrawable instead of pbuffer - */ - public void releasePbufferFromTexture() { throw new GLException("not implemented"); } - - public abstract ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3); + * Part of <code>GL_NV_vertex_array_range</code>. + * <p> + * Provides platform-independent access to the <code>wglFreeMemoryNV</code> / + * <code>glXFreeMemoryNV</code>. + * </p> + */ + public abstract void glFreeMemoryNV(ByteBuffer pointer); /** Maps the given "platform-independent" function name to a real function name. Currently this is only used to map "glAllocateMemoryNV" and @@ -1144,10 +1185,15 @@ public abstract class GLContextImpl extends GLContext { /** Helper routine which resets a ProcAddressTable generated by the GLEmitter by looking up anew all of its function pointers. */ - protected final void resetProcAddressTable(ProcAddressTable table) { - table.reset(getDrawableImpl().getGLDynamicLookupHelper() ); + protected final void resetProcAddressTable(final ProcAddressTable table) { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + table.reset(getDrawableImpl().getGLDynamicLookupHelper() ); + return null; + } + } ); } - + private final boolean initGLRendererAndGLVersionStrings() { final GLDynamicLookupHelper glDynLookupHelper = getDrawableImpl().getGLDynamicLookupHelper(); final long _glGetString = glDynLookupHelper.dynamicLookupFunction("glGetString"); @@ -1217,7 +1263,7 @@ public abstract class GLContextImpl extends GLContext { int[] major = new int[] { version.getMajor() }; int[] minor = new int[] { version.getMinor() }; limitNonARBContextVersion(major, minor, ctp); - if ( GLContext.isValidGLVersion(major[0], minor[0]) ) { + if ( GLContext.isValidGLVersion(ctp, major[0], minor[0]) ) { return new VersionNumber(major[0], minor[0], 0); } } @@ -1253,6 +1299,11 @@ public abstract class GLContextImpl extends GLContext { } } + protected final int getCtxOptions() { + return ctxOptions; + } + + /** * Sets the OpenGL implementation class and * the cache of which GL functions are available for calling through this @@ -1284,7 +1335,7 @@ public abstract class GLContextImpl extends GLContext { return true; // already done and not forced } - if ( 0 < major && !GLContext.isValidGLVersion(major, minor) ) { + if ( 0 < major && !GLContext.isValidGLVersion(ctxProfileBits, major, minor) ) { throw new GLException("Invalid GL Version Request "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); } @@ -1345,7 +1396,7 @@ public abstract class GLContextImpl extends GLContext { } // Only validate if a valid int version was fetched, otherwise cont. w/ version-string method -> 3.0 > Version || Version > MAX! - if ( GLContext.isValidGLVersion(glIntMajor[0], glIntMinor[0]) ) { + if ( GLContext.isValidGLVersion(ctxProfileBits, glIntMajor[0], glIntMinor[0]) ) { if( glIntMajor[0]<major || ( glIntMajor[0]==major && glIntMinor[0]<minor ) || 0 == major ) { if( strictMatch && 2 < major ) { // relaxed match for versions major < 3 requests, last resort! if(DEBUG) { @@ -1400,8 +1451,8 @@ public abstract class GLContextImpl extends GLContext { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: post version verification "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+", strictMatch "+strictMatch+", versionValidated "+versionValidated+", versionGL3IntFailed "+versionGL3IntFailed); } - if( 2 > major ) { // there is no ES2-compat for a profile w/ major < 2 - ctxProfileBits &= ~GLContext.CTX_IMPL_ES2_COMPAT; + if( 2 > major ) { // there is no ES2/3-compat for a profile w/ major < 2 + ctxProfileBits &= ~ ( GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_ES3_COMPAT ) ; } final VersionNumberString vendorVersion = GLVersionNumber.createVendorVersion(glVersion); @@ -1477,13 +1528,31 @@ public abstract class GLContextImpl extends GLContext { } } - if( ( 0 != ( CTX_PROFILE_ES & ctxProfileBits ) && major >= 2 ) || isExtensionAvailable(GLExtensions.ARB_ES2_compatibility) ) { + if( 0 != ( CTX_PROFILE_ES & ctxProfileBits ) ) { + if( major >= 3 ) { + ctxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ; + ctxProfileBits |= CTX_IMPL_FBO; + } else if( major >= 2 ) { + ctxProfileBits |= CTX_IMPL_ES2_COMPAT; + ctxProfileBits |= CTX_IMPL_FBO; + } + } else if( ( major > 4 || major == 4 && minor >= 3 ) || + ( ( major > 3 || major == 3 && minor >= 1 ) && isExtensionAvailable( GLExtensions.ARB_ES3_compatibility ) ) ) { + // See GLContext.isGLES3CompatibleAvailable(..)/isGLES3Compatible() + // Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ] + ctxProfileBits |= CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ; + ctxProfileBits |= CTX_IMPL_FBO; + } else if( isExtensionAvailable( GLExtensions.ARB_ES2_compatibility ) ) { ctxProfileBits |= CTX_IMPL_ES2_COMPAT; ctxProfileBits |= CTX_IMPL_FBO; } else if( hasFBOImpl(major, ctxProfileBits, extensionAvailability) ) { ctxProfileBits |= CTX_IMPL_FBO; } + if( ( 0 != ( CTX_PROFILE_ES & ctxProfileBits ) && major == 1 ) || isExtensionAvailable(GLExtensions.OES_single_precision) ) { + ctxProfileBits |= CTX_IMPL_FP32_COMPAT_API; + } + if(FORCE_NO_FBO_SUPPORT) { ctxProfileBits &= ~CTX_IMPL_FBO ; } @@ -1493,6 +1562,8 @@ public abstract class GLContextImpl extends GLContext { // setContextVersion(major, minor, ctxProfileBits, vendorVersion, true); + finalizeInit(gl); + setDefaultSwapInterval(); final int glErrX = gl.glGetError(); // clear GL error, maybe caused by above operations @@ -1508,6 +1579,8 @@ public abstract class GLContextImpl extends GLContext { int i = 0; final String MesaSP = "Mesa "; + // final String MesaRendererAMDsp = " AMD "; + // final String MesaRendererIntelsp = "Intel(R)"; final boolean hwAccel = 0 == ( ctp & GLContext.CTX_IMPL_ACCEL_SOFT ); final boolean compatCtx = 0 != ( ctp & GLContext.CTX_PROFILE_COMPAT ); final boolean isDriverMesa = glRenderer.contains(MesaSP) || glRenderer.contains("Gallium "); @@ -1596,7 +1669,7 @@ public abstract class GLContextImpl extends GLContext { // final int quirk = GLRendererQuirks.DontCloseX11Display; if( glRenderer.contains(MesaSP) ) { - if ( glRenderer.contains("X11") && vendorVersion.compareTo(Version80) < 0 ) { + if ( glRenderer.contains("X11") && vendorVersion.compareTo(Version800) < 0 ) { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer + ", Version=[vendor " + vendorVersion + ", GL " + glVersion+"]"); } @@ -1632,7 +1705,7 @@ public abstract class GLContextImpl extends GLContext { } quirks[i++] = quirk; } - if( hwAccel /* glRenderer.contains("Intel(R)") || glRenderer.contains("AMD ") */ ) + if( hwAccel /* glRenderer.contains( MesaRendererIntelsp ) || glRenderer.contains( MesaRendererAMDsp ) */ ) { final int quirk = GLRendererQuirks.NoDoubleBufferedPBuffer; if(DEBUG) { @@ -1640,14 +1713,13 @@ public abstract class GLContextImpl extends GLContext { } quirks[i++] = quirk; } - if( glRenderer.contains("Intel(R)") && compatCtx && ( major > 3 || major == 3 && minor >= 1 ) ) - { - // FIXME: Apply vendor version constraints! - final int quirk = GLRendererQuirks.GLNonCompliant; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); - } - quirks[i++] = quirk; + if (compatCtx && (major > 3 || (major == 3 && minor >= 1))) { + // FIXME: Apply vendor version constraints! + final int quirk = GLRendererQuirks.GLNonCompliant; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); + } + quirks[i++] = quirk; } if( Platform.getOSType() == Platform.OSType.WINDOWS && glRenderer.contains("SVGA3D") ) { @@ -1748,7 +1820,7 @@ public abstract class GLContextImpl extends GLContext { // Check GL 1st (cached) if(null!=glProcAddressTable) { // null if this context wasn't not created try { - if(0!=glProcAddressTable.getAddressFor(glFunctionName)) { + if( glProcAddressTable.isFunctionAvailable( glFunctionName ) ) { return true; } } catch (Exception e) {} @@ -1758,31 +1830,28 @@ public abstract class GLContextImpl extends GLContext { final ProcAddressTable pTable = getPlatformExtProcAddressTable(); if(null!=pTable) { try { - if(0!=pTable.getAddressFor(glFunctionName)) { + if( pTable.isFunctionAvailable( glFunctionName ) ) { return true; } } catch (Exception e) {} } // dynamic function lookup at last incl name aliasing (not cached) - DynamicLookupHelper dynLookup = getDrawableImpl().getGLDynamicLookupHelper(); - String tmpBase = GLNameResolver.normalizeVEN(GLNameResolver.normalizeARB(glFunctionName, true), true); - long addr = 0; + final DynamicLookupHelper dynLookup = getDrawableImpl().getGLDynamicLookupHelper(); + final String tmpBase = GLNameResolver.normalizeVEN(GLNameResolver.normalizeARB(glFunctionName, true), true); + boolean res = false; int variants = GLNameResolver.getFuncNamePermutationNumber(tmpBase); - for(int i = 0; 0==addr && i < variants; i++) { - String tmp = GLNameResolver.getFuncNamePermutation(tmpBase, i); + for(int i = 0; !res && i < variants; i++) { + final String tmp = GLNameResolver.getFuncNamePermutation(tmpBase, i); try { - addr = dynLookup.dynamicLookupFunction(tmp); + res = dynLookup.isFunctionAvailable(tmp); } catch (Exception e) { } } - if(0!=addr) { - return true; - } - return false; + return res; } @Override - public boolean isExtensionAvailable(String glExtensionName) { + public final boolean isExtensionAvailable(String glExtensionName) { if(null!=extensionAvailability) { return extensionAvailability.isExtensionAvailable(mapToRealGLExtensionName(glExtensionName)); } @@ -1824,7 +1893,7 @@ public abstract class GLContextImpl extends GLContext { protected static String getContextFQN(AbstractGraphicsDevice device, int major, int minor, int ctxProfileBits) { // remove non-key values - ctxProfileBits &= ~( GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ) ; + ctxProfileBits &= CTX_IMPL_CACHE_MASK; return device.getUniqueID() + "-" + toHexString(composeBits(major, minor, ctxProfileBits)); } @@ -1900,10 +1969,6 @@ public abstract class GLContextImpl extends GLContext { return glStateTracker; } - public final boolean isDefaultVAO(int vao) { - return defaultVAO == vao; - } - //--------------------------------------------------------------------------- // Helpers for context optimization where the last context is left // current on the OpenGL worker thread @@ -2009,7 +2074,7 @@ public abstract class GLContextImpl extends GLContext { public final int getContextCreationFlags() { return additionalCtxCreationFlags; } - + @Override public final void setContextCreationFlags(int flags) { if(!isCreated()) { @@ -2052,7 +2117,7 @@ public abstract class GLContextImpl extends GLContext { @Override public final void glDebugMessageControl(int source, int type, int severity, int count, IntBuffer ids, boolean enabled) { if(glDebugHandler.isExtensionARB()) { - gl.getGL2GL3().glDebugMessageControlARB(source, type, severity, count, ids, enabled); + gl.getGL2GL3().glDebugMessageControl(source, type, severity, count, ids, enabled); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageEnableAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, count, ids, enabled); } @@ -2061,7 +2126,7 @@ public abstract class GLContextImpl extends GLContext { @Override public final void glDebugMessageControl(int source, int type, int severity, int count, int[] ids, int ids_offset, boolean enabled) { if(glDebugHandler.isExtensionARB()) { - gl.getGL2GL3().glDebugMessageControlARB(source, type, severity, count, ids, ids_offset, enabled); + gl.getGL2GL3().glDebugMessageControl(source, type, severity, count, ids, ids_offset, enabled); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageEnableAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, count, ids, ids_offset, enabled); } @@ -2071,15 +2136,15 @@ public abstract class GLContextImpl extends GLContext { public final void glDebugMessageInsert(int source, int type, int id, int severity, String buf) { final int len = (null != buf) ? buf.length() : 0; if(glDebugHandler.isExtensionARB()) { - gl.getGL2GL3().glDebugMessageInsertARB(source, type, id, severity, len, buf); + gl.getGL2GL3().glDebugMessageInsert(source, type, id, severity, len, buf); } else if(glDebugHandler.isExtensionAMD()) { gl.getGL2GL3().glDebugMessageInsertAMD(GLDebugMessage.translateARB2AMDCategory(source, type), severity, id, len, buf); } } /** Internal bootstraping glGetString(GL_RENDERER) */ - protected static native String glGetStringInt(int name, long procAddress); + private static native String glGetStringInt(int name, long procAddress); /** Internal bootstraping glGetIntegerv(..) for version */ - protected static native void glGetIntegervInt(int pname, int[] params, int params_offset, long procAddress); + private static native void glGetIntegervInt(int pname, int[] params, int params_offset, long procAddress); } diff --git a/src/jogl/classes/jogamp/opengl/GLDebugMessageHandler.java b/src/jogl/classes/jogamp/opengl/GLDebugMessageHandler.java index 0000e6199..9ecaca75d 100644 --- a/src/jogl/classes/jogamp/opengl/GLDebugMessageHandler.java +++ b/src/jogl/classes/jogamp/opengl/GLDebugMessageHandler.java @@ -27,6 +27,8 @@ */ package jogamp.opengl; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import javax.media.nativewindow.NativeWindowException; @@ -39,8 +41,6 @@ import com.jogamp.common.os.Platform; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.opengl.GLExtensions; -import jogamp.opengl.gl4.GL4bcProcAddressTable; - /** * The GLDebugMessageHandler, handling <i>GL_ARB_debug_output</i> or <i>GL_AMD_debug_output</i> * debug messages.<br> @@ -107,6 +107,18 @@ public class GLDebugMessageHandler { } } + private final long getAddressFor(final ProcAddressTable table, final String functionName) { + return AccessController.doPrivileged(new PrivilegedAction<Long>() { + public Long run() { + try { + return Long.valueOf( table.getAddressFor(functionName) ); + } catch (IllegalArgumentException iae) { + return Long.valueOf(0); + } + } + } ).longValue(); + } + public void init() { ctx.validateCurrent(); if( isAvailable()) { @@ -148,17 +160,17 @@ public class GLDebugMessageHandler { } final ProcAddressTable procAddressTable = ctx.getGLProcAddressTable(); - if( procAddressTable instanceof GL4bcProcAddressTable) { - final GL4bcProcAddressTable desktopProcAddressTable = (GL4bcProcAddressTable)procAddressTable; + if( !ctx.isGLES1() && !ctx.isGLES2() ) { switch(extType) { case EXT_ARB: - glDebugMessageCallbackProcAddress = desktopProcAddressTable._addressof_glDebugMessageCallbackARB; + glDebugMessageCallbackProcAddress = getAddressFor(procAddressTable, "glDebugMessageCallbackARB"); break; case EXT_AMD: - glDebugMessageCallbackProcAddress = desktopProcAddressTable._addressof_glDebugMessageCallbackAMD; + glDebugMessageCallbackProcAddress = getAddressFor(procAddressTable, "glDebugMessageCallbackAMD"); break; } } else { + glDebugMessageCallbackProcAddress = 0; if(DEBUG) { System.err.println("Non desktop context not supported"); } @@ -212,9 +224,9 @@ public class GLDebugMessageHandler { private final void setSynchronousImpl() { if(isExtensionARB()) { if(synchronous) { - ctx.getGL().glEnable(GL2GL3.GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + ctx.getGL().glEnable(GL2GL3.GL_DEBUG_OUTPUT_SYNCHRONOUS); } else { - ctx.getGL().glDisable(GL2GL3.GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + ctx.getGL().glDisable(GL2GL3.GL_DEBUG_OUTPUT_SYNCHRONOUS); } if(DEBUG) { System.err.println("GLDebugMessageHandler: synchronous "+synchronous); diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java index 06e856d41..41ea06deb 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java @@ -247,7 +247,7 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { // @Override - public abstract boolean canCreateGLPbuffer(AbstractGraphicsDevice device); + public abstract boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp); @Override public GLPbuffer createGLPbuffer(AbstractGraphicsDevice deviceReq, @@ -263,7 +263,7 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { if(null == device) { throw new GLException("No shared device for requested: "+deviceReq); } - if ( !canCreateGLPbuffer(device) ) { + if ( !canCreateGLPbuffer(device, capsRequested.getGLProfile()) ) { throw new GLException("Pbuffer not available with device: "+device); } @@ -575,16 +575,16 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { rampEntry = 0.0f; gammaRamp[i] = rampEntry; } - registerGammaShutdownHook(); + needsGammaRampReset = true; return setGammaRamp(gammaRamp); } + @Override public synchronized void resetDisplayGamma() { - if (gammaShutdownHook == null) { - throw new IllegalArgumentException("Should not call this unless setDisplayGamma called first"); + if( needsGammaRampReset ) { + resetGammaRamp(originalGammaRamp); + needsGammaRampReset = false; } - resetGammaRamp(originalGammaRamp); - unregisterGammaShutdownHook(); } //------------------------------------------------------ @@ -616,35 +616,6 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { } // Shutdown hook mechanism for resetting gamma - private boolean gammaShutdownHookRegistered; - private Thread gammaShutdownHook; - private Buffer originalGammaRamp; - private synchronized void registerGammaShutdownHook() { - if (gammaShutdownHookRegistered) - return; - if (gammaShutdownHook == null) { - gammaShutdownHook = new Thread(new Runnable() { - @Override - public void run() { - synchronized (GLDrawableFactoryImpl.this) { - resetGammaRamp(originalGammaRamp); - } - } - }); - originalGammaRamp = getGammaRamp(); - } - Runtime.getRuntime().addShutdownHook(gammaShutdownHook); - gammaShutdownHookRegistered = true; - } - - private synchronized void unregisterGammaShutdownHook() { - if (!gammaShutdownHookRegistered) - return; - if (gammaShutdownHook == null) { - throw new InternalError("Error in gamma shutdown hook logic"); - } - Runtime.getRuntime().removeShutdownHook(gammaShutdownHook); - gammaShutdownHookRegistered = false; - // Leave the original gamma ramp data alone - } + private volatile Buffer originalGammaRamp; + private volatile boolean needsGammaRampReset = false; } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 177c465da..5418fbaf3 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -62,8 +62,13 @@ import javax.media.opengl.GLRunnable; methods to be able to share it between GLAutoDrawable implementations like GLAutoDrawableBase, GLCanvas and GLJPanel. */ public class GLDrawableHelper { /** true if property <code>jogl.debug.GLDrawable.PerfStats</code> is defined. */ - private static final boolean PERF_STATS = Debug.isPropertyDefined("jogl.debug.GLDrawable.PerfStats", true); + private static final boolean PERF_STATS; + static { + Debug.initSingleton(); + PERF_STATS = Debug.isPropertyDefined("jogl.debug.GLDrawable.PerfStats", true); + } + protected static final boolean DEBUG = GLDrawableImpl.DEBUG; private final Object listenersLock = new Object(); private final ArrayList<GLEventListener> listeners = new ArrayList<GLEventListener>(); @@ -531,10 +536,10 @@ public class GLDrawableHelper { } } - private final void init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) { + private final void init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape, boolean setViewport) { l.init(drawable); if(sendReshape) { - reshape(l, drawable, 0, 0, drawable.getWidth(), drawable.getHeight(), true /* setViewport */, false /* checkInit */); + reshape(l, drawable, 0, 0, drawable.getWidth(), drawable.getHeight(), setViewport, false /* checkInit */); } } @@ -545,33 +550,40 @@ public class GLDrawableHelper { public final void init(GLAutoDrawable drawable, boolean sendReshape) { synchronized(listenersLock) { final ArrayList<GLEventListener> _listeners = listeners; - for (int i=0; i < _listeners.size(); i++) { - final GLEventListener listener = _listeners.get(i) ; - - // If make ctx current, invoked by invokGL(..), results in a new ctx, init gets called. - // This may happen not just for initial setup, but for ctx recreation due to resource change (drawable/window), - // hence it must be called unconditional, always. - listenersToBeInit.remove(listener); // remove if exist, avoiding dbl init - init( listener, drawable, sendReshape); + final int listenerCount = _listeners.size(); + if( listenerCount > 0 ) { + for (int i=0; i < listenerCount; i++) { + final GLEventListener listener = _listeners.get(i) ; + + // If make ctx current, invoked by invokGL(..), results in a new ctx, init gets called. + // This may happen not just for initial setup, but for ctx recreation due to resource change (drawable/window), + // hence it must be called unconditional, always. + listenersToBeInit.remove(listener); // remove if exist, avoiding dbl init + init( listener, drawable, sendReshape, 0==i /* setViewport */); + } + } else { + // Expose same GL initialization if not using GLEventListener + drawable.getGL().glViewport(0, 0, drawable.getWidth(), drawable.getHeight()); } } } public final void display(GLAutoDrawable drawable) { displayImpl(drawable); - if(!execGLRunnables(drawable)) { + if( glRunnables.size()>0 && !execGLRunnables(drawable) ) { // glRunnables volatile OK; execGL.. only executed if size > 0 displayImpl(drawable); } } private final void displayImpl(GLAutoDrawable drawable) { synchronized(listenersLock) { final ArrayList<GLEventListener> _listeners = listeners; - for (int i=0; i < _listeners.size(); i++) { + final int listenerCount = _listeners.size(); + for (int i=0; i < listenerCount; i++) { final GLEventListener listener = _listeners.get(i) ; // GLEventListener may need to be init, // in case this one is added after the realization of the GLAutoDrawable if( listenersToBeInit.remove(listener) ) { - init( listener, drawable, true /* sendReshape */) ; + init( listener, drawable, true /* sendReshape */, listenersToBeInit.size() + 1 == listenerCount /* setViewport if 1st init */ ); } listener.display(drawable); } @@ -585,7 +597,7 @@ public class GLDrawableHelper { // in case this one is added after the realization of the GLAutoDrawable synchronized(listenersLock) { if( listenersToBeInit.remove(listener) ) { - init( listener, drawable, false /* sendReshape */) ; + listener.init(drawable); } } } @@ -598,29 +610,27 @@ public class GLDrawableHelper { public final void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { synchronized(listenersLock) { for (int i=0; i < listeners.size(); i++) { - reshape((GLEventListener) listeners.get(i), drawable, x, y, width, height, 0==i, true); + reshape((GLEventListener) listeners.get(i), drawable, x, y, width, height, 0==i /* setViewport */, true /* checkInit */); } } } - private final boolean execGLRunnables(GLAutoDrawable drawable) { + private final boolean execGLRunnables(GLAutoDrawable drawable) { // glRunnables.size()>0 boolean res = true; - if(glRunnables.size()>0) { // volatile OK - // swap one-shot list asap - final ArrayList<GLRunnableTask> _glRunnables; - synchronized(glRunnablesLock) { - if(glRunnables.size()>0) { - _glRunnables = glRunnables; - glRunnables = new ArrayList<GLRunnableTask>(); - } else { - _glRunnables = null; - } + // swap one-shot list asap + final ArrayList<GLRunnableTask> _glRunnables; + synchronized(glRunnablesLock) { + if(glRunnables.size()>0) { + _glRunnables = glRunnables; + glRunnables = new ArrayList<GLRunnableTask>(); + } else { + _glRunnables = null; } - - if(null!=_glRunnables) { - for (int i=0; i < _glRunnables.size(); i++) { - res = _glRunnables.get(i).run(drawable) && res; - } + } + + if(null!=_glRunnables) { + for (int i=0; i < _glRunnables.size(); i++) { + res = _glRunnables.get(i).run(drawable) && res; } } return res; diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java index 877e7b60b..e1088da29 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java @@ -116,6 +116,7 @@ public abstract class GLDrawableImpl implements GLDrawable { * {@link GL#glFlush()} has been called already and * the implementation may execute implementation specific code. * </p> + * @param doubleBuffered indicates whether double buffering is enabled, see above. */ protected abstract void swapBuffersImpl(boolean doubleBuffered); diff --git a/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java index 4c82fc2b3..8ba9f617b 100644 --- a/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java @@ -36,16 +36,29 @@ public abstract class GLDynamicLibraryBundleInfo implements DynamicLibraryBundle protected GLDynamicLibraryBundleInfo() { } - /** default **/ - @Override - public boolean shallLinkGlobal() { return false; } - - /** default **/ + /** + * Returns <code>true</code>, + * since we might load a desktop GL library and allow symbol access to subsequent libs. + * <p> + * This respects old DRI requirements: + * <pre> + * http://dri.sourceforge.net/doc/DRIuserguide.html + * </pre> + * </p> + */ + public final boolean shallLinkGlobal() { return true; } + + /** + * {@inheritDoc} + * <p> + * Returns <code>false</code>. + * </p> + */ @Override public boolean shallLookupGlobal() { return false; } @Override - public RunnableExecutor getLibLoaderExecutor() { + public final RunnableExecutor getLibLoaderExecutor() { return DynamicLibraryBundle.getDefaultRunnableExecutor(); } } diff --git a/src/jogl/classes/jogamp/opengl/GLDynamicLookupHelper.java b/src/jogl/classes/jogamp/opengl/GLDynamicLookupHelper.java index d2dac8148..1ed73f15e 100644 --- a/src/jogl/classes/jogamp/opengl/GLDynamicLookupHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDynamicLookupHelper.java @@ -36,7 +36,7 @@ public class GLDynamicLookupHelper extends DynamicLibraryBundle { super(info); } - public GLDynamicLibraryBundleInfo getGLBundleInfo() { return (GLDynamicLibraryBundleInfo) getBundleInfo(); } + public final GLDynamicLibraryBundleInfo getGLBundleInfo() { return (GLDynamicLibraryBundleInfo) getBundleInfo(); } /** NOP per default */ public boolean loadGLULibrary() { return false; } diff --git a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java index 85f63b52c..3833e6852 100644 --- a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java @@ -37,8 +37,14 @@ import com.jogamp.opengl.JoglVersion; * @see GLDrawableImpl#getDefaultReadFramebuffer() */ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { - protected static final boolean DEBUG = GLDrawableImpl.DEBUG || Debug.debug("FBObject"); - protected static final boolean DEBUG_SWAP = Debug.isPropertyDefined("jogl.debug.FBObject.Swap", true); + protected static final boolean DEBUG; + protected static final boolean DEBUG_SWAP; + + static { + Debug.initSingleton(); + DEBUG = GLDrawableImpl.DEBUG || Debug.debug("FBObject"); + DEBUG_SWAP = Debug.isPropertyDefined("jogl.debug.FBObject.Swap", true); + } private final GLDrawableImpl parent; private GLCapabilitiesImmutable origParentChosenCaps; @@ -52,7 +58,10 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { private int fboIBack; // points to GL_BACK buffer private int fboIFront; // points to GL_FRONT buffer private int pendingFBOReset = -1; + /** Indicated whether the FBO is bound. */ private boolean fboBound; + /** Indicated whether the FBO is swapped, resets to false after makeCurrent -> contextMadeCurrent. */ + private boolean fboSwapped; /** dump fboResetQuirk info only once pre ClassLoader and only in DEBUG mode */ private static volatile boolean resetQuirkInfoDumped = false; @@ -139,21 +148,20 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } } fbos[fboIFront].resetSamplingSink(gl); - fboBound = false; + fbos[0].formatToGLCapabilities(chosenFBOCaps); chosenFBOCaps.setDoubleBuffered( chosenFBOCaps.getDoubleBuffered() || samples > 0 ); - - initialized = true; } else { - initialized = false; - for(int i=0; i<fbos.length; i++) { fbos[i].destroy(gl); } fbos=null; - fboBound = false; - pendingFBOReset = -1; } + fboBound = false; + fboSwapped = false; + pendingFBOReset = -1; + initialized = realize; + if(DEBUG) { System.err.println("GLFBODrawableImpl.initialize("+realize+"): "+this); Thread.dumpStack(); @@ -230,6 +238,7 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { ourContext.makeCurrent(); gl.glFinish(); // sync GL command stream fboBound = false; // clear bound-flag immediatly, caused by contextMadeCurrent(..) - otherwise we would swap @ release + fboSwapped = false; try { final int maxSamples = gl.getMaxRenderbufferSamples(); newSamples = newSamples <= maxSamples ? newSamples : maxSamples; @@ -340,10 +349,12 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } fbos[fboIBack].bind(gl); fboBound = true; - } else if( fboBound ) { + fboSwapped = false; + } else if( fboBound && !fboSwapped ) { swapFBOImpl(glc); swapFBOImplPost(glc); fboBound=false; + fboSwapped=true; if(DEBUG_SWAP) { System.err.println("Post FBO swap(@release): done"); } @@ -353,14 +364,16 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { @Override protected void swapBuffersImpl(boolean doubleBuffered) { final GLContext ctx = GLContext.getCurrent(); - boolean doPostSwap = false; + boolean doPostSwap; if( null != ctx && ctx.getGLDrawable() == this && fboBound ) { swapFBOImpl(ctx); doPostSwap = true; - fboBound=false; + fboSwapped = true; if(DEBUG_SWAP) { System.err.println("Post FBO swap(@swap): done"); } + } else { + doPostSwap = false; } if( null != swapBufferContext ) { swapBufferContext.swapBuffers(doubleBuffered); diff --git a/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java b/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java index 48b509263..d54da4d28 100644 --- a/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java +++ b/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java @@ -200,8 +200,9 @@ public class GLGraphicsConfigurationUtil { if(null == device) { device = factory.getDefaultDevice(); } - final boolean fboAvailable = GLContext.isFBOAvailable(device, capsRequested.getGLProfile()); - final boolean pbufferAvailable = factory.canCreateGLPbuffer(device); + final GLProfile glp = capsRequested.getGLProfile(); + final boolean fboAvailable = GLContext.isFBOAvailable(device, glp); + final boolean pbufferAvailable = factory.canCreateGLPbuffer(device, glp); final GLRendererQuirks glrq = factory.getRendererQuirks(device); final boolean bitmapAvailable; diff --git a/src/jogl/classes/jogamp/opengl/GLVersionNumber.java b/src/jogl/classes/jogamp/opengl/GLVersionNumber.java index cea3ac4ab..e4187b35b 100644 --- a/src/jogl/classes/jogamp/opengl/GLVersionNumber.java +++ b/src/jogl/classes/jogamp/opengl/GLVersionNumber.java @@ -110,7 +110,7 @@ public class GLVersionNumber extends VersionNumberString { String str; { final GLVersionNumber glv = create(versionString); - str = versionString.substring(glv.endOfStringMatch()); + str = versionString.substring(glv.endOfStringMatch()).trim(); } while ( str.length() > 0 ) { @@ -120,7 +120,7 @@ public class GLVersionNumber extends VersionNumberString { if( version.hasMajor() && version.hasMinor() ) { // Requires at least a defined major and minor version component! return version; } - str = str.substring( eosm ); + str = str.substring( eosm ).trim(); } else { break; // no match } diff --git a/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java index cddd142e9..3d59d1d53 100644 --- a/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java @@ -36,8 +36,8 @@ import jogamp.opengl.*; * Implementation of the DynamicLookupHelper for Desktop ES2 (AMD, ..) * where EGL and ES2 functions reside within the desktop OpenGL library. */ -public class DesktopES2DynamicLibraryBundleInfo extends GLDynamicLibraryBundleInfo { - static List<String> glueLibNames; +public final class DesktopES2DynamicLibraryBundleInfo extends GLDynamicLibraryBundleInfo { + static final List<String> glueLibNames; static { glueLibNames = new ArrayList<String>(); glueLibNames.add("jogl_mobile"); @@ -47,16 +47,6 @@ public class DesktopES2DynamicLibraryBundleInfo extends GLDynamicLibraryBundleIn super(); } - /** - * Might be a desktop GL library, and might need to allow symbol access to subsequent libs. - * - * This respects old DRI requirements:<br> - * <pre> - * http://dri.sourceforge.net/doc/DRIuserguide.html - * </pre> - */ - public boolean shallLinkGlobal() { return true; } - public final List<String> getToolGetProcAddressFuncNameList() { List<String> res = new ArrayList<String>(); res.add("eglGetProcAddress"); @@ -71,7 +61,7 @@ public class DesktopES2DynamicLibraryBundleInfo extends GLDynamicLibraryBundleIn return true; } - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> libsGL = new ArrayList<String>(); diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLContext.java b/src/jogl/classes/jogamp/opengl/egl/EGLContext.java index 2b8ca31c9..e7977e3fb 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLContext.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLContext.java @@ -171,7 +171,7 @@ public class EGLContext extends GLContextImpl { try { // might be unavailable on EGL < 1.2 - if(!EGL.eglBindAPI(EGL.EGL_OPENGL_ES_API)) { + if( !EGL.eglBindAPI(EGL.EGL_OPENGL_ES_API) ) { throw new GLException("Catched: eglBindAPI to ES failed , error "+toHexString(EGL.eglGetError())); } } catch (GLException glex) { @@ -188,18 +188,21 @@ public class EGLContext extends GLContextImpl { } final IntBuffer contextAttrsNIO; + final int contextVersionReq, contextVersionAttr; { - final int[] contextAttrs = new int[] { - EGL.EGL_CONTEXT_CLIENT_VERSION, -1, - EGL.EGL_NONE - }; - if (glProfile.usesNativeGLES2()) { - contextAttrs[1] = 2; - } else if (glProfile.usesNativeGLES1()) { - contextAttrs[1] = 1; + if ( glProfile.usesNativeGLES3() ) { + contextVersionReq = 3; + contextVersionAttr = 2; + } else if ( glProfile.usesNativeGLES2() ) { + contextVersionReq = 2; + contextVersionAttr = 2; + } else if ( glProfile.usesNativeGLES1() ) { + contextVersionReq = 1; + contextVersionAttr = 1; } else { throw new GLException("Error creating OpenGL context - invalid GLProfile: "+glProfile); } + final int[] contextAttrs = new int[] { EGL.EGL_CONTEXT_CLIENT_VERSION, contextVersionAttr, EGL.EGL_NONE }; contextAttrsNIO = Buffers.newDirectIntBuffer(contextAttrs); } contextHandle = EGL.eglCreateContext(eglDisplay, eglConfig, shareWithHandle, contextAttrsNIO); @@ -219,8 +222,7 @@ public class EGLContext extends GLContextImpl { throw new GLException("Error making context " + toHexString(contextHandle) + " current: error code " + toHexString(EGL.eglGetError())); } - setGLFunctionAvailability(true, glProfile.usesNativeGLES2() ? 2 : 1, 0, CTX_PROFILE_ES, false); - return true; + return setGLFunctionAvailability(true, contextVersionReq, 0, CTX_PROFILE_ES, contextVersionReq>=3); // strict match for es >= 3 } @Override @@ -259,8 +261,7 @@ public class EGLContext extends GLContextImpl { protected final StringBuilder getPlatformExtensionsStringImpl() { StringBuilder sb = new StringBuilder(); if (!eglQueryStringInitialized) { - eglQueryStringAvailable = - getDrawableImpl().getGLDynamicLookupHelper().dynamicLookupFunction("eglQueryString") != 0; + eglQueryStringAvailable = getDrawableImpl().getGLDynamicLookupHelper().isFunctionAvailable("eglQueryString"); eglQueryStringInitialized = true; } if (eglQueryStringAvailable) { @@ -293,16 +294,25 @@ public class EGLContext extends GLContextImpl { final GLProfile glp = caps.getGLProfile(); final int[] reqMajorCTP = new int[2]; GLContext.getRequestMajorAndCompat(glp, reqMajorCTP); - if(glp.isGLES() && reqMajorCTP[0] >= 2) { - reqMajorCTP[1] |= GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ; + if( glp.isGLES() ) { + if( reqMajorCTP[0] >= 3 ) { + reqMajorCTP[1] |= GLContext.CTX_IMPL_ES3_COMPAT | GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ; + } else if( reqMajorCTP[0] >= 2 ) { + reqMajorCTP[1] |= GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ; + } } - if(!caps.getHardwareAccelerated()) { + if( !caps.getHardwareAccelerated() ) { reqMajorCTP[1] |= GLContext.CTX_IMPL_ACCEL_SOFT; } mapStaticGLVersion(device, reqMajorCTP[0], 0, reqMajorCTP[1]); } - /* pp */ static void mapStaticGLESVersion(AbstractGraphicsDevice device, int major) { - int ctp = ( 2 == major ) ? ( GLContext.CTX_PROFILE_ES | GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ) : ( GLContext.CTX_PROFILE_ES ); + /* pp */ static void mapStaticGLESVersion(AbstractGraphicsDevice device, final int major) { + int ctp = GLContext.CTX_PROFILE_ES; + if( major >= 3 ) { + ctp |= GLContext.CTX_IMPL_ES3_COMPAT | GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ; + } else if( major >= 2 ) { + ctp |= GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_FBO ; + } mapStaticGLVersion(device, major, 0, ctp); } /* pp */ static void mapStaticGLVersion(AbstractGraphicsDevice device, int major, int minor, int ctp) { @@ -344,9 +354,13 @@ public class EGLContext extends GLContextImpl { throw new GLException("Not yet implemented"); } - @Override - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { + public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) { + throw new GLException("Should not call this"); + } + + @Override + public final void glFreeMemoryNV(ByteBuffer pointer) { throw new GLException("Should not call this"); } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java index adb78b3b9..5d99e3eba 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java @@ -84,26 +84,30 @@ import com.jogamp.opengl.GLRendererQuirks; public class EGLDrawableFactory extends GLDrawableFactoryImpl { protected static final boolean DEBUG = GLDrawableFactoryImpl.DEBUG; // allow package access - /* package */ static final boolean QUERY_EGL_ES_NATIVE_TK = Debug.isPropertyDefined("jogl.debug.EGLDrawableFactory.QueryNativeTK", true); + /* package */ static final boolean QUERY_EGL_ES_NATIVE_TK; + + static { + Debug.initSingleton(); + QUERY_EGL_ES_NATIVE_TK = Debug.isPropertyDefined("jogl.debug.EGLDrawableFactory.QueryNativeTK", true); + } private static GLDynamicLookupHelper eglES1DynamicLookupHelper = null; private static GLDynamicLookupHelper eglES2DynamicLookupHelper = null; private static final boolean isANGLE(GLDynamicLookupHelper dl) { if(Platform.OSType.WINDOWS == Platform.OS_TYPE) { - final boolean r = 0 != dl.dynamicLookupFunction("eglQuerySurfacePointerANGLE") || - 0 != dl.dynamicLookupFunction("glBlitFramebufferANGLE") || - 0 != dl.dynamicLookupFunction("glRenderbufferStorageMultisampleANGLE"); - return r; + return dl.isFunctionAvailable("eglQuerySurfacePointerANGLE") || + dl.isFunctionAvailable("glBlitFramebufferANGLE") || + dl.isFunctionAvailable("glRenderbufferStorageMultisampleANGLE"); } else { return false; } } private static final boolean includesES1(GLDynamicLookupHelper dl) { - return 0 != dl.dynamicLookupFunction("glLoadIdentity") && - 0 != dl.dynamicLookupFunction("glEnableClientState") && - 0 != dl.dynamicLookupFunction("glColorPointer"); + return dl.isFunctionAvailable("glLoadIdentity") && + dl.isFunctionAvailable("glEnableClientState") && + dl.isFunctionAvailable("glColorPointer"); } public EGLDrawableFactory() { @@ -253,8 +257,8 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { String key = keyI.next(); SharedResource sr = sharedMap.get(key); System.err.println("EGLDrawableFactory.map["+i+"] "+key+" -> "+sr.getDevice()+", "+ - "es1 [avail "+sr.wasES1ContextCreated+", pbuffer "+sr.hasPBufferES1+", quirks "+sr.rendererQuirksES1+", ctp "+EGLContext.getGLVersion(1, 0, sr.ctpES1, null)+"], "+ - "es2 [avail "+sr.wasES2ContextCreated+", pbuffer "+sr.hasPBufferES2+", quirks "+sr.rendererQuirksES2+", ctp "+EGLContext.getGLVersion(2, 0, sr.ctpES2, null)+"]"); + "es1 [avail "+sr.wasES1ContextCreated+", pbuffer "+sr.hasPBufferES1+", quirks "+sr.rendererQuirksES1+", ctp "+EGLContext.getGLVersion(1, 0, sr.ctpES1, null)+"], "+ + "es2/3 [es2 "+sr.wasES2ContextCreated+", es3 "+sr.wasES3ContextCreated+", [pbuffer "+sr.hasPBufferES3ES2+", quirks "+sr.rendererQuirksES3ES2+", ctp "+EGLContext.getGLVersion(2, 0, sr.ctpES3ES2, null)+"]]"); } ; } @@ -271,38 +275,45 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { private final EGLGraphicsDevice device; // private final EGLContext contextES1; // private final EGLContext contextES2; - private final GLRendererQuirks rendererQuirksES1; - private final GLRendererQuirks rendererQuirksES2; - private final int ctpES1; - private final int ctpES2; + // private final EGLContext contextES3; private final boolean wasES1ContextCreated; private final boolean wasES2ContextCreated; + private final boolean wasES3ContextCreated; + private final GLRendererQuirks rendererQuirksES1; + private final GLRendererQuirks rendererQuirksES3ES2; + private final int ctpES1; + private final int ctpES3ES2; private final boolean hasPBufferES1; - private final boolean hasPBufferES2; + private final boolean hasPBufferES3ES2; SharedResource(EGLGraphicsDevice dev, boolean wasContextES1Created, boolean hasPBufferES1, GLRendererQuirks rendererQuirksES1, int ctpES1, - boolean wasContextES2Created, boolean hasPBufferES2, GLRendererQuirks rendererQuirksES2, int ctpES2) { + boolean wasContextES2Created, boolean wasContextES3Created, + boolean hasPBufferES3ES2, GLRendererQuirks rendererQuirksES3ES2, int ctpES3ES2) { this.device = dev; // this.contextES1 = ctxES1; - // this.contextES2 = ctxES2; + this.wasES1ContextCreated = wasContextES1Created; + this.hasPBufferES1= hasPBufferES1; this.rendererQuirksES1 = rendererQuirksES1; - this.rendererQuirksES2 = rendererQuirksES2; this.ctpES1 = ctpES1; - this.ctpES2 = ctpES2; - this.wasES1ContextCreated = wasContextES1Created; + + // this.contextES2 = ctxES2; + // this.contextES3 = ctxES3; this.wasES2ContextCreated = wasContextES2Created; - this.hasPBufferES1= hasPBufferES1; - this.hasPBufferES2= hasPBufferES2; + this.wasES3ContextCreated = wasContextES3Created; + this.hasPBufferES3ES2= hasPBufferES3ES2; + this.rendererQuirksES3ES2 = rendererQuirksES3ES2; + this.ctpES3ES2 = ctpES3ES2; } @Override public final boolean isValid() { - return wasES1ContextCreated || wasES2ContextCreated; + return wasES1ContextCreated || wasES2ContextCreated || wasES3ContextCreated; } @Override public final EGLGraphicsDevice getDevice() { return device; } // final EGLContext getContextES1() { return contextES1; } // final EGLContext getContextES2() { return contextES2; } + // final EGLContext getContextES3() { return contextES3; } @Override public AbstractGraphicsScreen getScreen() { @@ -318,7 +329,7 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { } @Override public GLRendererQuirks getRendererQuirks() { - return null != rendererQuirksES2 ? rendererQuirksES2 : rendererQuirksES1 ; + return null != rendererQuirksES3ES2 ? rendererQuirksES3ES2 : rendererQuirksES1 ; } } @@ -353,18 +364,30 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { boolean[] hasPBuffer, GLRendererQuirks[] rendererQuirks, int[] ctp) { final String profileString; switch( esProfile ) { + case 3: + profileString = GLProfile.GLES3; break; + case 2: + profileString = GLProfile.GLES2; break; case 1: profileString = GLProfile.GLES1; break; - case 2: default: - profileString = GLProfile.GLES2; break; + throw new GLException("Invalid ES profile number "+esProfile); } if ( !GLProfile.isAvailable(adevice, profileString) ) { + if( DEBUG ) { + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: "+profileString+" n/a on "+adevice); + } return false; } final GLProfile glp = GLProfile.get(adevice, profileString) ; final GLDrawableFactoryImpl desktopFactory = (GLDrawableFactoryImpl) GLDrawableFactory.getDesktopFactory(); final boolean mapsADeviceToDefaultDevice = !QUERY_EGL_ES_NATIVE_TK || null == desktopFactory || adevice instanceof EGLGraphicsDevice ; + if( DEBUG ) { + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: "+profileString+" ( "+esProfile+" ), "+ + "defaultSharedResourceSet "+(null!=defaultSharedResource)+", mapsADeviceToDefaultDevice "+mapsADeviceToDefaultDevice+ + " (QUERY_EGL_ES_NATIVE_TK "+QUERY_EGL_ES_NATIVE_TK+", hasDesktopFactory "+(null != desktopFactory)+ + ", isEGLGraphicsDevice "+(adevice instanceof EGLGraphicsDevice)+")"); + } EGLGraphicsDevice eglDevice = null; NativeSurface surface = null; @@ -387,16 +410,29 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { if( adevice != defaultDevice ) { if(null == defaultSharedResource) { return false; - } + } switch(esProfile) { + case 3: + if( !defaultSharedResource.wasES3ContextCreated ) { + return false; + } + rendererQuirks[0] = defaultSharedResource.rendererQuirksES3ES2; + ctp[0] = defaultSharedResource.ctpES3ES2; + break; + case 2: + if( !defaultSharedResource.wasES2ContextCreated ) { + return false; + } + rendererQuirks[0] = defaultSharedResource.rendererQuirksES3ES2; + ctp[0] = defaultSharedResource.ctpES3ES2; + break; case 1: + if( !defaultSharedResource.wasES1ContextCreated ) { + return false; + } rendererQuirks[0] = defaultSharedResource.rendererQuirksES1; ctp[0] = defaultSharedResource.ctpES1; break; - case 2: - rendererQuirks[0] = defaultSharedResource.rendererQuirksES2; - ctp[0] = defaultSharedResource.ctpES2; - break; } EGLContext.mapStaticGLVersion(adevice, esProfile, 0, ctp[0]); return true; @@ -421,7 +457,7 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { success = true; } if(DEBUG) { - System.err.println("EGLDrawableFactory.isEGLContextAvailable() no pbuffer config available, detected !pbuffer config: "+success); + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig() no pbuffer config available, detected !pbuffer config: "+success); EGLGraphicsConfigurationFactory.printCaps("!PBufferCaps", capsAnyL, System.err); } } @@ -457,13 +493,13 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { } else { // Oops .. something is wrong if(DEBUG) { - System.err.println("EGLDrawableFactory.isEGLContextAvailable: "+eglDevice+", "+context.getGLVersion()+" - VERSION is null, dropping availability!"); + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: "+eglDevice+", "+context.getGLVersion()+" - VERSION is null, dropping availability!"); } } } } catch (GLException gle) { if (DEBUG) { - System.err.println("EGLDrawableFactory.createShared: INFO: context create/makeCurrent failed"); + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: INFO: context create/makeCurrent failed"); gle.printStackTrace(); } } finally { @@ -557,14 +593,15 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { private SharedResource createEGLSharedResourceImpl(AbstractGraphicsDevice adevice) { final boolean madeCurrentES1; final boolean madeCurrentES2; + final boolean madeCurrentES3; boolean[] hasPBufferES1 = new boolean[] { false }; - boolean[] hasPBufferES2 = new boolean[] { false }; + boolean[] hasPBufferES3ES2 = new boolean[] { false }; // EGLContext[] eglCtxES1 = new EGLContext[] { null }; // EGLContext[] eglCtxES2 = new EGLContext[] { null }; GLRendererQuirks[] rendererQuirksES1 = new GLRendererQuirks[] { null }; - GLRendererQuirks[] rendererQuirksES2 = new GLRendererQuirks[] { null }; + GLRendererQuirks[] rendererQuirksES3ES2 = new GLRendererQuirks[] { null }; int[] ctpES1 = new int[] { -1 }; - int[] ctpES2 = new int[] { -1 }; + int[] ctpES3ES2 = new int[] { -1 }; if (DEBUG) { @@ -577,9 +614,16 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { madeCurrentES1 = false; } if( null != eglES2DynamicLookupHelper ) { - madeCurrentES2 = mapAvailableEGLESConfig(adevice, 2, hasPBufferES2, rendererQuirksES2, ctpES2); + madeCurrentES3 = mapAvailableEGLESConfig(adevice, 3, hasPBufferES3ES2, rendererQuirksES3ES2, ctpES3ES2); + if( madeCurrentES3 ) { + madeCurrentES2 = true; + EGLContext.mapStaticGLVersion(adevice, 2, 0, ctpES3ES2[0]); + } else { + madeCurrentES2 = mapAvailableEGLESConfig(adevice, 2, hasPBufferES3ES2, rendererQuirksES3ES2, ctpES3ES2); + } } else { madeCurrentES2 = false; + madeCurrentES3 = false; } if( !EGLContext.getAvailableGLVersionsSet(adevice) ) { @@ -589,10 +633,10 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { } if( hasX11 ) { handleDontCloseX11DisplayQuirk(rendererQuirksES1[0]); - handleDontCloseX11DisplayQuirk(rendererQuirksES2[0]); + handleDontCloseX11DisplayQuirk(rendererQuirksES3ES2[0]); } final SharedResource sr = new SharedResource(defaultDevice, madeCurrentES1, hasPBufferES1[0], rendererQuirksES1[0], ctpES1[0], - madeCurrentES2, hasPBufferES2[0], rendererQuirksES2[0], ctpES2[0]); + madeCurrentES2, madeCurrentES3, hasPBufferES3ES2[0], rendererQuirksES3ES2[0], ctpES3ES2[0]); synchronized(sharedMap) { sharedMap.put(adevice.getUniqueID(), sr); @@ -600,7 +644,8 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { if (DEBUG) { System.err.println("EGLDrawableFactory.createShared: devices: queried nativeTK "+QUERY_EGL_ES_NATIVE_TK+", adevice " + adevice + ", defaultDevice " + defaultDevice); System.err.println("EGLDrawableFactory.createShared: context ES1: " + madeCurrentES1 + ", hasPBuffer "+hasPBufferES1[0]); - System.err.println("EGLDrawableFactory.createShared: context ES2: " + madeCurrentES2 + ", hasPBuffer "+hasPBufferES2[0]); + System.err.println("EGLDrawableFactory.createShared: context ES2: " + madeCurrentES2 + ", hasPBuffer "+hasPBufferES3ES2[0]); + System.err.println("EGLDrawableFactory.createShared: context ES3: " + madeCurrentES3 + ", hasPBuffer "+hasPBufferES3ES2[0]); dumpMap(); } return sr; @@ -663,7 +708,7 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { } @Override - public boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { + public boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { // SharedResource sr = getOrCreateEGLSharedResource(device); // return sr.hasES1PBuffer() || sr.hasES2PBuffer(); return true; diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java index fe9d7573d..778f0cb38 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java @@ -29,6 +29,7 @@ package jogamp.opengl.egl; import com.jogamp.common.os.AndroidVersion; +import com.jogamp.common.os.Platform; import java.util.*; @@ -38,10 +39,10 @@ import jogamp.opengl.*; * Abstract implementation of the DynamicLookupHelper for EGL, * which decouples it's dependencies to EGLDrawable. * - * Currently two implementations exist, one for ES1 and one for ES2. + * Currently two implementations exist, one for ES1 and one for ES3 and ES2. */ public abstract class EGLDynamicLibraryBundleInfo extends GLDynamicLibraryBundleInfo { - static List<String> glueLibNames; + static final List<String> glueLibNames; static { glueLibNames = new ArrayList<String>(); glueLibNames.add("jogl_mobile"); @@ -52,19 +53,12 @@ public abstract class EGLDynamicLibraryBundleInfo extends GLDynamicLibraryBundle } /** - * Might be a desktop GL library, and might need to allow symbol access to subsequent libs. - * - * This respects old DRI requirements:<br> - * <pre> - * http://dri.sourceforge.net/doc/DRIuserguide.html - * </pre> + * Returns <code>true</code> on <code>Android</code>, + * and <code>false</code> otherwise. */ @Override - public boolean shallLinkGlobal() { return true; } - - @Override - public boolean shallLookupGlobal() { - if ( AndroidVersion.isAvailable ) { + public final boolean shallLookupGlobal() { + if ( Platform.OSType.ANDROID == Platform.OS_TYPE ) { // Android requires global symbol lookup return true; } @@ -94,7 +88,7 @@ public abstract class EGLDynamicLibraryBundleInfo extends GLDynamicLibraryBundle } } - protected List<String> getEGLLibNamesList() { + protected final List<String> getEGLLibNamesList() { List<String> eglLibNames = new ArrayList<String>(); // this is the default EGL lib name, according to the spec diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java index 0a373eb7f..dd3d6faea 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java @@ -30,12 +30,12 @@ package jogamp.opengl.egl; import java.util.*; -public class EGLES1DynamicLibraryBundleInfo extends EGLDynamicLibraryBundleInfo { +public final class EGLES1DynamicLibraryBundleInfo extends EGLDynamicLibraryBundleInfo { protected EGLES1DynamicLibraryBundleInfo() { super(); } - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); { final List<String> libsGL = new ArrayList<String>(); diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java index d4ee852b1..0d20fd4e8 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java @@ -30,28 +30,48 @@ package jogamp.opengl.egl; import java.util.*; -public class EGLES2DynamicLibraryBundleInfo extends EGLDynamicLibraryBundleInfo { +/** + * <p> + * Covering ES3 and ES2. + * </p> + */ +public final class EGLES2DynamicLibraryBundleInfo extends EGLDynamicLibraryBundleInfo { protected EGLES2DynamicLibraryBundleInfo() { super(); } - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); { final List<String> libsGL = new ArrayList<String>(); - // this is the default lib name, according to the spec + // ES3: This is the default lib name, according to the spec + libsGL.add("libGLESv3.so.3"); + + // ES3: Try these as well, if spec fails + libsGL.add("libGLESv3.so"); + libsGL.add("GLESv3"); + + // ES3: Alternative names + libsGL.add("GLES30"); + + // ES3: For windows distributions using the 'unlike' lib prefix + // where our tool does not add it. + libsGL.add("libGLESv3"); + libsGL.add("libGLES30"); + + // ES2: This is the default lib name, according to the spec libsGL.add("libGLESv2.so.2"); - // try these as well, if spec fails + // ES2: Try these as well, if spec fails libsGL.add("libGLESv2.so"); libsGL.add("GLESv2"); - // alternative names + // ES2: Alternative names libsGL.add("GLES20"); libsGL.add("GLESv2_CM"); - // for windows distributions using the 'unlike' lib prefix + // ES2: For windows distributions using the 'unlike' lib prefix // where our tool does not add it. libsGL.add("libGLESv2"); libsGL.add("libGLESv2_CM"); diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java b/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java index f857c6b5c..b61624d79 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java @@ -101,12 +101,16 @@ public class EGLGLCapabilities extends GLCapabilities { if(null == glp) { return true; } - if(0 != (renderableType & EGL.EGL_OPENGL_ES_BIT) && glp.usesNativeGLES1()) { + /** FIXME: EGLExt.EGL_OPENGL_ES3_BIT_KHR OK ? */ + if(0 != (renderableType & EGLExt.EGL_OPENGL_ES3_BIT_KHR) && glp.usesNativeGLES3()) { return true; } if(0 != (renderableType & EGL.EGL_OPENGL_ES2_BIT) && glp.usesNativeGLES2()) { return true; } + if(0 != (renderableType & EGL.EGL_OPENGL_ES_BIT) && glp.usesNativeGLES1()) { + return true; + } if(0 != (renderableType & EGL.EGL_OPENGL_BIT) && !glp.usesNativeGLES()) { return true; } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index b90609ff1..6787ef500 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -52,7 +52,7 @@ import javax.media.nativewindow.OffscreenLayerSurface; import javax.media.nativewindow.ProxySurface; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; -import javax.media.opengl.GL3; +import javax.media.opengl.GL3ES3; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; @@ -74,14 +74,13 @@ import com.jogamp.common.util.VersionNumber; import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; -import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.util.PMVMatrix; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; public class MacOSXCGLContext extends GLContextImpl -{ +{ // Abstract interface for implementation of this context (either // NSOpenGL-based or CGL-based) protected interface GLBackendImpl { @@ -142,7 +141,7 @@ public class MacOSXCGLContext extends GLContextImpl private static final String shaderBasename = "texture01_xxx"; - private static ShaderProgram createCALayerShader(GL3 gl) { + private static ShaderProgram createCALayerShader(GL3ES3 gl) { // Create & Link the shader program final ShaderProgram sp = new ShaderProgram(); final ShaderCode vp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, MacOSXCGLContext.class, @@ -420,12 +419,18 @@ public class MacOSXCGLContext extends GLContextImpl } @Override - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { + public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) { // FIXME: apparently the Apple extension doesn't require a custom memory allocator throw new GLException("Not yet implemented"); } @Override + public final void glFreeMemoryNV(ByteBuffer pointer) { + // FIXME: apparently the Apple extension doesn't require a custom memory allocator + throw new GLException("Not yet implemented"); + } + + @Override protected final void updateGLXProcAddressTable() { final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); @@ -459,15 +464,6 @@ public class MacOSXCGLContext extends GLContextImpl return new StringBuilder(); } - @Override - public boolean isExtensionAvailable(String glExtensionName) { - if (glExtensionName.equals(GLExtensions.ARB_pbuffer) || - glExtensionName.equals(GLExtensions.ARB_pixel_format)) { - return true; - } - return super.isExtensionAvailable(glExtensionName); - } - // Support for "mode switching" as described in MacOSXCGLDrawable public void setOpenGLMode(GLBackendType mode) { if (mode == openGLMode) { @@ -846,7 +842,7 @@ public class MacOSXCGLContext extends GLContextImpl } if( MacOSXCGLContext.this.isGL3core() ) { if( null == gl3ShaderProgram) { - gl3ShaderProgram = createCALayerShader(MacOSXCGLContext.this.gl.getGL3()); + gl3ShaderProgram = createCALayerShader(MacOSXCGLContext.this.gl.getGL3ES3()); } gl3ShaderProgramName = gl3ShaderProgram.program(); } else { @@ -939,7 +935,7 @@ public class MacOSXCGLContext extends GLContextImpl if(0 == cglCtx) { throw new InternalError("Null CGLContext for: "+this); } - int err = CGL.CGLLockContext(cglCtx); + final int err = CGL.CGLLockContext(cglCtx); if(CGL.kCGLNoError == err) { validatePBufferConfig(ctx); // required to handle pbuffer change ASAP return CGL.makeCurrentContext(ctx); @@ -1000,6 +996,8 @@ public class MacOSXCGLContext extends GLContextImpl CGL.setNSOpenGLLayerSwapInterval(l, interval); if( 0 < interval ) { vsyncTimeout = interval * screenVSyncTimeout + 1000; // +1ms + } else { + vsyncTimeout = 1 * screenVSyncTimeout + 1000; // +1ms } if(DEBUG) { System.err.println("NS setSwapInterval: "+interval+" -> "+vsyncTimeout+" micros"); } } @@ -1008,6 +1006,14 @@ public class MacOSXCGLContext extends GLContextImpl } private int skipSync=0; + /** TODO: Remove after discussion + private boolean perfIterReset = false; + private int perfIter = 0; + private long waitGLS = 0; + private long finishGLS = 0; + private long frameXS = 0; + private long lastFrameStart = 0; + */ @Override public boolean swapBuffers() { @@ -1036,6 +1042,45 @@ public class MacOSXCGLContext extends GLContextImpl res = CGL.flushBuffer(contextHandle); if(res) { if(0 == skipSync) { + /** TODO: Remove after discussion + perfIter++; + if( !perfIterReset && 100 == perfIter ) { + perfIterReset = true; + perfIter = 1; + waitGLS = 0; + finishGLS = 0; + frameXS = 0; + } + final long lastFramePeriod0 = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()) - lastFrameStart; + gl.glFinish(); // Require to finish previous GL rendering to give CALayer proper result + final long lastFramePeriod1 = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()) - lastFrameStart; + + // If v-sync is disabled, frames will be drawn as quickly as possible w/o delay, + // while still synchronizing w/ CALayer. + // If v-sync is enabled wait until next swap interval (v-sync). + CGL.waitUntilNSOpenGLLayerIsReady(cmd.nsOpenGLLayer, vsyncTimeout); + final long lastFramePeriodX = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()) - lastFrameStart; + + final long finishGL = lastFramePeriod1 - lastFramePeriod0; + final long waitGL = lastFramePeriodX - lastFramePeriod1; + finishGLS += finishGL; + waitGLS += waitGL; + frameXS += lastFramePeriodX; + + System.err.println("XXX["+perfIter+"] TO "+vsyncTimeout/1000+" ms, "+ + "lFrame0 "+lastFramePeriod0/1000+" ms, "+ + "lFrameX "+lastFramePeriodX/1000+" / "+frameXS/1000+" ~"+(frameXS/perfIter)/1000.0+" ms, "+ + "finishGL "+finishGL/1000+" / "+finishGLS/1000+" ~"+(finishGLS/perfIter)/1000.0+" ms, "+ + "waitGL "+waitGL/1000+" / "+waitGLS/1000+" ~"+(waitGLS/perfIter)/1000.0+" ms"); + */ + // + // Required(?) to finish previous GL rendering to give CALayer proper result, + // i.e. synchronize both threads each w/ their GLContext sharing same resources. + // + // FIXME: IMHO this synchronization should be implicitly performed via 'CGL.flushBuffer(contextHandle)' above, + // in case this will be determined a driver bug - use a QUIRK entry in GLRendererQuirks! + gl.glFinish(); + // If v-sync is disabled, frames will be drawn as quickly as possible w/o delay, // while still synchronizing w/ CALayer. // If v-sync is enabled wait until next swap interval (v-sync). @@ -1050,6 +1095,7 @@ public class MacOSXCGLContext extends GLContextImpl // trigger CALayer to update incl. possible surface change (new pbuffer handle) CGL.setNSOpenGLLayerNeedsDisplayPBuffer(cmd.nsOpenGLLayer, drawable.getHandle()); } + // lastFrameStart = TimeUnit.NANOSECONDS.toMicros(System.nanoTime()); } } else { res = true; diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java index 910158d1f..4bd7bc994 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java @@ -117,8 +117,7 @@ public abstract class MacOSXCGLDrawable extends GLDrawableImpl { createdContexts.add(new WeakReference<MacOSXCGLContext>(osxCtx)); } else { for(int i=0; i<createdContexts.size(); ) { - final WeakReference<MacOSXCGLContext> ref = createdContexts.get(i); - final MacOSXCGLContext _ctx = ref.get(); + final MacOSXCGLContext _ctx = createdContexts.get(i).get(); if( _ctx == null || _ctx == ctx) { createdContexts.remove(i); } else { @@ -134,8 +133,7 @@ public abstract class MacOSXCGLDrawable extends GLDrawableImpl { if(doubleBuffered) { synchronized (createdContexts) { for(int i=0; i<createdContexts.size(); ) { - final WeakReference<MacOSXCGLContext> ref = createdContexts.get(i); - final MacOSXCGLContext ctx = ref.get(); + final MacOSXCGLContext ctx = createdContexts.get(i).get(); if (ctx != null) { ctx.swapBuffers(); i++; diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java index c9402b33d..83d656475 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java @@ -332,8 +332,13 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { } @Override - public boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { - return true; + public boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { + if( glp.isGL2() ) { + // OSX only supports pbuffer w/ compatible, non-core, context. + return true; + } else { + return false; + } } @Override diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java index 03ec94db6..f8c874a53 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java @@ -31,13 +31,13 @@ package jogamp.opengl.macosx.cgl; import jogamp.opengl.*; import java.util.*; -public class MacOSXCGLDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundleInfo { +public final class MacOSXCGLDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundleInfo { protected MacOSXCGLDynamicLibraryBundleInfo() { super(); } @Override - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> libsGL = new ArrayList<String>(); libsGL.add("/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"); diff --git a/src/jogl/classes/jogamp/opengl/openal/av/ALDummyUsage.java b/src/jogl/classes/jogamp/opengl/openal/av/ALDummyUsage.java new file mode 100644 index 000000000..69223d0b9 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/openal/av/ALDummyUsage.java @@ -0,0 +1,19 @@ +package jogamp.opengl.openal.av; + +import jogamp.opengl.util.av.impl.FFMPEGMediaPlayer; + +import com.jogamp.openal.AL; +import com.jogamp.openal.JoalVersion; + +/** + * Demo JOAL usage w/ av dependency, i.e. FFMPEGMediaPlayer .. + */ +public class ALDummyUsage { + static AL al; + static FFMPEGMediaPlayer.PixelFormat pfmt; + + public static void main(String args[]) { + System.err.println("JOGL> Hello JOAL"); + System.err.println("JOAL: "+JoalVersion.getInstance().toString()); + } +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java index 32c863553..2d40fe4ec 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java @@ -28,6 +28,8 @@ package jogamp.opengl.util.av.impl; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -53,10 +55,10 @@ import com.jogamp.common.util.RunnableExecutor; * Tue Feb 28 12:07:53 2012 322537478b63c6bc01e640643550ff539864d790 minor 1 -> 2 */ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { - private static List<String> glueLibNames = new ArrayList<String>(); // none + private static final List<String> glueLibNames = new ArrayList<String>(); // none private static final int symbolCount = 31; - private static String[] symbolNames = { + private static final String[] symbolNames = { "avcodec_version", "avformat_version", /* 3 */ "avutil_version", @@ -97,7 +99,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { }; // alternate symbol names - private static String[][] altSymbolNames = { + private static final String[][] altSymbolNames = { { "avcodec_open", "avcodec_open2" }, // old, 53.6.0 { "avcodec_decode_audio3", "avcodec_decode_audio4" }, // old, 53.25.0 { "av_close_input_file", "avformat_close_input" }, // old, 53.17.0 @@ -105,7 +107,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { }; // optional symbol names - private static String[] optionalSymbolNames = { + private static final String[] optionalSymbolNames = { "avformat_free_context", // 52.96.0 (opt) "avformat_network_init", // 53.13.0 (opt) "avformat_network_deinit", // 53.13.0 (opt) @@ -131,8 +133,11 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { static boolean initSingleton() { return ready; } - private static boolean initSymbols() { - final DynamicLibraryBundle dl = new DynamicLibraryBundle(new FFMPEGDynamicLibraryBundleInfo()); + private static final boolean initSymbols() { + final DynamicLibraryBundle dl = AccessController.doPrivileged(new PrivilegedAction<DynamicLibraryBundle>() { + public DynamicLibraryBundle run() { + return new DynamicLibraryBundle(new FFMPEGDynamicLibraryBundleInfo()); + } } ); final boolean avutilLoaded = dl.isToolLibLoaded(0); final boolean avformatLoaded = dl.isToolLibLoaded(1); final boolean avcodecLoaded = dl.isToolLibLoaded(2); @@ -166,9 +171,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } // lookup - for(int i = 0; i<symbolCount; i++) { - symbolAddr[i] = dl.dynamicLookupFunction(symbolNames[i]); - } + AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + for(int i = 0; i<symbolCount; i++) { + symbolAddr[i] = dl.dynamicLookupFunction(symbolNames[i]); + } + return null; + } } ); // validate results for(int i = 0; i<symbolCount; i++) { @@ -206,10 +215,18 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } @Override - public boolean shallLinkGlobal() { return true; } + public final boolean shallLinkGlobal() { return true; } + /** + * {@inheritDoc} + * <p> + * Returns <code>true</code>. + * </p> + */ @Override - public boolean shallLookupGlobal() { return true; } + public final boolean shallLookupGlobal() { + return true; + } @Override public final List<String> getGlueLibNames() { @@ -217,7 +234,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } @Override - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> avutil = new ArrayList<String>(); @@ -274,12 +291,12 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } @Override - public boolean useToolGetProcAdressFirst(String funcName) { + public final boolean useToolGetProcAdressFirst(String funcName) { return false; } @Override - public RunnableExecutor getLibLoaderExecutor() { + public final RunnableExecutor getLibLoaderExecutor() { return DynamicLibraryBundle.getDefaultRunnableExecutor(); } diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java index 4be2bcb58..f416bb1f8 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -31,6 +31,8 @@ package jogamp.opengl.util.av.impl; import java.io.IOException; import java.nio.Buffer; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; @@ -43,9 +45,6 @@ import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; import jogamp.opengl.GLContextImpl; -import jogamp.opengl.es1.GLES1ProcAddressTable; -import jogamp.opengl.es2.GLES2ProcAddressTable; -import jogamp.opengl.gl4.GL4bcProcAddressTable; import jogamp.opengl.util.av.EGLMediaPlayerImpl; /*** @@ -194,25 +193,43 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl { System.out.println("setURL: p2 "+this); int tf, tif=GL.GL_RGBA; // texture format and internal format switch(vBytesPerPixelPerPlane) { - case 1: tf = GL2ES2.GL_ALPHA; tif=GL.GL_ALPHA; break; - case 3: tf = GL2ES2.GL_RGB; tif=GL.GL_RGB; break; - case 4: tf = GL2ES2.GL_RGBA; tif=GL.GL_RGBA; break; + case 1: + if( gl.isGL3ES3() ) { + tf = GL2ES2.GL_RED; tif=GL2ES2.GL_RED; // RED is supported on ES3 and >= GL3 [core]; ALPHA is deprecated on core! + } else { + tf = GL2ES2.GL_ALPHA; tif=GL2ES2.GL_ALPHA; // ALPHA is supported on ES2 and GL2 + } + break; + case 3: tf = GL2ES2.GL_RGB; tif=GL.GL_RGB; break; + case 4: tf = GL2ES2.GL_RGBA; tif=GL.GL_RGBA; break; default: throw new RuntimeException("Unsupported bytes-per-pixel / plane "+vBytesPerPixelPerPlane); } setTextureFormat(tif, tf); setTextureType(GL.GL_UNSIGNED_BYTE); - GLContextImpl ctx = (GLContextImpl)gl.getContext(); - ProcAddressTable pt = ctx.getGLProcAddressTable(); - if(pt instanceof GLES2ProcAddressTable) { - procAddrGLTexSubImage2D = ((GLES2ProcAddressTable)pt)._addressof_glTexSubImage2D; - } else if(pt instanceof GLES1ProcAddressTable) { - procAddrGLTexSubImage2D = ((GLES1ProcAddressTable)pt)._addressof_glTexSubImage2D; - } else if(pt instanceof GL4bcProcAddressTable) { - procAddrGLTexSubImage2D = ((GL4bcProcAddressTable)pt)._addressof_glTexSubImage2D; - } else { - throw new InternalError("Unknown ProcAddressTable: "+pt.getClass().getName()+" of "+ctx.getClass().getName()); + final GLContextImpl ctx = (GLContextImpl)gl.getContext(); + final ProcAddressTable pt = ctx.getGLProcAddressTable(); + procAddrGLTexSubImage2D = getAddressFor(pt, "glTexSubImage2D"); + if( 0 == procAddrGLTexSubImage2D ) { + throw new InternalError("glTexSubImage2D n/a in ProcAddressTable: "+pt.getClass().getName()+" of "+ctx.getGLVersion()); } } + + /** + * Catches IllegalArgumentException and returns 0 if functionName is n/a, + * otherwise the ProcAddressTable's field value. + */ + private final long getAddressFor(final ProcAddressTable table, final String functionName) { + return AccessController.doPrivileged(new PrivilegedAction<Long>() { + public Long run() { + try { + return Long.valueOf( table.getAddressFor(functionName) ); + } catch (IllegalArgumentException iae) { + return Long.valueOf(0); + } + } + } ).longValue(); + } + private void updateAttributes2(int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane, int lSz0, int lSz1, int lSz2, int tWd0, int tWd1, int tWd2) { @@ -296,9 +313,9 @@ public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl { " vec2 v_off = vec2("+tc_w_1+", 0.5);\n"+ " vec2 tc_half = texCoord*0.5;\n"+ " float y,u,v,r,g,b;\n"+ - " y = texture2D(image, texCoord).a;\n"+ - " u = texture2D(image, u_off+tc_half).a;\n"+ - " v = texture2D(image, v_off+tc_half).a;\n"+ + " y = texture2D(image, texCoord).r;\n"+ + " u = texture2D(image, u_off+tc_half).r;\n"+ + " v = texture2D(image, v_off+tc_half).r;\n"+ " y = 1.1643*(y-0.0625);\n"+ " u = u-0.5;\n"+ " v = v-0.5;\n"+ diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncPipeline.java b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncPipeline.java index f4f20ac7c..2e924cbfb 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncPipeline.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncPipeline.java @@ -66,7 +66,13 @@ import com.jogamp.opengl.util.glsl.fixedfunc.ShaderSelectionMode; * </p> */ public class FixedFuncPipeline { - protected static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.FixedFuncPipeline", true); + protected static final boolean DEBUG; + + static { + Debug.initSingleton(); + DEBUG = Debug.isPropertyDefined("jogl.debug.FixedFuncPipeline", true); + } + /** The maximum texture units which could be used, depending on {@link ShaderSelectionMode}. */ public static final int MAX_TEXTURE_UNITS = 8; public static final int MAX_LIGHTS = 8; diff --git a/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java b/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java index 251291a14..833771dd1 100644 --- a/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java +++ b/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java @@ -301,13 +301,15 @@ public class JPEGDecoder { private final ArrayHashSet<Integer> compIDs; private final ComponentIn[] comps; private final int compCount; + /** quantization tables */ + final int[][] qtt; int maxCompID; int maxH; int maxV; int mcusPerLine; int mcusPerColumn; - Frame(boolean progressive, int precision, int scanLines, int samplesPerLine, int componentsCount) { + Frame(boolean progressive, int precision, int scanLines, int samplesPerLine, int componentsCount, int[][] qtt) { this.progressive = progressive; this.precision = precision; this.scanLines = scanLines; @@ -315,6 +317,7 @@ public class JPEGDecoder { compIDs = new ArrayHashSet<Integer>(componentsCount); comps = new ComponentIn[componentsCount]; this.compCount = componentsCount; + this.qtt = qtt; } private final void checkBounds(int idx) { @@ -322,6 +325,17 @@ public class JPEGDecoder { throw new CodecException("Idx out of bounds "+idx+", "+this); } } + public final void validateComponents() { + for(int i=0; i<compCount; i++) { + final ComponentIn c = comps[i]; + if( null == c ) { + throw new CodecException("Component["+i+"] null"); + } + if( null == this.qtt[c.qttIdx] ) { + throw new CodecException("Component["+i+"].qttIdx -> null QTT"); + } + } + } public final int getCompCount() { return compCount; } public final int getMaxCompID() { return maxCompID; } @@ -357,7 +371,8 @@ public class JPEGDecoder { /** The JPEG encoded components */ class ComponentIn { final int h, v; - final int[] quantizationTable; + /** index to frame.qtt[] */ + final int qttIdx; int blocksPerColumn; int blocksPerColumnForMcu; int blocksPerLine; @@ -368,10 +383,10 @@ public class JPEGDecoder { BinObj huffmanTableAC; BinObj huffmanTableDC; - ComponentIn(int h, int v, int[] quantizationTable) { + ComponentIn(int h, int v, int qttIdx) { this.h = h; this.v = v; - this.quantizationTable = quantizationTable; + this.qttIdx = qttIdx; } public final void allocateBlocks(int blocksPerColumn, int blocksPerColumnForMcu, int blocksPerLine, int blocksPerLineForMcu) { @@ -389,7 +404,7 @@ public class JPEGDecoder { } public final String toString() { - return "CompIn[h "+h+", v "+v+", blocks["+blocksPerColumn+", mcu "+blocksPerColumnForMcu+"]["+blocksPerLine+", mcu "+blocksPerLineForMcu+"][64]]"; + return "CompIn[h "+h+", v "+v+", qttIdx "+qttIdx+", blocks["+blocksPerColumn+", mcu "+blocksPerColumnForMcu+"]["+blocksPerLine+", mcu "+blocksPerLineForMcu+"][64]]"; } } @@ -526,12 +541,14 @@ public class JPEGDecoder { } public synchronized JPEGDecoder parse(final InputStream inputStream) throws IOException { clear(inputStream); - Frame frame = null; - int resetInterval = 0; - int[][] quantizationTables = new int[0x0F][]; // 4 bits - ArrayList<Frame> frames = new ArrayList<Frame>(); + + final int[][] quantizationTables = new int[0x0F][]; // 4 bits final BinObj[] huffmanTablesAC = new BinObj[0x0F]; // Huffman table spec - 4 bits final BinObj[] huffmanTablesDC = new BinObj[0x0F]; // Huffman table spec - 4 bits + // final ArrayList<Frame> frames = new ArrayList<Frame>(); // JAU: max 1-frame + + Frame frame = null; + int resetInterval = 0; int fileMarker = readUint16(); if ( fileMarker != M_SOI ) { throw new CodecException("SOI not found, but has marker "+toHexString(fileMarker)); @@ -579,6 +596,7 @@ public class JPEGDecoder { while( count < quantizationTablesLength ) { final int quantizationTableSpec = readUint8(); count++; final int precisionID = quantizationTableSpec >> 4; + final int tableIdx = quantizationTableSpec & 0x0F; final int[] tableData = new int[64]; if ( precisionID == 0 ) { // 8 bit values for (int j = 0; j < 64; j++) { @@ -591,12 +609,15 @@ public class JPEGDecoder { tableData[z] = readUint16(); count+=2; } } else { - throw new CodecException("DQT: invalid table precision "+precisionID+", quantizationTableSpec "+quantizationTableSpec); + throw new CodecException("DQT: invalid table precision "+precisionID+", quantizationTableSpec "+quantizationTableSpec+", idx "+tableIdx); } - quantizationTables[quantizationTableSpec & 0x0F] = tableData; + quantizationTables[tableIdx] = tableData; + if( DEBUG ) { + System.err.println("JPEG.parse.QTT["+tableIdx+"]: spec "+quantizationTableSpec+", precision "+precisionID+", data "+count+"/"+quantizationTablesLength); + } } if(count!=quantizationTablesLength){ - throw new CodecException("ERROR: QTT format error [count!=Length]"); + throw new CodecException("ERROR: QTT format error [count!=Length]: "+count+"/"+quantizationTablesLength); } fileMarker = 0; // consumed and get-next } @@ -604,6 +625,9 @@ public class JPEGDecoder { case M_SOF0: case M_SOF2: { + if( null != frame ) { // JAU: max 1-frame + throw new CodecException("only single frame JPEGs supported"); + } int count = 0; final int sofLen = readUint16(); count+=2; // header length; final int componentsCount; @@ -613,7 +637,7 @@ public class JPEGDecoder { final int scanLines = readUint16(); count+=2; final int samplesPerLine = readUint16(); count+=2; componentsCount = readUint8(); count++; - frame = new Frame(progressive, precision, scanLines, samplesPerLine, componentsCount); + frame = new Frame(progressive, precision, scanLines, samplesPerLine, componentsCount, quantizationTables); width = frame.samplesPerLine; height = frame.scanLines; } @@ -622,14 +646,15 @@ public class JPEGDecoder { final int temp = readUint8(); count++; final int h = temp >> 4; final int v = temp & 0x0F; - final int qId = readUint8(); count++; - frame.putOrdered(componentId, new ComponentIn(h, v, quantizationTables[qId])); + final int qttIdx = readUint8(); count++; + final ComponentIn compIn = new ComponentIn(h, v, qttIdx); + frame.putOrdered(componentId, compIn); } if(count!=sofLen){ throw new CodecException("ERROR: SOF format error [count!=Length]"); } prepareComponents(frame); - frames.add(frame); + // frames.add(frame); // JAU: max 1-frame if(DEBUG) { System.err.println("JPG.parse.SOF[02]: Got frame "+frame); } fileMarker = 0; // consumed and get-next } @@ -711,14 +736,21 @@ public class JPEGDecoder { } } if(DEBUG) { System.err.println("JPG.parse.2: End of parsing input "+this); } + /** // JAU: max 1-frame if ( frames.size() != 1 ) { - throw new CodecException("only single frame JPEGs supported"); + throw new CodecException("only single frame JPEGs supported "+this); + } */ + if( null == frame ) { + throw new CodecException("no single frame found in stream "+this); } + frame.validateComponents(); final int compCount = frame.getCompCount(); this.components = new ComponentOut[compCount]; for (int i = 0; i < compCount; i++) { final ComponentIn component = frame.getCompByIndex(i); + // System.err.println("JPG.parse.buildComponentData["+i+"]: "+component); // JAU + // System.err.println("JPG.parse.buildComponentData["+i+"]: "+frame); // JAU this.components[i] = new ComponentOut( output.buildComponentData(frame, component), (float)component.h / (float)frame.maxH, (float)component.v / (float)frame.maxV ); @@ -834,12 +866,14 @@ public class JPEGDecoder { final byte[] r = new byte[64]; for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) { - int scanLine = blockRow << 3; + final int scanLine = blockRow << 3; + // System.err.println("JPG.buildComponentData: row "+blockRow+"/"+blocksPerColumn+" -> scanLine "+scanLine); // JAU for (int i = 0; i < 8; i++) { lines.add(new byte[samplesPerLine]); } for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) { - quantizeAndInverse(component.getBlock(blockRow, blockCol), r, R, component.quantizationTable); + // System.err.println("JPG.buildComponentData: col "+blockCol+"/"+blocksPerLine+", comp.qttIdx "+component.qttIdx+", qtt "+frame.qtt[component.qttIdx]); // JAU + quantizeAndInverse(component.getBlock(blockRow, blockCol), r, R, frame.qtt[component.qttIdx]); final int sample = blockCol << 3; int offset = 0; diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java b/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java index e88a95a33..0fffc85b1 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java @@ -1,5 +1,7 @@ package jogamp.opengl.util.pngj;
+import java.util.HashMap;
+
/**
* Internal PNG predictor filter, or strategy to select it.
*
@@ -26,15 +28,18 @@ public enum FilterType { */
FILTER_PAETH(4),
/**
- * Default strategy: select one of the above filters depending on global image parameters
+ * Default strategy: select one of the above filters depending on global
+ * image parameters
*/
FILTER_DEFAULT(-1),
/**
- * Aggressive strategy: select one of the above filters trying each of the filters (every 8 rows)
+ * Aggressive strategy: select one of the above filters trying each of the
+ * filters (every 8 rows)
*/
FILTER_AGGRESSIVE(-2),
/**
- * Very aggressive strategy: select one of the above filters trying each of the filters (for every row!)
+ * Very aggressive strategy: select one of the above filters trying each of
+ * the filters (for every row!)
*/
FILTER_VERYAGGRESSIVE(-3),
/**
@@ -52,12 +57,17 @@ public enum FilterType { this.val = val;
}
- public static FilterType getByVal(int i) {
+ private static HashMap<Integer, FilterType> byVal;
+
+ static {
+ byVal = new HashMap<Integer, FilterType>();
for (FilterType ft : values()) {
- if (ft.val == i)
- return ft;
+ byVal.put(ft.val, ft);
}
- return null;
+ }
+
+ public static FilterType getByVal(int i) {
+ return byVal.get(i);
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageInfo.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageInfo.java index 26562ef3e..e62134cd5 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ImageInfo.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageInfo.java @@ -3,7 +3,8 @@ package jogamp.opengl.util.pngj; /**
* Simple immutable wrapper for basic image info.
* <p>
- * Some parameters are redundant, but the constructor receives an 'orthogonal' subset.
+ * Some parameters are redundant, but the constructor receives an 'orthogonal'
+ * subset.
* <p>
* ref: http://www.w3.org/TR/PNG/#11IHDR
*/
@@ -23,14 +24,15 @@ public class ImageInfo { public final int rows;
/**
- * Bits per sample (per channel) in the buffer (1-2-4-8-16). This is 8-16 for RGB/ARGB images, 1-2-4-8 for
- * grayscale. For indexed images, number of bits per palette index (1-2-4-8)
+ * Bits per sample (per channel) in the buffer (1-2-4-8-16). This is 8-16
+ * for RGB/ARGB images, 1-2-4-8 for grayscale. For indexed images, number of
+ * bits per palette index (1-2-4-8)
*/
public final int bitDepth;
/**
- * Number of channels, as used internally: 3 for RGB, 4 for RGBA, 2 for GA (gray with alpha), 1 for grayscale or
- * indexed.
+ * Number of channels, as used internally: 3 for RGB, 4 for RGBA, 2 for GA
+ * (gray with alpha), 1 for grayscale or indexed.
*/
public final int channels;
@@ -50,7 +52,8 @@ public class ImageInfo { public final boolean indexed;
/**
- * Flag: true if image internally uses less than one byte per sample (bit depth 1-2-4)
+ * Flag: true if image internally uses less than one byte per sample (bit
+ * depth 1-2-4)
*/
public final boolean packed;
@@ -75,10 +78,12 @@ public class ImageInfo { public final int samplesPerRow;
/**
- * Amount of "packed samples" : when several samples are stored in a single byte (bitdepth 1,2 4) they are counted
- * as one "packed sample". This is less that samplesPerRow only when bitdepth is 1-2-4 (flag packed = true)
+ * Amount of "packed samples" : when several samples are stored in a single
+ * byte (bitdepth 1,2 4) they are counted as one "packed sample". This is
+ * less that samplesPerRow only when bitdepth is 1-2-4 (flag packed = true)
* <p>
- * This equals the number of elements in the scanline array if working with packedMode=true
+ * This equals the number of elements in the scanline array if working with
+ * packedMode=true
* <p>
* For internal use, client code should rarely access this.
*/
@@ -99,7 +104,8 @@ public class ImageInfo { * @param rows
* Height in pixels
* @param bitdepth
- * Bits per sample, in the buffer : 8-16 for RGB true color and greyscale
+ * Bits per sample, in the buffer : 8-16 for RGB true color and
+ * greyscale
* @param alpha
* Flag: has an alpha channel (RGBA or GA)
* @param grayscale
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java index 9f8a13230..e34e6a226 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java @@ -5,7 +5,8 @@ import jogamp.opengl.util.pngj.ImageLineHelper.ImageLineStats; /**
* Lightweight wrapper for an image scanline, used for read and write.
* <p>
- * This object can be (usually it is) reused while iterating over the image lines.
+ * This object can be (usually it is) reused while iterating over the image
+ * lines.
* <p>
* See <code>scanline</code> field, to understand the format.
*/
@@ -18,21 +19,25 @@ public class ImageLine { private int rown = 0;
/**
- * The 'scanline' is an array of integers, corresponds to an image line (row).
+ * The 'scanline' is an array of integers, corresponds to an image line
+ * (row).
* <p>
- * Except for 'packed' formats (gray/indexed with 1-2-4 bitdepth) each <code>int</code> is a "sample" (one for
- * channel), (0-255 or 0-65535) in the corresponding PNG sequence: <code>R G B R G B...</code> or
+ * Except for 'packed' formats (gray/indexed with 1-2-4 bitdepth) each
+ * <code>int</code> is a "sample" (one for channel), (0-255 or 0-65535) in
+ * the corresponding PNG sequence: <code>R G B R G B...</code> or
* <code>R G B A R G B A...</tt>
* or <code>g g g ...</code> or <code>i i i</code> (palette index)
* <p>
- * For bitdepth=1/2/4 , and if samplesUnpacked=false, each value is a PACKED byte!
+ * For bitdepth=1/2/4 , and if samplesUnpacked=false, each value is a PACKED
+ * byte!
* <p>
- * To convert a indexed line to RGB balues, see <code>ImageLineHelper.palIdx2RGB()</code> (you can't do the reverse)
+ * To convert a indexed line to RGB balues, see
+ * <code>ImageLineHelper.palIdx2RGB()</code> (you can't do the reverse)
*/
public final int[] scanline;
/**
- * Same as {@link #scanline}, but with one byte per sample. Only one of scanline and scanlineb is valid - this
- * depends on {@link #sampleType}
+ * Same as {@link #scanline}, but with one byte per sample. Only one of
+ * scanline and scanlineb is valid - this depends on {@link #sampleType}
*/
public final byte[] scanlineb;
@@ -53,10 +58,11 @@ public class ImageLine { public final SampleType sampleType;
/**
- * true: each element of the scanline array represents a sample always, even for internally packed PNG formats
+ * true: each element of the scanline array represents a sample always, even
+ * for internally packed PNG formats
*
- * false: if the original image was of packed type (bit depth less than 8) we keep samples packed in a single array
- * element
+ * false: if the original image was of packed type (bit depth less than 8)
+ * we keep samples packed in a single array element
*/
public final boolean samplesUnpacked;
@@ -70,11 +76,14 @@ public class ImageLine { /**
*
* @param imgInfo
- * Inmutable ImageInfo, basic parameter of the image we are reading or writing
+ * Inmutable ImageInfo, basic parameter of the image we are
+ * reading or writing
* @param stype
- * INT or BYTE : this determines which scanline is the really used one
+ * INT or BYTE : this determines which scanline is the really
+ * used one
* @param unpackedMode
- * If true, we use unpacked format, even for packed original images
+ * If true, we use unpacked format, even for packed original
+ * images
*
*/
public ImageLine(ImageInfo imgInfo, SampleType stype, boolean unpackedMode) {
@@ -226,7 +235,10 @@ public class ImageLine { }
}
- /** size original: samplesPerRow sizeFinal: samplesPerRowPacked (trailing elements are trash!) **/
+ /**
+ * size original: samplesPerRow sizeFinal: samplesPerRowPacked (trailing
+ * elements are trash!)
+ **/
static void packInplaceByte(final ImageInfo iminfo, final byte[] src, final byte[] dst, final boolean scaled) {
final int bitDepth = iminfo.bitDepth;
if (bitDepth >= 8)
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java index 98f235662..91516a704 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java @@ -5,11 +5,14 @@ import jogamp.opengl.util.pngj.chunks.PngChunkPLTE; import jogamp.opengl.util.pngj.chunks.PngChunkTRNS;
/**
- * Bunch of utility static methods to process/analyze an image line at the pixel level.
+ * Bunch of utility static methods to process/analyze an image line at the pixel
+ * level.
* <p>
- * Not essential at all, some methods are probably to be removed if future releases.
+ * Not essential at all, some methods are probably to be removed if future
+ * releases.
* <p>
- * WARNING: most methods for getting/setting values work currently only for integer base imageLines
+ * WARNING: most methods for getting/setting values work currently only for
+ * integer base imageLines
*/
public class ImageLineHelper {
@@ -18,7 +21,8 @@ public class ImageLineHelper { private final static double BIG_VALUE_NEG = Double.MAX_VALUE * (-0.5);
/**
- * Given an indexed line with a palette, unpacks as a RGB array, or RGBA if a non nul PngChunkTRNS chunk is passed
+ * Given an indexed line with a palette, unpacks as a RGB array, or RGBA if
+ * a non nul PngChunkTRNS chunk is passed
*
* @param line
* ImageLine as returned from PngReader
@@ -26,7 +30,8 @@ public class ImageLineHelper { * Palette chunk
* @param buf
* Preallocated array, optional
- * @return R G B (A), one sample 0-255 per array element. Ready for pngw.writeRowInt()
+ * @return R G B (A), one sample 0-255 per array element. Ready for
+ * pngw.writeRowInt()
*/
public static int[] palette2rgb(ImageLine line, PngChunkPLTE pal, PngChunkTRNS trns, int[] buf) {
boolean isalpha = trns != null;
@@ -53,9 +58,12 @@ public class ImageLineHelper { return palette2rgb(line, pal, null, buf);
}
- /** what follows is pretty uninteresting/untested/obsolete, subject to change */
/**
- * Just for basic info or debugging. Shows values for first and last pixel. Does not include alpha
+ * what follows is pretty uninteresting/untested/obsolete, subject to change
+ */
+ /**
+ * Just for basic info or debugging. Shows values for first and last pixel.
+ * Does not include alpha
*/
public static String infoFirstLastPixels(ImageLine line) {
return line.imgInfo.channels == 1 ? String.format("first=(%d) last=(%d)", line.scanline[0],
@@ -71,8 +79,8 @@ public class ImageLineHelper { }
/**
- * Computes some statistics for the line. Not very efficient or elegant, mainly for tests. Only for RGB/RGBA Outputs
- * values as doubles (0.0 - 1.0)
+ * Computes some statistics for the line. Not very efficient or elegant,
+ * mainly for tests. Only for RGB/RGBA Outputs values as doubles (0.0 - 1.0)
*/
static class ImageLineStats {
public double[] prom = { 0.0, 0.0, 0.0, 0.0 }; // channel averages
@@ -237,9 +245,11 @@ public class ImageLineHelper { /**
* Unpacks scanline (for bitdepth 1-2-4) into a array <code>int[]</code>
* <p>
- * You can (OPTIONALLY) pass an preallocated array, that will be filled and returned. If null, it will be allocated
+ * You can (OPTIONALLY) pass an preallocated array, that will be filled and
+ * returned. If null, it will be allocated
* <p>
- * If <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
+ * If
+ * <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
* <p>
* You probably should use {@link ImageLine#unpackToNewImageLine()}
*
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java index 1e0ab746a..feb50e7b6 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java @@ -3,10 +3,12 @@ package jogamp.opengl.util.pngj; import jogamp.opengl.util.pngj.ImageLine.SampleType; /** - * Wraps in a matrix a set of image rows, not necessarily contiguous - but equispaced. + * Wraps in a matrix a set of image rows, not necessarily contiguous - but + * equispaced. * - * The fields mirrors those of {@link ImageLine}, and you can access each row as a ImageLine backed by the matrix row, - * see {@link #getImageLineAtMatrixRow(int)} + * The fields mirrors those of {@link ImageLine}, and you can access each row as + * a ImageLine backed by the matrix row, see + * {@link #getImageLineAtMatrixRow(int)} */ public class ImageLines { @@ -23,7 +25,8 @@ public class ImageLines { public final byte[][] scanlinesb; /** - * Allocates a matrix to store {@code nRows} image rows. See {@link ImageLine} and {@link PngReader#readRowsInt()} + * Allocates a matrix to store {@code nRows} image rows. See + * {@link ImageLine} and {@link PngReader#readRowsInt()} * {@link PngReader#readRowsByte()} * * @param imgInfo @@ -54,8 +57,9 @@ public class ImageLines { } /** - * Warning: this always returns a valid matrix row (clamping on 0 : nrows-1, and rounding down) Eg: - * rowOffset=4,rowStep=2 imageRowToMatrixRow(17) returns 6 , imageRowToMatrixRow(1) returns 0 + * Warning: this always returns a valid matrix row (clamping on 0 : nrows-1, + * and rounding down) Eg: rowOffset=4,rowStep=2 imageRowToMatrixRow(17) + * returns 6 , imageRowToMatrixRow(1) returns 0 */ public int imageRowToMatrixRow(int imrow) { int r = (imrow - rowOffset) / rowStep; @@ -86,9 +90,11 @@ public class ImageLines { * Returns a ImageLine is backed by the matrix, no allocation done * * @param mrow - * Matrix row, from 0 to nRows This is not necessarily the image row, see - * {@link #imageRowToMatrixRow(int)} and {@link #matrixRowToImageRow(int)} - * @return A new ImageLine, backed by the matrix, with the correct ('real') rownumber + * Matrix row, from 0 to nRows This is not necessarily the image + * row, see {@link #imageRowToMatrixRow(int)} and + * {@link #matrixRowToImageRow(int)} + * @return A new ImageLine, backed by the matrix, with the correct ('real') + * rownumber */ public ImageLine getImageLineAtMatrixRow(int mrow) { if (mrow < 0 || mrow > nRows) diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngHelperInternal.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngHelperInternal.java index 63edf8d17..a950c6b33 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngHelperInternal.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngHelperInternal.java @@ -144,18 +144,23 @@ public class PngHelperInternal { }
}
- public static void skipBytes(InputStream is, int len) {
- byte[] buf = new byte[8192 * 4];
- int read, remain = len;
+ public static void skipBytes(InputStream is, long len) {
try {
- while (remain > 0) {
- read = is.read(buf, 0, remain > buf.length ? buf.length : remain);
- if (read < 0)
- throw new PngjInputException("error reading (skipping) : EOF");
- remain -= read;
+ while (len > 0) {
+ long n1 = is.skip(len);
+ if (n1 > 0) {
+ len -= n1;
+ } else if (n1 == 0) { // should we retry? lets read one byte
+ if (is.read() == -1) // EOF
+ break;
+ else
+ len--;
+ } else
+ // negative? this should never happen but...
+ throw new IOException("skip() returned a negative value ???");
}
} catch (IOException e) {
- throw new PngjInputException("error reading (skipping)", e);
+ throw new PngjInputException(e);
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java index 6cc39b0e6..cdad09809 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java @@ -37,7 +37,8 @@ class PngIDatChunkInputStream extends InputStream { List<IdatChunkInfo> foundChunksInfo = new ArrayList<IdatChunkInfo>();
/**
- * Constructor must be called just after reading length and id of first IDAT chunk
+ * Constructor must be called just after reading length and id of first IDAT
+ * chunk
**/
PngIDatChunkInputStream(InputStream iStream, int lenFirstChunk, long offset) {
this.offset = offset;
@@ -95,7 +96,8 @@ class PngIDatChunkInputStream extends InputStream { }
/**
- * sometimes last row read does not fully consumes the chunk here we read the reamaing dummy bytes
+ * sometimes last row read does not fully consumes the chunk here we read
+ * the reamaing dummy bytes
*/
void forceChunkEnd() {
if (!ended) {
@@ -108,7 +110,8 @@ class PngIDatChunkInputStream extends InputStream { }
/**
- * This can return less than len, but never 0 Returns -1 if "pseudo file" ended prematurely. That is our error.
+ * This can return less than len, but never 0 Returns -1 if "pseudo file"
+ * ended prematurely. That is our error.
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java index 8cb4295a5..e42dd8733 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java @@ -1,940 +1,1000 @@ -package jogamp.opengl.util.pngj;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.zip.CRC32;
-import java.util.zip.InflaterInputStream;
-
-import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.ImageLine.SampleType;
-import jogamp.opengl.util.pngj.chunks.ChunkHelper;
-import jogamp.opengl.util.pngj.chunks.ChunkLoadBehaviour;
-import jogamp.opengl.util.pngj.chunks.ChunkRaw;
-import jogamp.opengl.util.pngj.chunks.ChunksList;
-import jogamp.opengl.util.pngj.chunks.PngChunk;
-import jogamp.opengl.util.pngj.chunks.PngChunkIDAT;
-import jogamp.opengl.util.pngj.chunks.PngChunkIHDR;
-import jogamp.opengl.util.pngj.chunks.PngChunkSkipped;
-import jogamp.opengl.util.pngj.chunks.PngMetadata;
-
-/**
- * Reads a PNG image, line by line.
- * <p>
- * The reading sequence is as follows: <br>
- * 1. At construction time, the header and IHDR chunk are read (basic image info) <br>
- * 2. Afterwards you can set some additional global options. Eg. {@link #setUnpackedMode(boolean)},
- * {@link #setCrcCheckDisabled()}.<br>
- * 3. Optional: If you call getMetadata() or getChunksLisk() before start reading the rows, all the chunks before IDAT
- * are automatically loaded and available <br>
- * 4a. The rows are read onen by one of the <tt>readRowXXX</tt> methods: {@link #readRowInt(int)},
- * {@link PngReader#readRowByte(int)}, etc, in order, from 0 to nrows-1 (you can skip or repeat rows, but not go
- * backwards)<br>
- * 4b. Alternatively, you can read all rows, or a subset, in a single call: {@link #readRowsInt()},
- * {@link #readRowsByte()} ,etc. In general this consumes more memory, but for interlaced images this is equally
- * efficient, and more so if reading a small subset of rows.<br>
- * 5. Read of the last row auyomatically loads the trailing chunks, and ends the reader.<br>
- * 6. end() forcibly finishes/aborts the reading and closes the stream
- */
-public class PngReader {
- /**
- * Basic image info - final and inmutable.
- */
- public final ImageInfo imgInfo;
-
- /**
- * not necesarily a filename, can be a description - merely informative
- */
- protected final String filename;
-
- private ChunkLoadBehaviour chunkLoadBehaviour = ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS; // see setter/getter
-
- private boolean shouldCloseStream = true; // true: closes stream after ending - see setter/getter
-
- // some performance/defensive limits
- private long maxTotalBytesRead = 200 * 1024 * 1024; // 200MB
- private int maxBytesMetadata = 5 * 1024 * 1024; // for ancillary chunks - see setter/getter
- private int skipChunkMaxSize = 2 * 1024 * 1024; // chunks exceeding this size will be skipped (nor even CRC checked)
- private String[] skipChunkIds = { "fdAT" }; // chunks with these ids will be skipped (nor even CRC checked)
- private HashSet<String> skipChunkIdsSet; // lazily created from skipChunksById
-
- protected final PngMetadata metadata; // this a wrapper over chunks
- protected final ChunksList chunksList;
-
- protected ImageLine imgLine;
-
- // line as bytes, counting from 1 (index 0 is reserved for filter type)
- protected byte[] rowb = null;
- protected byte[] rowbprev = null; // rowb previous
- protected byte[] rowbfilter = null; // current line 'filtered': exactly as in uncompressed stream
-
- // only set for interlaced PNG
- private final boolean interlaced;
- private final PngDeinterlacer deinterlacer;
-
- private boolean crcEnabled = true;
-
- // this only influences the 1-2-4 bitdepth format
- private boolean unpackedMode = false;
- /**
- * Current chunk group, (0-6) already read or reading
- * <p>
- * see {@link ChunksList}
- */
- protected int currentChunkGroup = -1;
-
- protected int rowNum = -1; // last read row number, starting from 0
- private long offset = 0; // offset in InputStream = bytes read
- private int bytesChunksLoaded; // bytes loaded from anciallary chunks
-
- protected final InputStream inputStream;
- protected InflaterInputStream idatIstream;
- protected PngIDatChunkInputStream iIdatCstream;
-
- protected CRC32 crctest; // If set to non null, it gets a CRC of the unfiltered bytes, to check for images equality
-
- /**
- * Constructs a PngReader from an InputStream.
- * <p>
- * See also <code>FileHelper.createPngReader(File f)</code> if available.
- *
- * Reads only the signature and first chunk (IDHR)
- *
- * @param filenameOrDescription
- * : Optional, can be a filename or a description. Just for error/debug messages
- *
- */
- public PngReader(InputStream inputStream, String filenameOrDescription) {
- this.filename = filenameOrDescription == null ? "" : filenameOrDescription;
- this.inputStream = inputStream;
- this.chunksList = new ChunksList(null);
- this.metadata = new PngMetadata(chunksList);
- // starts reading: signature
- byte[] pngid = new byte[8];
- PngHelperInternal.readBytes(inputStream, pngid, 0, pngid.length);
- offset += pngid.length;
- if (!Arrays.equals(pngid, PngHelperInternal.getPngIdSignature()))
- throw new PngjInputException("Bad PNG signature");
- // reads first chunk
- currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR;
- int clen = PngHelperInternal.readInt4(inputStream);
- offset += 4;
- if (clen != 13)
- throw new PngjInputException("IDHR chunk len != 13 ?? " + clen);
- byte[] chunkid = new byte[4];
- PngHelperInternal.readBytes(inputStream, chunkid, 0, 4);
- if (!Arrays.equals(chunkid, ChunkHelper.b_IHDR))
- throw new PngjInputException("IHDR not found as first chunk??? [" + ChunkHelper.toString(chunkid) + "]");
- offset += 4;
- PngChunkIHDR ihdr = (PngChunkIHDR) readChunk(chunkid, clen, false);
- boolean alpha = (ihdr.getColormodel() & 0x04) != 0;
- boolean palette = (ihdr.getColormodel() & 0x01) != 0;
- boolean grayscale = (ihdr.getColormodel() == 0 || ihdr.getColormodel() == 4);
- // creates ImgInfo and imgLine, and allocates buffers
- imgInfo = new ImageInfo(ihdr.getCols(), ihdr.getRows(), ihdr.getBitspc(), alpha, grayscale, palette);
- // allocation: one extra byte for filter type one pixel
- rowbfilter = new byte[imgInfo.bytesPerRow + 1];
- rowb = new byte[imgInfo.bytesPerRow + 1];
- rowbprev = new byte[rowb.length];
- interlaced = ihdr.getInterlaced() == 1;
- deinterlacer = interlaced ? new PngDeinterlacer(imgInfo) : null;
- // some checks
- if (ihdr.getFilmeth() != 0 || ihdr.getCompmeth() != 0 || (ihdr.getInterlaced() & 0xFFFE) != 0)
- throw new PngjInputException("compression method o filter method or interlaced unrecognized ");
- if (ihdr.getColormodel() < 0 || ihdr.getColormodel() > 6 || ihdr.getColormodel() == 1
- || ihdr.getColormodel() == 5)
- throw new PngjInputException("Invalid colormodel " + ihdr.getColormodel());
- if (ihdr.getBitspc() != 1 && ihdr.getBitspc() != 2 && ihdr.getBitspc() != 4 && ihdr.getBitspc() != 8
- && ihdr.getBitspc() != 16)
- throw new PngjInputException("Invalid bit depth " + ihdr.getBitspc());
- }
-
- private boolean firstChunksNotYetRead() {
- return currentChunkGroup < ChunksList.CHUNK_GROUP_1_AFTERIDHR;
- }
-
- /**
- * Reads last Internally called after having read the last line. It reads extra chunks after IDAT, if present.
- */
- private void readLastAndClose() {
- // offset = iIdatCstream.getOffset();
- if (currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) {
- try {
- idatIstream.close();
- } catch (Exception e) {
- }
- readLastChunks();
- }
- close();
- }
-
- private void close() {
- if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END) { // this could only happen if forced close
- try {
- idatIstream.close();
- } catch (Exception e) {
- }
- currentChunkGroup = ChunksList.CHUNK_GROUP_6_END;
- }
- if (shouldCloseStream) {
- try {
- inputStream.close();
- } catch (Exception e) {
- throw new PngjInputException("error closing input stream!", e);
- }
- }
- }
-
- // nbytes: NOT including the filter byte. leaves result in rowb
- private void unfilterRow(int nbytes) {
- int ftn = rowbfilter[0];
- FilterType ft = FilterType.getByVal(ftn);
- if (ft == null)
- throw new PngjInputException("Filter type " + ftn + " invalid");
- switch (ft) {
- case FILTER_NONE:
- unfilterRowNone(nbytes);
- break;
- case FILTER_SUB:
- unfilterRowSub(nbytes);
- break;
- case FILTER_UP:
- unfilterRowUp(nbytes);
- break;
- case FILTER_AVERAGE:
- unfilterRowAverage(nbytes);
- break;
- case FILTER_PAETH:
- unfilterRowPaeth(nbytes);
- break;
- default:
- throw new PngjInputException("Filter type " + ftn + " not implemented");
- }
- if (crctest != null)
- crctest.update(rowb, 1, rowb.length - 1);
- }
-
- private void unfilterRowAverage(final int nbytes) {
- int i, j, x;
- for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) {
- x = j > 0 ? (rowb[j] & 0xff) : 0;
- rowb[i] = (byte) (rowbfilter[i] + (x + (rowbprev[i] & 0xFF)) / 2);
- }
- }
-
- private void unfilterRowNone(final int nbytes) {
- for (int i = 1; i <= nbytes; i++) {
- rowb[i] = (byte) (rowbfilter[i]);
- }
- }
-
- private void unfilterRowPaeth(final int nbytes) {
- int i, j, x, y;
- for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) {
- x = j > 0 ? (rowb[j] & 0xFF) : 0;
- y = j > 0 ? (rowbprev[j] & 0xFF) : 0;
- rowb[i] = (byte) (rowbfilter[i] + PngHelperInternal.filterPaethPredictor(x, rowbprev[i] & 0xFF, y));
- }
- }
-
- private void unfilterRowSub(final int nbytes) {
- int i, j;
- for (i = 1; i <= imgInfo.bytesPixel; i++) {
- rowb[i] = (byte) (rowbfilter[i]);
- }
- for (j = 1, i = imgInfo.bytesPixel + 1; i <= nbytes; i++, j++) {
- rowb[i] = (byte) (rowbfilter[i] + rowb[j]);
- }
- }
-
- private void unfilterRowUp(final int nbytes) {
- for (int i = 1; i <= nbytes; i++) {
- rowb[i] = (byte) (rowbfilter[i] + rowbprev[i]);
- }
- }
-
- /**
- * Reads chunks before first IDAT. Normally this is called automatically
- * <p>
- * Position before: after IDHR (crc included) Position after: just after the first IDAT chunk id
- * <P>
- * This can be called several times (tentatively), it does nothing if already run
- * <p>
- * (Note: when should this be called? in the constructor? hardly, because we loose the opportunity to call
- * setChunkLoadBehaviour() and perhaps other settings before reading the first row? but sometimes we want to access
- * some metadata (plte, phys) before. Because of this, this method can be called explicitly but is also called
- * implicititly in some methods (getMetatada(), getChunksList())
- */
- private final void readFirstChunks() {
- if (!firstChunksNotYetRead())
- return;
- int clen = 0;
- boolean found = false;
- byte[] chunkid = new byte[4]; // it's important to reallocate in each iteration
- currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR;
- while (!found) {
- clen = PngHelperInternal.readInt4(inputStream);
- offset += 4;
- if (clen < 0)
- break;
- PngHelperInternal.readBytes(inputStream, chunkid, 0, 4);
- offset += 4;
- if (Arrays.equals(chunkid, ChunkHelper.b_IDAT)) {
- found = true;
- currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT;
- // add dummy idat chunk to list
- chunksList.appendReadChunk(new PngChunkIDAT(imgInfo, clen, offset - 8), currentChunkGroup);
- break;
- } else if (Arrays.equals(chunkid, ChunkHelper.b_IEND)) {
- throw new PngjInputException("END chunk found before image data (IDAT) at offset=" + offset);
- }
- if (Arrays.equals(chunkid, ChunkHelper.b_PLTE))
- currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE;
- readChunk(chunkid, clen, false);
- if (Arrays.equals(chunkid, ChunkHelper.b_PLTE))
- currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE;
- }
- int idatLen = found ? clen : -1;
- if (idatLen < 0)
- throw new PngjInputException("first idat chunk not found!");
- iIdatCstream = new PngIDatChunkInputStream(inputStream, idatLen, offset);
- idatIstream = new InflaterInputStream(iIdatCstream);
- if (!crcEnabled)
- iIdatCstream.disableCrcCheck();
- }
-
- /**
- * Reads (and processes) chunks after last IDAT.
- **/
- void readLastChunks() {
- // PngHelper.logdebug("idat ended? " + iIdatCstream.isEnded());
- currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT;
- if (!iIdatCstream.isEnded())
- iIdatCstream.forceChunkEnd();
- int clen = iIdatCstream.getLenLastChunk();
- byte[] chunkid = iIdatCstream.getIdLastChunk();
- boolean endfound = false;
- boolean first = true;
- boolean skip = false;
- while (!endfound) {
- skip = false;
- if (!first) {
- clen = PngHelperInternal.readInt4(inputStream);
- offset += 4;
- if (clen < 0)
- throw new PngjInputException("bad chuck len " + clen);
- PngHelperInternal.readBytes(inputStream, chunkid, 0, 4);
- offset += 4;
- }
- first = false;
- if (Arrays.equals(chunkid, ChunkHelper.b_IDAT)) {
- skip = true; // extra dummy (empty?) idat chunk, it can happen, ignore it
- } else if (Arrays.equals(chunkid, ChunkHelper.b_IEND)) {
- currentChunkGroup = ChunksList.CHUNK_GROUP_6_END;
- endfound = true;
- }
- readChunk(chunkid, clen, skip);
- }
- if (!endfound)
- throw new PngjInputException("end chunk not found - offset=" + offset);
- // PngHelper.logdebug("end chunk found ok offset=" + offset);
- }
-
- /**
- * Reads chunkd from input stream, adds to ChunksList, and returns it. If it's skipped, a PngChunkSkipped object is
- * created
- */
- private PngChunk readChunk(byte[] chunkid, int clen, boolean skipforced) {
- if (clen < 0)
- throw new PngjInputException("invalid chunk lenght: " + clen);
- // skipChunksByIdSet is created lazyly, if fist IHDR has already been read
- if (skipChunkIdsSet == null && currentChunkGroup > ChunksList.CHUNK_GROUP_0_IDHR)
- skipChunkIdsSet = new HashSet<String>(Arrays.asList(skipChunkIds));
- String chunkidstr = ChunkHelper.toString(chunkid);
- boolean critical = ChunkHelper.isCritical(chunkidstr);
- PngChunk pngChunk = null;
- boolean skip = skipforced;
- if (maxTotalBytesRead > 0 && clen + offset > maxTotalBytesRead)
- throw new PngjInputException("Maximum total bytes to read exceeeded: " + maxTotalBytesRead + " offset:"
- + offset + " clen=" + clen);
- // an ancillary chunks can be skipped because of several reasons:
- if (currentChunkGroup > ChunksList.CHUNK_GROUP_0_IDHR && !critical)
- skip = skip || (skipChunkMaxSize > 0 && clen >= skipChunkMaxSize) || skipChunkIdsSet.contains(chunkidstr)
- || (maxBytesMetadata > 0 && clen > maxBytesMetadata - bytesChunksLoaded)
- || !ChunkHelper.shouldLoad(chunkidstr, chunkLoadBehaviour);
- if (skip) {
- PngHelperInternal.skipBytes(inputStream, clen);
- PngHelperInternal.readInt4(inputStream); // skip - we dont call PngHelperInternal.skipBytes(inputStream,
- // clen + 4) for risk of overflow
- pngChunk = new PngChunkSkipped(chunkidstr, imgInfo, clen);
- } else {
- ChunkRaw chunk = new ChunkRaw(clen, chunkid, true);
- chunk.readChunkData(inputStream, crcEnabled || critical);
- pngChunk = PngChunk.factory(chunk, imgInfo);
- if (!pngChunk.crit)
- bytesChunksLoaded += chunk.len;
- }
- pngChunk.setOffset(offset - 8L);
- chunksList.appendReadChunk(pngChunk, currentChunkGroup);
- offset += clen + 4L;
- return pngChunk;
- }
-
- /**
- * Logs/prints a warning.
- * <p>
- * The default behaviour is print to stderr, but it can be overriden.
- * <p>
- * This happens rarely - most errors are fatal.
- */
- protected void logWarn(String warn) {
- System.err.println(warn);
- }
-
- /**
- * @see #setChunkLoadBehaviour(ChunkLoadBehaviour)
- */
- public ChunkLoadBehaviour getChunkLoadBehaviour() {
- return chunkLoadBehaviour;
- }
-
- /**
- * Determines which ancillary chunks (metada) are to be loaded
- *
- * @param chunkLoadBehaviour
- * {@link ChunkLoadBehaviour}
- */
- public void setChunkLoadBehaviour(ChunkLoadBehaviour chunkLoadBehaviour) {
- this.chunkLoadBehaviour = chunkLoadBehaviour;
- }
-
- /**
- * All loaded chunks (metada). If we have not yet end reading the image, this will include only the chunks before
- * the pixels data (IDAT)
- * <p>
- * Critical chunks are included, except that all IDAT chunks appearance are replaced by a single dummy-marker IDAT
- * chunk. These might be copied to the PngWriter
- * <p>
- *
- * @see #getMetadata()
- */
- public ChunksList getChunksList() {
- if (firstChunksNotYetRead())
- readFirstChunks();
- return chunksList;
- }
-
- int getCurrentChunkGroup() {
- return currentChunkGroup;
- }
-
- /**
- * High level wrapper over chunksList
- *
- * @see #getChunksList()
- */
- public PngMetadata getMetadata() {
- if (firstChunksNotYetRead())
- readFirstChunks();
- return metadata;
- }
-
- /**
- * If called for first time, calls readRowInt. Elsewhere, it calls the appropiate readRowInt/readRowByte
- * <p>
- * In general, specifying the concrete readRowInt/readRowByte is preferrable
- *
- * @see #readRowInt(int) {@link #readRowByte(int)}
- */
- public ImageLine readRow(int nrow) {
- if (imgLine == null)
- imgLine = new ImageLine(imgInfo, SampleType.INT, unpackedMode);
- return imgLine.sampleType != SampleType.BYTE ? readRowInt(nrow) : readRowByte(nrow);
- }
-
- /**
- * Reads the row as INT, storing it in the {@link #imgLine} property and returning it.
- *
- * The row must be greater or equal than the last read row.
- *
- * @param nrow
- * Row number, from 0 to rows-1. Increasing order.
- * @return ImageLine object, also available as field. Data is in {@link ImageLine#scanline} (int) field.
- */
- public ImageLine readRowInt(int nrow) {
- if (imgLine == null)
- imgLine = new ImageLine(imgInfo, SampleType.INT, unpackedMode);
- if (imgLine.getRown() == nrow) // already read
- return imgLine;
- readRowInt(imgLine.scanline, nrow);
- imgLine.setFilterUsed(FilterType.getByVal(rowbfilter[0]));
- imgLine.setRown(nrow);
- return imgLine;
- }
-
- /**
- * Reads the row as BYTES, storing it in the {@link #imgLine} property and returning it.
- *
- * The row must be greater or equal than the last read row. This method allows to pass the same row that was last
- * read.
- *
- * @param nrow
- * Row number, from 0 to rows-1. Increasing order.
- * @return ImageLine object, also available as field. Data is in {@link ImageLine#scanlineb} (byte) field.
- */
- public ImageLine readRowByte(int nrow) {
- if (imgLine == null)
- imgLine = new ImageLine(imgInfo, SampleType.BYTE, unpackedMode);
- if (imgLine.getRown() == nrow) // already read
- return imgLine;
- readRowByte(imgLine.scanlineb, nrow);
- imgLine.setFilterUsed(FilterType.getByVal(rowbfilter[0]));
- imgLine.setRown(nrow);
- return imgLine;
- }
-
- /**
- * @see #readRowInt(int[], int)
- */
- public final int[] readRow(int[] buffer, final int nrow) {
- return readRowInt(buffer, nrow);
- }
-
- /**
- * Reads a line and returns it as a int[] array.
- * <p>
- * You can pass (optionally) a prealocatted buffer.
- * <p>
- * If the bitdepth is less than 8, the bytes are packed - unless {@link #unpackedMode} is true.
- *
- * @param buffer
- * Prealocated buffer, or null.
- * @param nrow
- * Row number (0 is top). Most be strictly greater than the last read row.
- *
- * @return The scanline in the same passwd buffer if it was allocated, a newly allocated one otherwise
- */
- public final int[] readRowInt(int[] buffer, final int nrow) {
- if (buffer == null)
- buffer = new int[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked];
- if (!interlaced) {
- if (nrow <= rowNum)
- throw new PngjInputException("rows must be read in increasing order: " + nrow);
- int bytesread = 0;
- while (rowNum < nrow)
- bytesread = readRowRaw(rowNum + 1); // read rows, perhaps skipping if necessary
- decodeLastReadRowToInt(buffer, bytesread);
- } else { // interlaced
- if (deinterlacer.getImageInt() == null)
- deinterlacer.setImageInt(readRowsInt().scanlines); // read all image and store it in deinterlacer
- System.arraycopy(deinterlacer.getImageInt()[nrow], 0, buffer, 0, unpackedMode ? imgInfo.samplesPerRow
- : imgInfo.samplesPerRowPacked);
- }
- return buffer;
- }
-
- /**
- * Reads a line and returns it as a byte[] array.
- * <p>
- * You can pass (optionally) a prealocatted buffer.
- * <p>
- * If the bitdepth is less than 8, the bytes are packed - unless {@link #unpackedMode} is true. <br>
- * If the bitdepth is 16, the least significant byte is lost.
- * <p>
- *
- * @param buffer
- * Prealocated buffer, or null.
- * @param nrow
- * Row number (0 is top). Most be strictly greater than the last read row.
- *
- * @return The scanline in the same passwd buffer if it was allocated, a newly allocated one otherwise
- */
- public final byte[] readRowByte(byte[] buffer, final int nrow) {
- if (buffer == null)
- buffer = new byte[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked];
- if (!interlaced) {
- if (nrow <= rowNum)
- throw new PngjInputException("rows must be read in increasing order: " + nrow);
- int bytesread = 0;
- while (rowNum < nrow)
- bytesread = readRowRaw(rowNum + 1); // read rows, perhaps skipping if necessary
- decodeLastReadRowToByte(buffer, bytesread);
- } else { // interlaced
- if (deinterlacer.getImageByte() == null)
- deinterlacer.setImageByte(readRowsByte().scanlinesb); // read all image and store it in deinterlacer
- System.arraycopy(deinterlacer.getImageByte()[nrow], 0, buffer, 0, unpackedMode ? imgInfo.samplesPerRow
- : imgInfo.samplesPerRowPacked);
- }
- return buffer;
- }
-
- /**
- * @param nrow
- * @deprecated Now {@link #readRow(int)} implements the same funcion. This method will be removed in future releases
- */
- public ImageLine getRow(int nrow) {
- return readRow(nrow);
- }
-
- private void decodeLastReadRowToInt(int[] buffer, int bytesRead) {
- if (imgInfo.bitDepth <= 8)
- for (int i = 0, j = 1; i < bytesRead; i++)
- buffer[i] = (rowb[j++] & 0xFF); // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html
- else
- for (int i = 0, j = 1; j <= bytesRead; i++)
- buffer[i] = ((rowb[j++] & 0xFF) << 8) + (rowb[j++] & 0xFF); // 16 bitspc
- if (imgInfo.packed && unpackedMode)
- ImageLine.unpackInplaceInt(imgInfo, buffer, buffer, false);
- }
-
- private void decodeLastReadRowToByte(byte[] buffer, int bytesRead) {
- if (imgInfo.bitDepth <= 8)
- System.arraycopy(rowb, 1, buffer, 0, bytesRead);
- else
- for (int i = 0, j = 1; j < bytesRead; i++, j += 2)
- buffer[i] = rowb[j];// 16 bits in 1 byte: this discards the LSB!!!
- if (imgInfo.packed && unpackedMode)
- ImageLine.unpackInplaceByte(imgInfo, buffer, buffer, false);
- }
-
- /**
- * Reads a set of lines and returns it as a ImageLines object, which wraps matrix. Internally it reads all lines,
- * but decodes and stores only the wanted ones. This starts and ends the reading, and cannot be combined with other
- * reading methods.
- * <p>
- * This it's more efficient (speed an memory) that doing calling readRowInt() for each desired line only if the
- * image is interlaced.
- * <p>
- * Notice that the columns in the matrix is not the pixel width of the image, but rather pixels x channels
- *
- * @see #readRowInt(int) to read about the format of each row
- *
- * @param rowOffset
- * Number of rows to be skipped
- * @param nRows
- * Total number of rows to be read. -1: read all available
- * @param rowStep
- * Row increment. If 1, we read consecutive lines; if 2, we read even/odd lines, etc
- * @return Set of lines as a ImageLines, which wraps a matrix
- */
- public ImageLines readRowsInt(int rowOffset, int nRows, int rowStep) {
- if (nRows < 0)
- nRows = (imgInfo.rows - rowOffset) / rowStep;
- if (rowStep < 1 || rowOffset < 0 || nRows * rowStep + rowOffset > imgInfo.rows)
- throw new PngjInputException("bad args");
- ImageLines imlines = new ImageLines(imgInfo, SampleType.INT, unpackedMode, rowOffset, nRows, rowStep);
- if (!interlaced) {
- for (int j = 0; j < imgInfo.rows; j++) {
- int bytesread = readRowRaw(j); // read and perhaps discards
- int mrow = imlines.imageRowToMatrixRowStrict(j);
- if (mrow >= 0)
- decodeLastReadRowToInt(imlines.scanlines[mrow], bytesread);
- }
- } else { // and now, for something completely different (interlaced)
- int[] buf = new int[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked];
- for (int p = 1; p <= 7; p++) {
- deinterlacer.setPass(p);
- for (int i = 0; i < deinterlacer.getRows(); i++) {
- int bytesread = readRowRaw(i);
- int j = deinterlacer.getCurrRowReal();
- int mrow = imlines.imageRowToMatrixRowStrict(j);
- if (mrow >= 0) {
- decodeLastReadRowToInt(buf, bytesread);
- deinterlacer.deinterlaceInt(buf, imlines.scanlines[mrow], !unpackedMode);
- }
- }
- }
- }
- end();
- return imlines;
- }
-
- /**
- * Same as readRowsInt(0, imgInfo.rows, 1)
- *
- * @see #readRowsInt(int, int, int)
- */
- public ImageLines readRowsInt() {
- return readRowsInt(0, imgInfo.rows, 1);
- }
-
- /**
- * Reads a set of lines and returns it as a ImageLines object, which wrapas a byte[][] matrix. Internally it reads
- * all lines, but decodes and stores only the wanted ones. This starts and ends the reading, and cannot be combined
- * with other reading methods.
- * <p>
- * This it's more efficient (speed an memory) that doing calling readRowByte() for each desired line only if the
- * image is interlaced.
- * <p>
- * Notice that the columns in the matrix is not the pixel width of the image, but rather pixels x channels
- *
- * @see #readRowByte(int) to read about the format of each row. Notice that if the bitdepth is 16 this will lose
- * information
- *
- * @param rowOffset
- * Number of rows to be skipped
- * @param nRows
- * Total number of rows to be read. -1: read all available
- * @param rowStep
- * Row increment. If 1, we read consecutive lines; if 2, we read even/odd lines, etc
- * @return Set of lines as a matrix
- */
- public ImageLines readRowsByte(int rowOffset, int nRows, int rowStep) {
- if (nRows < 0)
- nRows = (imgInfo.rows - rowOffset) / rowStep;
- if (rowStep < 1 || rowOffset < 0 || nRows * rowStep + rowOffset > imgInfo.rows)
- throw new PngjInputException("bad args");
- ImageLines imlines = new ImageLines(imgInfo, SampleType.BYTE, unpackedMode, rowOffset, nRows, rowStep);
- if (!interlaced) {
- for (int j = 0; j < imgInfo.rows; j++) {
- int bytesread = readRowRaw(j); // read and perhaps discards
- int mrow = imlines.imageRowToMatrixRowStrict(j);
- if (mrow >= 0)
- decodeLastReadRowToByte(imlines.scanlinesb[mrow], bytesread);
- }
- } else { // and now, for something completely different (interlaced)
- byte[] buf = new byte[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked];
- for (int p = 1; p <= 7; p++) {
- deinterlacer.setPass(p);
- for (int i = 0; i < deinterlacer.getRows(); i++) {
- int bytesread = readRowRaw(i);
- int j = deinterlacer.getCurrRowReal();
- int mrow = imlines.imageRowToMatrixRowStrict(j);
- if (mrow >= 0) {
- decodeLastReadRowToByte(buf, bytesread);
- deinterlacer.deinterlaceByte(buf, imlines.scanlinesb[mrow], !unpackedMode);
- }
- }
- }
- }
- end();
- return imlines;
- }
-
- /**
- * Same as readRowsByte(0, imgInfo.rows, 1)
- *
- * @see #readRowsByte(int, int, int)
- */
- public ImageLines readRowsByte() {
- return readRowsByte(0, imgInfo.rows, 1);
- }
-
- /*
- * For the interlaced case, nrow indicates the subsampled image - the pass must be set already.
- *
- * This must be called in strict order, both for interlaced or no interlaced.
- *
- * Updates rowNum.
- *
- * Leaves raw result in rowb
- *
- * Returns bytes actually read (not including the filter byte)
- */
- private int readRowRaw(final int nrow) {
- //
- if (nrow == 0 && firstChunksNotYetRead())
- readFirstChunks();
- if (nrow == 0 && interlaced)
- Arrays.fill(rowb, (byte) 0); // new subimage: reset filters: this is enough, see the swap that happens lines
- // below
- int bytesRead = imgInfo.bytesPerRow; // NOT including the filter byte
- if (interlaced) {
- if (nrow < 0 || nrow > deinterlacer.getRows() || (nrow != 0 && nrow != deinterlacer.getCurrRowSubimg() + 1))
- throw new PngjInputException("invalid row in interlaced mode: " + nrow);
- deinterlacer.setRow(nrow);
- bytesRead = (imgInfo.bitspPixel * deinterlacer.getPixelsToRead() + 7) / 8;
- if (bytesRead < 1)
- throw new PngjExceptionInternal("wtf??");
- } else { // check for non interlaced
- if (nrow < 0 || nrow >= imgInfo.rows || nrow != rowNum + 1)
- throw new PngjInputException("invalid row: " + nrow);
- }
- rowNum = nrow;
- // swap buffers
- byte[] tmp = rowb;
- rowb = rowbprev;
- rowbprev = tmp;
- // loads in rowbfilter "raw" bytes, with filter
- PngHelperInternal.readBytes(idatIstream, rowbfilter, 0, bytesRead + 1);
- offset = iIdatCstream.getOffset();
- if (offset < 0)
- throw new PngjExceptionInternal("bad offset ??" + offset);
- if (maxTotalBytesRead > 0 && offset >= maxTotalBytesRead)
- throw new PngjInputException("Reading IDAT: Maximum total bytes to read exceeeded: " + maxTotalBytesRead
- + " offset:" + offset);
- rowb[0] = 0;
- unfilterRow(bytesRead);
- rowb[0] = rowbfilter[0];
- if ((rowNum == imgInfo.rows - 1 && !interlaced) || (interlaced && deinterlacer.isAtLastRow()))
- readLastAndClose();
- return bytesRead;
- }
-
- /**
- * Reads all the (remaining) file, skipping the pixels data. This is much more efficient that calling readRow(),
- * specially for big files (about 10 times faster!), because it doesn't even decompress the IDAT stream and disables
- * CRC check Use this if you are not interested in reading pixels,only metadata.
- */
- public void readSkippingAllRows() {
- if (firstChunksNotYetRead())
- readFirstChunks();
- // we read directly from the compressed stream, we dont decompress nor chec CRC
- iIdatCstream.disableCrcCheck();
- try {
- int r;
- do {
- r = iIdatCstream.read(rowbfilter, 0, rowbfilter.length);
- } while (r >= 0);
- } catch (IOException e) {
- throw new PngjInputException("error in raw read of IDAT", e);
- }
- offset = iIdatCstream.getOffset();
- if (offset < 0)
- throw new PngjExceptionInternal("bad offset ??" + offset);
- if (maxTotalBytesRead > 0 && offset >= maxTotalBytesRead)
- throw new PngjInputException("Reading IDAT: Maximum total bytes to read exceeeded: " + maxTotalBytesRead
- + " offset:" + offset);
- readLastAndClose();
- }
-
- /**
- * Set total maximum bytes to read (0: unlimited; default: 200MB). <br>
- * These are the bytes read (not loaded) in the input stream. If exceeded, an exception will be thrown.
- */
- public void setMaxTotalBytesRead(long maxTotalBytesToRead) {
- this.maxTotalBytesRead = maxTotalBytesToRead;
- }
-
- /**
- * @return Total maximum bytes to read.
- */
- public long getMaxTotalBytesRead() {
- return maxTotalBytesRead;
- }
-
- /**
- * Set total maximum bytes to load from ancillary chunks (0: unlimited; default: 5Mb).<br>
- * If exceeded, some chunks will be skipped
- */
- public void setMaxBytesMetadata(int maxBytesChunksToLoad) {
- this.maxBytesMetadata = maxBytesChunksToLoad;
- }
-
- /**
- * @return Total maximum bytes to load from ancillary ckunks.
- */
- public int getMaxBytesMetadata() {
- return maxBytesMetadata;
- }
-
- /**
- * Set maximum size in bytes for individual ancillary chunks (0: unlimited; default: 2MB). <br>
- * Chunks exceeding this length will be skipped (the CRC will not be checked) and the chunk will be saved as a
- * PngChunkSkipped object. See also setSkipChunkIds
- */
- public void setSkipChunkMaxSize(int skipChunksBySize) {
- this.skipChunkMaxSize = skipChunksBySize;
- }
-
- /**
- * @return maximum size in bytes for individual ancillary chunks.
- */
- public int getSkipChunkMaxSize() {
- return skipChunkMaxSize;
- }
-
- /**
- * Chunks ids to be skipped. <br>
- * These chunks will be skipped (the CRC will not be checked) and the chunk will be saved as a PngChunkSkipped
- * object. See also setSkipChunkMaxSize
- */
- public void setSkipChunkIds(String[] skipChunksById) {
- this.skipChunkIds = skipChunksById == null ? new String[] {} : skipChunksById;
- }
-
- /**
- * @return Chunk-IDs to be skipped.
- */
- public String[] getSkipChunkIds() {
- return skipChunkIds;
- }
-
- /**
- * if true, input stream will be closed after ending read
- * <p>
- * default=true
- */
- public void setShouldCloseStream(boolean shouldCloseStream) {
- this.shouldCloseStream = shouldCloseStream;
- }
-
- /**
- * Normally this does nothing, but it can be used to force a premature closing. Its recommended practice to call it
- * after reading the image pixels.
- */
- public void end() {
- if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END)
- close();
- }
-
- /**
- * Interlaced PNG is accepted -though not welcomed- now...
- */
- public boolean isInterlaced() {
- return interlaced;
- }
-
- /**
- * set/unset "unpackedMode"<br>
- * If false (default) packed types (bitdepth=1,2 or 4) will keep several samples packed in one element (byte or int) <br>
- * If true, samples will be unpacked on reading, and each element in the scanline will be sample. This implies more
- * processing and memory, but it's the most efficient option if you intend to read individual pixels. <br>
- * This option should only be set before start reading.
- *
- * @param unPackedMode
- */
- public void setUnpackedMode(boolean unPackedMode) {
- this.unpackedMode = unPackedMode;
- }
-
- /**
- * @see PngReader#setUnpackedMode(boolean)
- */
- public boolean isUnpackedMode() {
- return unpackedMode;
- }
-
- /**
- * Disables the CRC integrity check in IDAT chunks and ancillary chunks, this gives a slight increase in reading
- * speed for big files
- */
- public void setCrcCheckDisabled() {
- crcEnabled = false;
- }
-
- /**
- * Just for testing. TO be called after ending reading, only if initCrctest() was called before start
- *
- * @return CRC of the raw pixels values
- */
- long getCrctestVal() {
- return crctest.getValue();
- }
-
- /**
- * Inits CRC object and enables CRC calculation
- */
- void initCrctest() {
- this.crctest = new CRC32();
- }
-
- /**
- * Basic info, for debugging.
- */
- public String toString() { // basic info
- return "filename=" + filename + " " + imgInfo.toString();
- }
-
-}
+package jogamp.opengl.util.pngj; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.zip.CRC32; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +import jogamp.opengl.util.pngj.ImageLine.SampleType; +import jogamp.opengl.util.pngj.chunks.ChunkHelper; +import jogamp.opengl.util.pngj.chunks.ChunkLoadBehaviour; +import jogamp.opengl.util.pngj.chunks.ChunkRaw; +import jogamp.opengl.util.pngj.chunks.ChunksList; +import jogamp.opengl.util.pngj.chunks.PngChunk; +import jogamp.opengl.util.pngj.chunks.PngChunkIDAT; +import jogamp.opengl.util.pngj.chunks.PngChunkIHDR; +import jogamp.opengl.util.pngj.chunks.PngChunkSkipped; +import jogamp.opengl.util.pngj.chunks.PngMetadata; + +/** + * Reads a PNG image, line by line. + * <p> + * The reading sequence is as follows: <br> + * 1. At construction time, the header and IHDR chunk are read (basic image + * info) <br> + * 2. Afterwards you can set some additional global options. Eg. + * {@link #setUnpackedMode(boolean)}, {@link #setCrcCheckDisabled()}.<br> + * 3. Optional: If you call getMetadata() or getChunksLisk() before start + * reading the rows, all the chunks before IDAT are automatically loaded and + * available <br> + * 4a. The rows are read onen by one of the <tt>readRowXXX</tt> methods: + * {@link #readRowInt(int)}, {@link PngReader#readRowByte(int)}, etc, in order, + * from 0 to nrows-1 (you can skip or repeat rows, but not go backwards)<br> + * 4b. Alternatively, you can read all rows, or a subset, in a single call: + * {@link #readRowsInt()}, {@link #readRowsByte()} ,etc. In general this + * consumes more memory, but for interlaced images this is equally efficient, + * and more so if reading a small subset of rows.<br> + * 5. Read of the last row auyomatically loads the trailing chunks, and ends the + * reader.<br> + * 6. end() forcibly finishes/aborts the reading and closes the stream + */ +public class PngReader { + + /** + * Basic image info - final and inmutable. + */ + public final ImageInfo imgInfo; + /** + * not necesarily a filename, can be a description - merely informative + */ + protected final String filename; + private ChunkLoadBehaviour chunkLoadBehaviour = ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS; // see setter/getter + private boolean shouldCloseStream = true; // true: closes stream after ending - see setter/getter + // some performance/defensive limits + private long maxTotalBytesRead = 200 * 1024 * 1024; // 200MB + private int maxBytesMetadata = 5 * 1024 * 1024; // for ancillary chunks - see setter/getter + private int skipChunkMaxSize = 2 * 1024 * 1024; // chunks exceeding this size will be skipped (nor even CRC checked) + private String[] skipChunkIds = { "fdAT" }; // chunks with these ids will be skipped (nor even CRC checked) + private HashSet<String> skipChunkIdsSet; // lazily created from skipChunksById + protected final PngMetadata metadata; // this a wrapper over chunks + protected final ChunksList chunksList; + protected ImageLine imgLine; + // line as bytes, counting from 1 (index 0 is reserved for filter type) + protected final int buffersLen; // nominal length is imgInfo.bytesPerRow + 1 but it can be larger + protected byte[] rowb = null; + protected byte[] rowbprev = null; // rowb previous + protected byte[] rowbfilter = null; // current line 'filtered': exactly as in uncompressed stream + // only set for interlaced PNG + private final boolean interlaced; + private final PngDeinterlacer deinterlacer; + private boolean crcEnabled = true; + // this only influences the 1-2-4 bitdepth format + private boolean unpackedMode = false; + private Inflater inflater = null; // can be reused among several objects. see reuseBuffersFrom() + /** + * Current chunk group, (0-6) already read or reading + * <p> + * see {@link ChunksList} + */ + protected int currentChunkGroup = -1; + protected int rowNum = -1; // last read row number, starting from 0 + private long offset = 0; // offset in InputStream = bytes read + private int bytesChunksLoaded; // bytes loaded from anciallary chunks + protected final InputStream inputStream; + protected InflaterInputStream idatIstream; + protected PngIDatChunkInputStream iIdatCstream; + protected CRC32 crctest; // If set to non null, it gets a CRC of the unfiltered bytes, to check for images equality + + /** + * Constructs a PngReader from an InputStream. + * <p> + * See also <code>FileHelper.createPngReader(File f)</code> if available. + * + * Reads only the signature and first chunk (IDHR) + * + * @param filenameOrDescription + * : Optional, can be a filename or a description. Just for + * error/debug messages + * + */ + public PngReader(InputStream inputStream, String filenameOrDescription) { + this.filename = filenameOrDescription == null ? "" : filenameOrDescription; + this.inputStream = inputStream; + this.chunksList = new ChunksList(null); + this.metadata = new PngMetadata(chunksList); + // starts reading: signature + byte[] pngid = new byte[8]; + PngHelperInternal.readBytes(inputStream, pngid, 0, pngid.length); + offset += pngid.length; + if (!Arrays.equals(pngid, PngHelperInternal.getPngIdSignature())) + throw new PngjInputException("Bad PNG signature"); + // reads first chunk + currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; + int clen = PngHelperInternal.readInt4(inputStream); + offset += 4; + if (clen != 13) + throw new PngjInputException("IDHR chunk len != 13 ?? " + clen); + byte[] chunkid = new byte[4]; + PngHelperInternal.readBytes(inputStream, chunkid, 0, 4); + if (!Arrays.equals(chunkid, ChunkHelper.b_IHDR)) + throw new PngjInputException("IHDR not found as first chunk??? [" + ChunkHelper.toString(chunkid) + "]"); + offset += 4; + PngChunkIHDR ihdr = (PngChunkIHDR) readChunk(chunkid, clen, false); + boolean alpha = (ihdr.getColormodel() & 0x04) != 0; + boolean palette = (ihdr.getColormodel() & 0x01) != 0; + boolean grayscale = (ihdr.getColormodel() == 0 || ihdr.getColormodel() == 4); + // creates ImgInfo and imgLine, and allocates buffers + imgInfo = new ImageInfo(ihdr.getCols(), ihdr.getRows(), ihdr.getBitspc(), alpha, grayscale, palette); + interlaced = ihdr.getInterlaced() == 1; + deinterlacer = interlaced ? new PngDeinterlacer(imgInfo) : null; + buffersLen = imgInfo.bytesPerRow + 1; + // some checks + if (ihdr.getFilmeth() != 0 || ihdr.getCompmeth() != 0 || (ihdr.getInterlaced() & 0xFFFE) != 0) + throw new PngjInputException("compression method o filter method or interlaced unrecognized "); + if (ihdr.getColormodel() < 0 || ihdr.getColormodel() > 6 || ihdr.getColormodel() == 1 + || ihdr.getColormodel() == 5) + throw new PngjInputException("Invalid colormodel " + ihdr.getColormodel()); + if (ihdr.getBitspc() != 1 && ihdr.getBitspc() != 2 && ihdr.getBitspc() != 4 && ihdr.getBitspc() != 8 + && ihdr.getBitspc() != 16) + throw new PngjInputException("Invalid bit depth " + ihdr.getBitspc()); + } + + private boolean firstChunksNotYetRead() { + return currentChunkGroup < ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } + + private void allocateBuffers() { // only if needed + if (rowbfilter == null || rowbfilter.length < buffersLen) { + rowbfilter = new byte[buffersLen]; + rowb = new byte[buffersLen]; + rowbprev = new byte[buffersLen]; + } + } + + /** + * Reads last Internally called after having read the last line. It reads + * extra chunks after IDAT, if present. + */ + private void readLastAndClose() { + // offset = iIdatCstream.getOffset(); + if (currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) { + try { + idatIstream.close(); + } catch (Exception e) { + } + readLastChunks(); + } + close(); + } + + private void close() { + if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END) { // this could only happen if forced close + try { + idatIstream.close(); + } catch (Exception e) { + } + currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; + } + if (shouldCloseStream) { + try { + inputStream.close(); + } catch (Exception e) { + throw new PngjInputException("error closing input stream!", e); + } + } + } + + // nbytes: NOT including the filter byte. leaves result in rowb + private void unfilterRow(int nbytes) { + int ftn = rowbfilter[0]; + FilterType ft = FilterType.getByVal(ftn); + if (ft == null) + throw new PngjInputException("Filter type " + ftn + " invalid"); + switch (ft) { + case FILTER_NONE: + unfilterRowNone(nbytes); + break; + case FILTER_SUB: + unfilterRowSub(nbytes); + break; + case FILTER_UP: + unfilterRowUp(nbytes); + break; + case FILTER_AVERAGE: + unfilterRowAverage(nbytes); + break; + case FILTER_PAETH: + unfilterRowPaeth(nbytes); + break; + default: + throw new PngjInputException("Filter type " + ftn + " not implemented"); + } + if (crctest != null) + crctest.update(rowb, 1, buffersLen - 1); + } + + private void unfilterRowAverage(final int nbytes) { + int i, j, x; + for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) { + x = j > 0 ? (rowb[j] & 0xff) : 0; + rowb[i] = (byte) (rowbfilter[i] + (x + (rowbprev[i] & 0xFF)) / 2); + } + } + + private void unfilterRowNone(final int nbytes) { + for (int i = 1; i <= nbytes; i++) { + rowb[i] = (byte) (rowbfilter[i]); + } + } + + private void unfilterRowPaeth(final int nbytes) { + int i, j, x, y; + for (j = 1 - imgInfo.bytesPixel, i = 1; i <= nbytes; i++, j++) { + x = j > 0 ? (rowb[j] & 0xFF) : 0; + y = j > 0 ? (rowbprev[j] & 0xFF) : 0; + rowb[i] = (byte) (rowbfilter[i] + PngHelperInternal.filterPaethPredictor(x, rowbprev[i] & 0xFF, y)); + } + } + + private void unfilterRowSub(final int nbytes) { + int i, j; + for (i = 1; i <= imgInfo.bytesPixel; i++) { + rowb[i] = (byte) (rowbfilter[i]); + } + for (j = 1, i = imgInfo.bytesPixel + 1; i <= nbytes; i++, j++) { + rowb[i] = (byte) (rowbfilter[i] + rowb[j]); + } + } + + private void unfilterRowUp(final int nbytes) { + for (int i = 1; i <= nbytes; i++) { + rowb[i] = (byte) (rowbfilter[i] + rowbprev[i]); + } + } + + /** + * Reads chunks before first IDAT. Normally this is called automatically + * <p> + * Position before: after IDHR (crc included) Position after: just after the + * first IDAT chunk id + * <P> + * This can be called several times (tentatively), it does nothing if + * already run + * <p> + * (Note: when should this be called? in the constructor? hardly, because we + * loose the opportunity to call setChunkLoadBehaviour() and perhaps other + * settings before reading the first row? but sometimes we want to access + * some metadata (plte, phys) before. Because of this, this method can be + * called explicitly but is also called implicititly in some methods + * (getMetatada(), getChunksList()) + */ + private final void readFirstChunks() { + if (!firstChunksNotYetRead()) + return; + int clen = 0; + boolean found = false; + byte[] chunkid = new byte[4]; // it's important to reallocate in each iteration + currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + while (!found) { + clen = PngHelperInternal.readInt4(inputStream); + offset += 4; + if (clen < 0) + break; + PngHelperInternal.readBytes(inputStream, chunkid, 0, 4); + offset += 4; + if (Arrays.equals(chunkid, ChunkHelper.b_IDAT)) { + found = true; + currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; + // add dummy idat chunk to list + chunksList.appendReadChunk(new PngChunkIDAT(imgInfo, clen, offset - 8), currentChunkGroup); + break; + } else if (Arrays.equals(chunkid, ChunkHelper.b_IEND)) { + throw new PngjInputException("END chunk found before image data (IDAT) at offset=" + offset); + } + if (Arrays.equals(chunkid, ChunkHelper.b_PLTE)) + currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; + readChunk(chunkid, clen, false); + if (Arrays.equals(chunkid, ChunkHelper.b_PLTE)) + currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; + } + int idatLen = found ? clen : -1; + if (idatLen < 0) + throw new PngjInputException("first idat chunk not found!"); + iIdatCstream = new PngIDatChunkInputStream(inputStream, idatLen, offset); + if(inflater == null) { + inflater = new Inflater(); + } else { + inflater.reset(); + } + idatIstream = new InflaterInputStream(iIdatCstream, inflater); + if (!crcEnabled) + iIdatCstream.disableCrcCheck(); + } + + /** + * Reads (and processes) chunks after last IDAT. + **/ + void readLastChunks() { + // PngHelper.logdebug("idat ended? " + iIdatCstream.isEnded()); + currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; + if (!iIdatCstream.isEnded()) + iIdatCstream.forceChunkEnd(); + int clen = iIdatCstream.getLenLastChunk(); + byte[] chunkid = iIdatCstream.getIdLastChunk(); + boolean endfound = false; + boolean first = true; + boolean skip = false; + while (!endfound) { + skip = false; + if (!first) { + clen = PngHelperInternal.readInt4(inputStream); + offset += 4; + if (clen < 0) + throw new PngjInputException("bad chuck len " + clen); + PngHelperInternal.readBytes(inputStream, chunkid, 0, 4); + offset += 4; + } + first = false; + if (Arrays.equals(chunkid, ChunkHelper.b_IDAT)) { + skip = true; // extra dummy (empty?) idat chunk, it can happen, ignore it + } else if (Arrays.equals(chunkid, ChunkHelper.b_IEND)) { + currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; + endfound = true; + } + readChunk(chunkid, clen, skip); + } + if (!endfound) + throw new PngjInputException("end chunk not found - offset=" + offset); + // PngHelper.logdebug("end chunk found ok offset=" + offset); + } + + /** + * Reads chunkd from input stream, adds to ChunksList, and returns it. If + * it's skipped, a PngChunkSkipped object is created + */ + private PngChunk readChunk(byte[] chunkid, int clen, boolean skipforced) { + if (clen < 0) + throw new PngjInputException("invalid chunk lenght: " + clen); + // skipChunksByIdSet is created lazyly, if fist IHDR has already been read + if (skipChunkIdsSet == null && currentChunkGroup > ChunksList.CHUNK_GROUP_0_IDHR) + skipChunkIdsSet = new HashSet<String>(Arrays.asList(skipChunkIds)); + String chunkidstr = ChunkHelper.toString(chunkid); + boolean critical = ChunkHelper.isCritical(chunkidstr); + PngChunk pngChunk = null; + boolean skip = skipforced; + if (maxTotalBytesRead > 0 && clen + offset > maxTotalBytesRead) + throw new PngjInputException("Maximum total bytes to read exceeeded: " + maxTotalBytesRead + " offset:" + + offset + " clen=" + clen); + // an ancillary chunks can be skipped because of several reasons: + if (currentChunkGroup > ChunksList.CHUNK_GROUP_0_IDHR && !critical) + skip = skip || (skipChunkMaxSize > 0 && clen >= skipChunkMaxSize) || skipChunkIdsSet.contains(chunkidstr) + || (maxBytesMetadata > 0 && clen > maxBytesMetadata - bytesChunksLoaded) + || !ChunkHelper.shouldLoad(chunkidstr, chunkLoadBehaviour); + if (skip) { + PngHelperInternal.skipBytes(inputStream, clen); + PngHelperInternal.readInt4(inputStream); // skip - we dont call PngHelperInternal.skipBytes(inputStream, + // clen + 4) for risk of overflow + pngChunk = new PngChunkSkipped(chunkidstr, imgInfo, clen); + } else { + ChunkRaw chunk = new ChunkRaw(clen, chunkid, true); + chunk.readChunkData(inputStream, crcEnabled || critical); + pngChunk = PngChunk.factory(chunk, imgInfo); + if (!pngChunk.crit) + bytesChunksLoaded += chunk.len; + } + pngChunk.setOffset(offset - 8L); + chunksList.appendReadChunk(pngChunk, currentChunkGroup); + offset += clen + 4L; + return pngChunk; + } + + /** + * Logs/prints a warning. + * <p> + * The default behaviour is print to stderr, but it can be overriden. + * <p> + * This happens rarely - most errors are fatal. + */ + protected void logWarn(String warn) { + System.err.println(warn); + } + + /** + * @see #setChunkLoadBehaviour(ChunkLoadBehaviour) + */ + public ChunkLoadBehaviour getChunkLoadBehaviour() { + return chunkLoadBehaviour; + } + + /** + * Determines which ancillary chunks (metada) are to be loaded + * + * @param chunkLoadBehaviour + * {@link ChunkLoadBehaviour} + */ + public void setChunkLoadBehaviour(ChunkLoadBehaviour chunkLoadBehaviour) { + this.chunkLoadBehaviour = chunkLoadBehaviour; + } + + /** + * All loaded chunks (metada). If we have not yet end reading the image, + * this will include only the chunks before the pixels data (IDAT) + * <p> + * Critical chunks are included, except that all IDAT chunks appearance are + * replaced by a single dummy-marker IDAT chunk. These might be copied to + * the PngWriter + * <p> + * + * @see #getMetadata() + */ + public ChunksList getChunksList() { + if (firstChunksNotYetRead()) + readFirstChunks(); + return chunksList; + } + + int getCurrentChunkGroup() { + return currentChunkGroup; + } + + /** + * High level wrapper over chunksList + * + * @see #getChunksList() + */ + public PngMetadata getMetadata() { + if (firstChunksNotYetRead()) + readFirstChunks(); + return metadata; + } + + /** + * If called for first time, calls readRowInt. Elsewhere, it calls the + * appropiate readRowInt/readRowByte + * <p> + * In general, specifying the concrete readRowInt/readRowByte is preferrable + * + * @see #readRowInt(int) {@link #readRowByte(int)} + */ + public ImageLine readRow(int nrow) { + if (imgLine == null) + imgLine = new ImageLine(imgInfo, SampleType.INT, unpackedMode); + return imgLine.sampleType != SampleType.BYTE ? readRowInt(nrow) : readRowByte(nrow); + } + + /** + * Reads the row as INT, storing it in the {@link #imgLine} property and + * returning it. + * + * The row must be greater or equal than the last read row. + * + * @param nrow + * Row number, from 0 to rows-1. Increasing order. + * @return ImageLine object, also available as field. Data is in + * {@link ImageLine#scanline} (int) field. + */ + public ImageLine readRowInt(int nrow) { + if (imgLine == null) + imgLine = new ImageLine(imgInfo, SampleType.INT, unpackedMode); + if (imgLine.getRown() == nrow) // already read + return imgLine; + readRowInt(imgLine.scanline, nrow); + imgLine.setFilterUsed(FilterType.getByVal(rowbfilter[0])); + imgLine.setRown(nrow); + return imgLine; + } + + /** + * Reads the row as BYTES, storing it in the {@link #imgLine} property and + * returning it. + * + * The row must be greater or equal than the last read row. This method + * allows to pass the same row that was last read. + * + * @param nrow + * Row number, from 0 to rows-1. Increasing order. + * @return ImageLine object, also available as field. Data is in + * {@link ImageLine#scanlineb} (byte) field. + */ + public ImageLine readRowByte(int nrow) { + if (imgLine == null) + imgLine = new ImageLine(imgInfo, SampleType.BYTE, unpackedMode); + if (imgLine.getRown() == nrow) // already read + return imgLine; + readRowByte(imgLine.scanlineb, nrow); + imgLine.setFilterUsed(FilterType.getByVal(rowbfilter[0])); + imgLine.setRown(nrow); + return imgLine; + } + + /** + * @see #readRowInt(int[], int) + */ + public final int[] readRow(int[] buffer, final int nrow) { + return readRowInt(buffer, nrow); + } + + /** + * Reads a line and returns it as a int[] array. + * <p> + * You can pass (optionally) a prealocatted buffer. + * <p> + * If the bitdepth is less than 8, the bytes are packed - unless + * {@link #unpackedMode} is true. + * + * @param buffer + * Prealocated buffer, or null. + * @param nrow + * Row number (0 is top). Most be strictly greater than the last + * read row. + * + * @return The scanline in the same passwd buffer if it was allocated, a + * newly allocated one otherwise + */ + public final int[] readRowInt(int[] buffer, final int nrow) { + if (buffer == null) + buffer = new int[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked]; + if (!interlaced) { + if (nrow <= rowNum) + throw new PngjInputException("rows must be read in increasing order: " + nrow); + int bytesread = 0; + while (rowNum < nrow) + bytesread = readRowRaw(rowNum + 1); // read rows, perhaps skipping if necessary + decodeLastReadRowToInt(buffer, bytesread); + } else { // interlaced + if (deinterlacer.getImageInt() == null) + deinterlacer.setImageInt(readRowsInt().scanlines); // read all image and store it in deinterlacer + System.arraycopy(deinterlacer.getImageInt()[nrow], 0, buffer, 0, unpackedMode ? imgInfo.samplesPerRow + : imgInfo.samplesPerRowPacked); + } + return buffer; + } + + /** + * Reads a line and returns it as a byte[] array. + * <p> + * You can pass (optionally) a prealocatted buffer. + * <p> + * If the bitdepth is less than 8, the bytes are packed - unless + * {@link #unpackedMode} is true. <br> + * If the bitdepth is 16, the least significant byte is lost. + * <p> + * + * @param buffer + * Prealocated buffer, or null. + * @param nrow + * Row number (0 is top). Most be strictly greater than the last + * read row. + * + * @return The scanline in the same passwd buffer if it was allocated, a + * newly allocated one otherwise + */ + public final byte[] readRowByte(byte[] buffer, final int nrow) { + if (buffer == null) + buffer = new byte[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked]; + if (!interlaced) { + if (nrow <= rowNum) + throw new PngjInputException("rows must be read in increasing order: " + nrow); + int bytesread = 0; + while (rowNum < nrow) + bytesread = readRowRaw(rowNum + 1); // read rows, perhaps skipping if necessary + decodeLastReadRowToByte(buffer, bytesread); + } else { // interlaced + if (deinterlacer.getImageByte() == null) + deinterlacer.setImageByte(readRowsByte().scanlinesb); // read all image and store it in deinterlacer + System.arraycopy(deinterlacer.getImageByte()[nrow], 0, buffer, 0, unpackedMode ? imgInfo.samplesPerRow + : imgInfo.samplesPerRowPacked); + } + return buffer; + } + + /** + * @param nrow + * @deprecated Now {@link #readRow(int)} implements the same funcion. This + * method will be removed in future releases + */ + public ImageLine getRow(int nrow) { + return readRow(nrow); + } + + private void decodeLastReadRowToInt(int[] buffer, int bytesRead) { + if (imgInfo.bitDepth <= 8) + for (int i = 0, j = 1; i < bytesRead; i++) + buffer[i] = (rowb[j++] & 0xFF); // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html + else + for (int i = 0, j = 1; j <= bytesRead; i++) + buffer[i] = ((rowb[j++] & 0xFF) << 8) + (rowb[j++] & 0xFF); // 16 bitspc + if (imgInfo.packed && unpackedMode) + ImageLine.unpackInplaceInt(imgInfo, buffer, buffer, false); + } + + private void decodeLastReadRowToByte(byte[] buffer, int bytesRead) { + if (imgInfo.bitDepth <= 8) + System.arraycopy(rowb, 1, buffer, 0, bytesRead); + else + for (int i = 0, j = 1; j < bytesRead; i++, j += 2) + buffer[i] = rowb[j];// 16 bits in 1 byte: this discards the LSB!!! + if (imgInfo.packed && unpackedMode) + ImageLine.unpackInplaceByte(imgInfo, buffer, buffer, false); + } + + /** + * Reads a set of lines and returns it as a ImageLines object, which wraps + * matrix. Internally it reads all lines, but decodes and stores only the + * wanted ones. This starts and ends the reading, and cannot be combined + * with other reading methods. + * <p> + * This it's more efficient (speed an memory) that doing calling + * readRowInt() for each desired line only if the image is interlaced. + * <p> + * Notice that the columns in the matrix is not the pixel width of the + * image, but rather pixels x channels + * + * @see #readRowInt(int) to read about the format of each row + * + * @param rowOffset + * Number of rows to be skipped + * @param nRows + * Total number of rows to be read. -1: read all available + * @param rowStep + * Row increment. If 1, we read consecutive lines; if 2, we read + * even/odd lines, etc + * @return Set of lines as a ImageLines, which wraps a matrix + */ + public ImageLines readRowsInt(int rowOffset, int nRows, int rowStep) { + if (nRows < 0) + nRows = (imgInfo.rows - rowOffset) / rowStep; + if (rowStep < 1 || rowOffset < 0 || nRows * rowStep + rowOffset > imgInfo.rows) + throw new PngjInputException("bad args"); + ImageLines imlines = new ImageLines(imgInfo, SampleType.INT, unpackedMode, rowOffset, nRows, rowStep); + if (!interlaced) { + for (int j = 0; j < imgInfo.rows; j++) { + int bytesread = readRowRaw(j); // read and perhaps discards + int mrow = imlines.imageRowToMatrixRowStrict(j); + if (mrow >= 0) + decodeLastReadRowToInt(imlines.scanlines[mrow], bytesread); + } + } else { // and now, for something completely different (interlaced) + int[] buf = new int[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked]; + for (int p = 1; p <= 7; p++) { + deinterlacer.setPass(p); + for (int i = 0; i < deinterlacer.getRows(); i++) { + int bytesread = readRowRaw(i); + int j = deinterlacer.getCurrRowReal(); + int mrow = imlines.imageRowToMatrixRowStrict(j); + if (mrow >= 0) { + decodeLastReadRowToInt(buf, bytesread); + deinterlacer.deinterlaceInt(buf, imlines.scanlines[mrow], !unpackedMode); + } + } + } + } + end(); + return imlines; + } + + /** + * Same as readRowsInt(0, imgInfo.rows, 1) + * + * @see #readRowsInt(int, int, int) + */ + public ImageLines readRowsInt() { + return readRowsInt(0, imgInfo.rows, 1); + } + + /** + * Reads a set of lines and returns it as a ImageLines object, which wrapas + * a byte[][] matrix. Internally it reads all lines, but decodes and stores + * only the wanted ones. This starts and ends the reading, and cannot be + * combined with other reading methods. + * <p> + * This it's more efficient (speed an memory) that doing calling + * readRowByte() for each desired line only if the image is interlaced. + * <p> + * Notice that the columns in the matrix is not the pixel width of the + * image, but rather pixels x channels + * + * @see #readRowByte(int) to read about the format of each row. Notice that + * if the bitdepth is 16 this will lose information + * + * @param rowOffset + * Number of rows to be skipped + * @param nRows + * Total number of rows to be read. -1: read all available + * @param rowStep + * Row increment. If 1, we read consecutive lines; if 2, we read + * even/odd lines, etc + * @return Set of lines as a matrix + */ + public ImageLines readRowsByte(int rowOffset, int nRows, int rowStep) { + if (nRows < 0) + nRows = (imgInfo.rows - rowOffset) / rowStep; + if (rowStep < 1 || rowOffset < 0 || nRows * rowStep + rowOffset > imgInfo.rows) + throw new PngjInputException("bad args"); + ImageLines imlines = new ImageLines(imgInfo, SampleType.BYTE, unpackedMode, rowOffset, nRows, rowStep); + if (!interlaced) { + for (int j = 0; j < imgInfo.rows; j++) { + int bytesread = readRowRaw(j); // read and perhaps discards + int mrow = imlines.imageRowToMatrixRowStrict(j); + if (mrow >= 0) + decodeLastReadRowToByte(imlines.scanlinesb[mrow], bytesread); + } + } else { // and now, for something completely different (interlaced) + byte[] buf = new byte[unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked]; + for (int p = 1; p <= 7; p++) { + deinterlacer.setPass(p); + for (int i = 0; i < deinterlacer.getRows(); i++) { + int bytesread = readRowRaw(i); + int j = deinterlacer.getCurrRowReal(); + int mrow = imlines.imageRowToMatrixRowStrict(j); + if (mrow >= 0) { + decodeLastReadRowToByte(buf, bytesread); + deinterlacer.deinterlaceByte(buf, imlines.scanlinesb[mrow], !unpackedMode); + } + } + } + } + end(); + return imlines; + } + + /** + * Same as readRowsByte(0, imgInfo.rows, 1) + * + * @see #readRowsByte(int, int, int) + */ + public ImageLines readRowsByte() { + return readRowsByte(0, imgInfo.rows, 1); + } + + /* + * For the interlaced case, nrow indicates the subsampled image - the pass must be set already. + * + * This must be called in strict order, both for interlaced or no interlaced. + * + * Updates rowNum. + * + * Leaves raw result in rowb + * + * Returns bytes actually read (not including the filter byte) + */ + private int readRowRaw(final int nrow) { + if (nrow == 0) { + if (firstChunksNotYetRead()) + readFirstChunks(); + allocateBuffers(); + if (interlaced) + Arrays.fill(rowb, (byte) 0); // new subimage: reset filters: this is enough, see the swap that happens lines + } + // below + int bytesRead = imgInfo.bytesPerRow; // NOT including the filter byte + if (interlaced) { + if (nrow < 0 || nrow > deinterlacer.getRows() || (nrow != 0 && nrow != deinterlacer.getCurrRowSubimg() + 1)) + throw new PngjInputException("invalid row in interlaced mode: " + nrow); + deinterlacer.setRow(nrow); + bytesRead = (imgInfo.bitspPixel * deinterlacer.getPixelsToRead() + 7) / 8; + if (bytesRead < 1) + throw new PngjExceptionInternal("wtf??"); + } else { // check for non interlaced + if (nrow < 0 || nrow >= imgInfo.rows || nrow != rowNum + 1) + throw new PngjInputException("invalid row: " + nrow); + } + rowNum = nrow; + // swap buffers + byte[] tmp = rowb; + rowb = rowbprev; + rowbprev = tmp; + // loads in rowbfilter "raw" bytes, with filter + PngHelperInternal.readBytes(idatIstream, rowbfilter, 0, bytesRead + 1); + offset = iIdatCstream.getOffset(); + if (offset < 0) + throw new PngjExceptionInternal("bad offset ??" + offset); + if (maxTotalBytesRead > 0 && offset >= maxTotalBytesRead) + throw new PngjInputException("Reading IDAT: Maximum total bytes to read exceeeded: " + maxTotalBytesRead + + " offset:" + offset); + rowb[0] = 0; + unfilterRow(bytesRead); + rowb[0] = rowbfilter[0]; + if ((rowNum == imgInfo.rows - 1 && !interlaced) || (interlaced && deinterlacer.isAtLastRow())) + readLastAndClose(); + return bytesRead; + } + + /** + * Reads all the (remaining) file, skipping the pixels data. This is much + * more efficient that calling readRow(), specially for big files (about 10 + * times faster!), because it doesn't even decompress the IDAT stream and + * disables CRC check Use this if you are not interested in reading + * pixels,only metadata. + */ + public void readSkippingAllRows() { + if (firstChunksNotYetRead()) + readFirstChunks(); + // we read directly from the compressed stream, we dont decompress nor chec CRC + iIdatCstream.disableCrcCheck(); + allocateBuffers(); + try { + int r; + do { + r = iIdatCstream.read(rowbfilter, 0, buffersLen); + } while (r >= 0); + } catch (IOException e) { + throw new PngjInputException("error in raw read of IDAT", e); + } + offset = iIdatCstream.getOffset(); + if (offset < 0) + throw new PngjExceptionInternal("bad offset ??" + offset); + if (maxTotalBytesRead > 0 && offset >= maxTotalBytesRead) + throw new PngjInputException("Reading IDAT: Maximum total bytes to read exceeeded: " + maxTotalBytesRead + + " offset:" + offset); + readLastAndClose(); + } + + /** + * Set total maximum bytes to read (0: unlimited; default: 200MB). <br> + * These are the bytes read (not loaded) in the input stream. If exceeded, + * an exception will be thrown. + */ + public void setMaxTotalBytesRead(long maxTotalBytesToRead) { + this.maxTotalBytesRead = maxTotalBytesToRead; + } + + /** + * @return Total maximum bytes to read. + */ + public long getMaxTotalBytesRead() { + return maxTotalBytesRead; + } + + /** + * Set total maximum bytes to load from ancillary chunks (0: unlimited; + * default: 5Mb).<br> + * If exceeded, some chunks will be skipped + */ + public void setMaxBytesMetadata(int maxBytesChunksToLoad) { + this.maxBytesMetadata = maxBytesChunksToLoad; + } + + /** + * @return Total maximum bytes to load from ancillary ckunks. + */ + public int getMaxBytesMetadata() { + return maxBytesMetadata; + } + + /** + * Set maximum size in bytes for individual ancillary chunks (0: unlimited; + * default: 2MB). <br> + * Chunks exceeding this length will be skipped (the CRC will not be + * checked) and the chunk will be saved as a PngChunkSkipped object. See + * also setSkipChunkIds + */ + public void setSkipChunkMaxSize(int skipChunksBySize) { + this.skipChunkMaxSize = skipChunksBySize; + } + + /** + * @return maximum size in bytes for individual ancillary chunks. + */ + public int getSkipChunkMaxSize() { + return skipChunkMaxSize; + } + + /** + * Chunks ids to be skipped. <br> + * These chunks will be skipped (the CRC will not be checked) and the chunk + * will be saved as a PngChunkSkipped object. See also setSkipChunkMaxSize + */ + public void setSkipChunkIds(String[] skipChunksById) { + this.skipChunkIds = skipChunksById == null ? new String[] {} : skipChunksById; + } + + /** + * @return Chunk-IDs to be skipped. + */ + public String[] getSkipChunkIds() { + return skipChunkIds; + } + + /** + * if true, input stream will be closed after ending read + * <p> + * default=true + */ + public void setShouldCloseStream(boolean shouldCloseStream) { + this.shouldCloseStream = shouldCloseStream; + } + + /** + * Normally this does nothing, but it can be used to force a premature + * closing. Its recommended practice to call it after reading the image + * pixels. + */ + public void end() { + if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END) + close(); + } + + /** + * Interlaced PNG is accepted -though not welcomed- now... + */ + public boolean isInterlaced() { + return interlaced; + } + + /** + * set/unset "unpackedMode"<br> + * If false (default) packed types (bitdepth=1,2 or 4) will keep several + * samples packed in one element (byte or int) <br> + * If true, samples will be unpacked on reading, and each element in the + * scanline will be sample. This implies more processing and memory, but + * it's the most efficient option if you intend to read individual pixels. <br> + * This option should only be set before start reading. + * + * @param unPackedMode + */ + public void setUnpackedMode(boolean unPackedMode) { + this.unpackedMode = unPackedMode; + } + + /** + * @see PngReader#setUnpackedMode(boolean) + */ + public boolean isUnpackedMode() { + return unpackedMode; + } + + /** + * Tries to reuse the allocated buffers from other already used PngReader + * object. This will have no effect if the buffers are smaller than necessary. + * It also reuses the inflater. + * + * @param other A PngReader that has already finished reading pixels. Can be null. + */ + public void reuseBuffersFrom(PngReader other) { + if(other==null) return; + if (other.currentChunkGroup < ChunksList.CHUNK_GROUP_5_AFTERIDAT) + throw new PngjInputException("PngReader to be reused have not yet ended reading pixels"); + if (other.rowbfilter != null && other.rowbfilter.length >= buffersLen) { + rowbfilter = other.rowbfilter; + rowb = other.rowb; + rowbprev = other.rowbprev; + } + inflater = other.inflater; + } + + /** + * Disables the CRC integrity check in IDAT chunks and ancillary chunks, + * this gives a slight increase in reading speed for big files + */ + public void setCrcCheckDisabled() { + crcEnabled = false; + } + + /** + * Just for testing. TO be called after ending reading, only if + * initCrctest() was called before start + * + * @return CRC of the raw pixels values + */ + long getCrctestVal() { + return crctest.getValue(); + } + + /** + * Inits CRC object and enables CRC calculation + */ + void initCrctest() { + this.crctest = new CRC32(); + } + + /** + * Basic info, for debugging. + */ + public String toString() { // basic info + return "filename=" + filename + " " + imgInfo.toString(); + } +} diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java index 601cd96c0..3e684a881 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java @@ -83,8 +83,9 @@ public class PngWriter { }
/**
- * Constructs a new PngWriter from a output stream. After construction nothing is writen yet. You still can set some
- * parameters (compression, filters) and queue chunks before start writing the pixels.
+ * Constructs a new PngWriter from a output stream. After construction
+ * nothing is writen yet. You still can set some parameters (compression,
+ * filters) and queue chunks before start writing the pixels.
* <p>
* See also <code>FileHelper.createPngWriter()</code> if available.
*
@@ -418,13 +419,15 @@ public class PngWriter { /**
* Copies first (pre IDAT) ancillary chunks from a PngReader.
* <p>
- * Should be called when creating an image from another, before starting writing lines, to copy relevant chunks.
+ * Should be called when creating an image from another, before starting
+ * writing lines, to copy relevant chunks.
* <p>
*
* @param reader
* : PngReader object, already opened.
* @param copy_mask
- * : Mask bit (OR), see <code>ChunksToWrite.COPY_XXX</code> constants
+ * : Mask bit (OR), see <code>ChunksToWrite.COPY_XXX</code>
+ * constants
*/
public void copyChunksFirst(PngReader reader, int copy_mask) {
copyChunks(reader, copy_mask, false);
@@ -433,14 +436,15 @@ public class PngWriter { /**
* Copies last (post IDAT) ancillary chunks from a PngReader.
* <p>
- * Should be called when creating an image from another, after writing all lines, before closing the writer, to copy
- * additional chunks.
+ * Should be called when creating an image from another, after writing all
+ * lines, before closing the writer, to copy additional chunks.
* <p>
*
* @param reader
* : PngReader object, already opened and fully read.
* @param copy_mask
- * : Mask bit (OR), see <code>ChunksToWrite.COPY_XXX</code> constants
+ * : Mask bit (OR), see <code>ChunksToWrite.COPY_XXX</code>
+ * constants
*/
public void copyChunksLast(PngReader reader, int copy_mask) {
copyChunks(reader, copy_mask, true);
@@ -449,8 +453,8 @@ public class PngWriter { /**
* Computes compressed size/raw size, approximate.
* <p>
- * Actually: compressed size = total size of IDAT data , raw size = uncompressed pixel bytes = rows * (bytesPerRow +
- * 1).
+ * Actually: compressed size = total size of IDAT data , raw size =
+ * uncompressed pixel bytes = rows * (bytesPerRow + 1).
*
* This must be called after pngw.end()
*/
@@ -463,7 +467,8 @@ public class PngWriter { }
/**
- * Finalizes the image creation and closes the stream. This MUST be called after writing the lines.
+ * Finalizes the image creation and closes the stream. This MUST be called
+ * after writing the lines.
*/
public void end() {
if (rowNum != imgInfo.rows - 1)
@@ -525,15 +530,17 @@ public class PngWriter { * See also setCompLevel()
*
* @param filterType
- * One of the five prediction types or strategy to choose it (see <code>PngFilterType</code>) Recommended
- * values: DEFAULT (default) or AGGRESIVE
+ * One of the five prediction types or strategy to choose it (see
+ * <code>PngFilterType</code>) Recommended values: DEFAULT
+ * (default) or AGGRESIVE
*/
public void setFilterType(FilterType filterType) {
filterStrat = new FilterWriteStrategy(imgInfo, filterType);
}
/**
- * Sets maximum size of IDAT fragments. This has little effect on performance you should rarely call this
+ * Sets maximum size of IDAT fragments. This has little effect on
+ * performance you should rarely call this
* <p>
*
* @param idatMaxSize
@@ -553,7 +560,8 @@ public class PngWriter { }
/**
- * Deflater strategy: one of Deflater.FILTERED Deflater.HUFFMAN_ONLY Deflater.DEFAULT_STRATEGY
+ * Deflater strategy: one of Deflater.FILTERED Deflater.HUFFMAN_ONLY
+ * Deflater.DEFAULT_STRATEGY
* <p>
* Default: Deflater.FILTERED . This should be changed very rarely.
*/
@@ -562,8 +570,8 @@ public class PngWriter { }
/**
- * Writes line, checks that the row number is consistent with that of the ImageLine See writeRow(int[] newrow, int
- * rown)
+ * Writes line, checks that the row number is consistent with that of the
+ * ImageLine See writeRow(int[] newrow, int rown)
*
* @deprecated Better use writeRow(ImageLine imgline, int rownumber)
*/
@@ -607,18 +615,22 @@ public class PngWriter { /**
* Writes a full image row.
* <p>
- * This must be called sequentially from n=0 to n=rows-1 One integer per sample , in the natural order: R G B R G B
- * ... (or R G B A R G B A... if has alpha) The values should be between 0 and 255 for 8 bitspc images, and between
- * 0- 65535 form 16 bitspc images (this applies also to the alpha channel if present) The array can be reused.
+ * This must be called sequentially from n=0 to n=rows-1 One integer per
+ * sample , in the natural order: R G B R G B ... (or R G B A R G B A... if
+ * has alpha) The values should be between 0 and 255 for 8 bitspc images,
+ * and between 0- 65535 form 16 bitspc images (this applies also to the
+ * alpha channel if present) The array can be reused.
* <p>
- * Warning: the array might be modified in some cases (unpacked row with low bitdepth)
+ * Warning: the array might be modified in some cases (unpacked row with low
+ * bitdepth)
* <p>
*
* @param newrow
- * Array of pixel values. Warning: the array size should be exact (samplesPerRowP)
+ * Array of pixel values. Warning: the array size should be exact
+ * (samplesPerRowP)
* @param rown
- * Row number, from 0 (top) to rows-1 (bottom). This is just used as a check. Pass -1 if you want to
- * autocompute it
+ * Row number, from 0 (top) to rows-1 (bottom). This is just used
+ * as a check. Pass -1 if you want to autocompute it
*/
public void writeRowInt(int[] newrow, int rown) {
prepareEncodeRow(rown);
@@ -627,8 +639,9 @@ public class PngWriter { }
/**
- * Same semantics as writeRowInt but using bytes. Each byte is still a sample. If 16bitdepth, we are passing only
- * the most significant byte (and hence losing some info)
+ * Same semantics as writeRowInt but using bytes. Each byte is still a
+ * sample. If 16bitdepth, we are passing only the most significant byte (and
+ * hence losing some info)
*
* @see PngWriter#writeRowInt(int[], int)
*/
@@ -659,12 +672,15 @@ public class PngWriter { }
/**
- * If false (default), and image has bitdepth 1-2-4, the scanlines passed are assumed to be already packed.
+ * If false (default), and image has bitdepth 1-2-4, the scanlines passed
+ * are assumed to be already packed.
* <p>
- * If true, each element is a sample, the writer will perform the packing if necessary.
+ * If true, each element is a sample, the writer will perform the packing if
+ * necessary.
* <p>
- * Warning: when using {@link #writeRow(ImageLine, int)} (recommended) the <tt>packed</tt> flag of the ImageLine
- * object overrides (and overwrites!) this field.
+ * Warning: when using {@link #writeRow(ImageLine, int)} (recommended) the
+ * <tt>packed</tt> flag of the ImageLine object overrides (and overwrites!)
+ * this field.
*/
public void setUseUnPackedMode(boolean useUnpackedMode) {
this.unpackedMode = useUnpackedMode;
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java index 963abc50e..c429b893b 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java @@ -1,7 +1,8 @@ package jogamp.opengl.util.pngj;
/**
- * Exception for anomalous internal problems (sort of asserts) that point to some issue with the library
+ * Exception for anomalous internal problems (sort of asserts) that point to
+ * some issue with the library
*
* @author Hernan J Gonzalez
*
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngjUnsupportedException.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngjUnsupportedException.java index 0801e33bb..f68458d19 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngjUnsupportedException.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngjUnsupportedException.java @@ -1,7 +1,8 @@ package jogamp.opengl.util.pngj;
/**
- * Exception thrown because of some valid feature of PNG standard that this library does not support
+ * Exception thrown because of some valid feature of PNG standard that this
+ * library does not support
*/
public class PngjUnsupportedException extends RuntimeException {
private static final long serialVersionUID = 1L;
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ProgressiveOutputStream.java b/src/jogl/classes/jogamp/opengl/util/pngj/ProgressiveOutputStream.java index a5bad666c..4516a0886 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ProgressiveOutputStream.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ProgressiveOutputStream.java @@ -4,7 +4,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException;
/**
- * stream that outputs to memory and allows to flush fragments every 'size' bytes to some other destination
+ * stream that outputs to memory and allows to flush fragments every 'size'
+ * bytes to some other destination
*/
abstract class ProgressiveOutputStream extends ByteArrayOutputStream {
private final int size;
@@ -50,8 +51,8 @@ abstract class ProgressiveOutputStream extends ByteArrayOutputStream { }
/**
- * if it's time to flush data (or if forced==true) calls abstract method flushBuffer() and cleans those bytes from
- * own buffer
+ * if it's time to flush data (or if forced==true) calls abstract method
+ * flushBuffer() and cleans those bytes from own buffer
*/
private final void checkFlushBuffer(boolean forced) {
while (forced || count >= size) {
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkHelper.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkHelper.java index ed091d35a..a995e4481 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkHelper.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkHelper.java @@ -1,253 +1,297 @@ -package jogamp.opengl.util.pngj.chunks;
-
-// see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
-// http://www.w3.org/TR/PNG/#5Chunk-naming-conventions
-// http://www.w3.org/TR/PNG/#table53
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.InflaterInputStream;
-
-import jogamp.opengl.util.pngj.PngHelperInternal;
-import jogamp.opengl.util.pngj.PngjException;
-
-
-public class ChunkHelper {
- public static final String IHDR = "IHDR";
- public static final String PLTE = "PLTE";
- public static final String IDAT = "IDAT";
- public static final String IEND = "IEND";
- public static final byte[] b_IHDR = toBytes(IHDR);
- public static final byte[] b_PLTE = toBytes(PLTE);
- public static final byte[] b_IDAT = toBytes(IDAT);
- public static final byte[] b_IEND = toBytes(IEND);
-
- public static final String cHRM = "cHRM";
- public static final String gAMA = "gAMA";
- public static final String iCCP = "iCCP";
- public static final String sBIT = "sBIT";
- public static final String sRGB = "sRGB";
- public static final String bKGD = "bKGD";
- public static final String hIST = "hIST";
- public static final String tRNS = "tRNS";
- public static final String pHYs = "pHYs";
- public static final String sPLT = "sPLT";
- public static final String tIME = "tIME";
- public static final String iTXt = "iTXt";
- public static final String tEXt = "tEXt";
- public static final String zTXt = "zTXt";
-
- /**
- * Converts to bytes using Latin1 (ISO-8859-1)
- */
- public static byte[] toBytes(String x) {
- return x.getBytes(PngHelperInternal.charsetLatin1);
- }
-
- /**
- * Converts to String using Latin1 (ISO-8859-1)
- */
- public static String toString(byte[] x) {
- return new String(x, PngHelperInternal.charsetLatin1);
- }
-
- /**
- * Converts to String using Latin1 (ISO-8859-1)
- */
- public static String toString(byte[] x, int offset, int len) {
- return new String(x, offset, len, PngHelperInternal.charsetLatin1);
- }
-
- /**
- * Converts to bytes using UTF-8
- */
- public static byte[] toBytesUTF8(String x) {
- return x.getBytes(PngHelperInternal.charsetUTF8);
- }
-
- /**
- * Converts to string using UTF-8
- */
- public static String toStringUTF8(byte[] x) {
- return new String(x, PngHelperInternal.charsetUTF8);
- }
-
- /**
- * Converts to string using UTF-8
- */
- public static String toStringUTF8(byte[] x, int offset, int len) {
- return new String(x, offset, len, PngHelperInternal.charsetUTF8);
- }
-
- /**
- * critical chunk : first letter is uppercase
- */
- public static boolean isCritical(String id) {
- return (Character.isUpperCase(id.charAt(0)));
- }
-
- /**
- * public chunk: second letter is uppercase
- */
- public static boolean isPublic(String id) { //
- return (Character.isUpperCase(id.charAt(1)));
- }
-
- /**
- * Safe to copy chunk: fourth letter is lower case
- */
- public static boolean isSafeToCopy(String id) {
- return (!Character.isUpperCase(id.charAt(3)));
- }
-
- /**
- * "Unknown" just means that our chunk factory (even when it has been augmented by client code) did not recognize
- * its id
- */
- public static boolean isUnknown(PngChunk c) {
- return c instanceof PngChunkUNKNOWN;
- }
-
- /**
- * Finds position of null byte in array
- *
- * @param b
- * @return -1 if not found
- */
- public static int posNullByte(byte[] b) {
- for (int i = 0; i < b.length; i++)
- if (b[i] == 0)
- return i;
- return -1;
- }
-
- /**
- * Decides if a chunk should be loaded, according to a ChunkLoadBehaviour
- *
- * @param id
- * @param behav
- * @return true/false
- */
- public static boolean shouldLoad(String id, ChunkLoadBehaviour behav) {
- if (isCritical(id))
- return true;
- boolean kwown = PngChunk.isKnown(id);
- switch (behav) {
- case LOAD_CHUNK_ALWAYS:
- return true;
- case LOAD_CHUNK_IF_SAFE:
- return kwown || isSafeToCopy(id);
- case LOAD_CHUNK_KNOWN:
- return kwown;
- case LOAD_CHUNK_NEVER:
- return false;
- }
- return false; // should not reach here
- }
-
- public final static byte[] compressBytes(byte[] ori, boolean compress) {
- return compressBytes(ori, 0, ori.length, compress);
- }
-
- public static byte[] compressBytes(byte[] ori, int offset, int len, boolean compress) {
- try {
- ByteArrayInputStream inb = new ByteArrayInputStream(ori, offset, len);
- InputStream in = compress ? inb : new InflaterInputStream(inb);
- ByteArrayOutputStream outb = new ByteArrayOutputStream();
- OutputStream out = compress ? new DeflaterOutputStream(outb) : outb;
- shovelInToOut(in, out);
- in.close();
- out.close();
- return outb.toByteArray();
- } catch (Exception e) {
- throw new PngjException(e);
- }
- }
-
- /**
- * Shovels all data from an input stream to an output stream.
- */
- private static void shovelInToOut(InputStream in, OutputStream out) throws IOException {
- byte[] buffer = new byte[1024];
- int len;
- while ((len = in.read(buffer)) > 0) {
- out.write(buffer, 0, len);
- }
- }
-
- public static boolean maskMatch(int v, int mask) {
- return (v & mask) != 0;
- }
-
- /**
- * Returns only the chunks that "match" the predicate
- *
- * See also trimList()
- */
- public static List<PngChunk> filterList(List<PngChunk> target, ChunkPredicate predicateKeep) {
- List<PngChunk> result = new ArrayList<PngChunk>();
- for (PngChunk element : target) {
- if (predicateKeep.match(element)) {
- result.add(element);
- }
- }
- return result;
- }
-
- /**
- * Remove (in place) the chunks that "match" the predicate
- *
- * See also filterList
- */
- public static int trimList(List<PngChunk> target, ChunkPredicate predicateRemove) {
- Iterator<PngChunk> it = target.iterator();
- int cont = 0;
- while (it.hasNext()) {
- PngChunk c = it.next();
- if (predicateRemove.match(c)) {
- it.remove();
- cont++;
- }
- }
- return cont;
- }
-
- /**
- * MY adhoc criteria: two chunks are "equivalent" ("practically equal") if they have same id and (perhaps, if
- * multiple are allowed) if the match also in some "internal key" (eg: key for string values, palette for sPLT, etc)
- *
- * Notice that the use of this is optional, and that the PNG standard allows Text chunks that have same key
- *
- * @return true if "equivalent"
- */
- public static final boolean equivalent(PngChunk c1, PngChunk c2) {
- if (c1 == c2)
- return true;
- if (c1 == null || c2 == null || !c1.id.equals(c2.id))
- return false;
- // same id
- if (c1.getClass() != c2.getClass())
- return false; // should not happen
- if (!c2.allowsMultiple())
- return true;
- if (c1 instanceof PngChunkTextVar) {
- return ((PngChunkTextVar) c1).getKey().equals(((PngChunkTextVar) c2).getKey());
- }
- if (c1 instanceof PngChunkSPLT) {
- return ((PngChunkSPLT) c1).getPalName().equals(((PngChunkSPLT) c2).getPalName());
- }
- // unknown chunks that allow multiple? consider they don't match
- return false;
- }
-
- public static boolean isText(PngChunk c) {
- return c instanceof PngChunkTextVar;
- }
-
-}
+package jogamp.opengl.util.pngj.chunks; + + +// see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html +// http://www.w3.org/TR/PNG/#5Chunk-naming-conventions +// http://www.w3.org/TR/PNG/#table53 +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +import jogamp.opengl.util.pngj.PngHelperInternal; +import jogamp.opengl.util.pngj.PngjException; + + +public class ChunkHelper { + public static final String IHDR = "IHDR"; + public static final String PLTE = "PLTE"; + public static final String IDAT = "IDAT"; + public static final String IEND = "IEND"; + public static final byte[] b_IHDR = toBytes(IHDR); + public static final byte[] b_PLTE = toBytes(PLTE); + public static final byte[] b_IDAT = toBytes(IDAT); + public static final byte[] b_IEND = toBytes(IEND); + + public static final String cHRM = "cHRM"; + public static final String gAMA = "gAMA"; + public static final String iCCP = "iCCP"; + public static final String sBIT = "sBIT"; + public static final String sRGB = "sRGB"; + public static final String bKGD = "bKGD"; + public static final String hIST = "hIST"; + public static final String tRNS = "tRNS"; + public static final String pHYs = "pHYs"; + public static final String sPLT = "sPLT"; + public static final String tIME = "tIME"; + public static final String iTXt = "iTXt"; + public static final String tEXt = "tEXt"; + public static final String zTXt = "zTXt"; + + private static final ThreadLocal<Inflater> inflaterProvider = new ThreadLocal<Inflater>() { + protected Inflater initialValue() { + return new Inflater(); + } + }; + + private static final ThreadLocal<Deflater> deflaterProvider = new ThreadLocal<Deflater>() { + protected Deflater initialValue() { + return new Deflater(); + } + }; + + /* + * static auxiliary buffer. any method that uses this should synchronize against this + */ + private static byte[] tmpbuffer = new byte[4096]; + + /** + * Converts to bytes using Latin1 (ISO-8859-1) + */ + public static byte[] toBytes(String x) { + return x.getBytes(PngHelperInternal.charsetLatin1); + } + + /** + * Converts to String using Latin1 (ISO-8859-1) + */ + public static String toString(byte[] x) { + return new String(x, PngHelperInternal.charsetLatin1); + } + + /** + * Converts to String using Latin1 (ISO-8859-1) + */ + public static String toString(byte[] x, int offset, int len) { + return new String(x, offset, len, PngHelperInternal.charsetLatin1); + } + + /** + * Converts to bytes using UTF-8 + */ + public static byte[] toBytesUTF8(String x) { + return x.getBytes(PngHelperInternal.charsetUTF8); + } + + /** + * Converts to string using UTF-8 + */ + public static String toStringUTF8(byte[] x) { + return new String(x, PngHelperInternal.charsetUTF8); + } + + /** + * Converts to string using UTF-8 + */ + public static String toStringUTF8(byte[] x, int offset, int len) { + return new String(x, offset, len, PngHelperInternal.charsetUTF8); + } + + /** + * critical chunk : first letter is uppercase + */ + public static boolean isCritical(String id) { + return (Character.isUpperCase(id.charAt(0))); + } + + /** + * public chunk: second letter is uppercase + */ + public static boolean isPublic(String id) { // + return (Character.isUpperCase(id.charAt(1))); + } + + /** + * Safe to copy chunk: fourth letter is lower case + */ + public static boolean isSafeToCopy(String id) { + return (!Character.isUpperCase(id.charAt(3))); + } + + /** + * "Unknown" just means that our chunk factory (even when it has been + * augmented by client code) did not recognize its id + */ + public static boolean isUnknown(PngChunk c) { + return c instanceof PngChunkUNKNOWN; + } + + /** + * Finds position of null byte in array + * + * @param b + * @return -1 if not found + */ + public static int posNullByte(byte[] b) { + for (int i = 0; i < b.length; i++) + if (b[i] == 0) + return i; + return -1; + } + + /** + * Decides if a chunk should be loaded, according to a ChunkLoadBehaviour + * + * @param id + * @param behav + * @return true/false + */ + public static boolean shouldLoad(String id, ChunkLoadBehaviour behav) { + if (isCritical(id)) + return true; + boolean kwown = PngChunk.isKnown(id); + switch (behav) { + case LOAD_CHUNK_ALWAYS: + return true; + case LOAD_CHUNK_IF_SAFE: + return kwown || isSafeToCopy(id); + case LOAD_CHUNK_KNOWN: + return kwown; + case LOAD_CHUNK_NEVER: + return false; + } + return false; // should not reach here + } + + public final static byte[] compressBytes(byte[] ori, boolean compress) { + return compressBytes(ori, 0, ori.length, compress); + } + + public static byte[] compressBytes(byte[] ori, int offset, int len, boolean compress) { + try { + ByteArrayInputStream inb = new ByteArrayInputStream(ori, offset, len); + InputStream in = compress ? inb : new InflaterInputStream(inb, getInflater()); + ByteArrayOutputStream outb = new ByteArrayOutputStream(); + OutputStream out = compress ? new DeflaterOutputStream(outb) : outb; + shovelInToOut(in, out); + in.close(); + out.close(); + return outb.toByteArray(); + } catch (Exception e) { + throw new PngjException(e); + } + } + + /** + * Shovels all data from an input stream to an output stream. + */ + private static void shovelInToOut(InputStream in, OutputStream out) throws IOException { + synchronized (tmpbuffer) { + int len; + while ((len = in.read(tmpbuffer)) > 0) { + out.write(tmpbuffer, 0, len); + } + } + } + + public static boolean maskMatch(int v, int mask) { + return (v & mask) != 0; + } + + /** + * Returns only the chunks that "match" the predicate + * + * See also trimList() + */ + public static List<PngChunk> filterList(List<PngChunk> target, ChunkPredicate predicateKeep) { + List<PngChunk> result = new ArrayList<PngChunk>(); + for (PngChunk element : target) { + if (predicateKeep.match(element)) { + result.add(element); + } + } + return result; + } + + /** + * Remove (in place) the chunks that "match" the predicate + * + * See also filterList + */ + public static int trimList(List<PngChunk> target, ChunkPredicate predicateRemove) { + Iterator<PngChunk> it = target.iterator(); + int cont = 0; + while (it.hasNext()) { + PngChunk c = it.next(); + if (predicateRemove.match(c)) { + it.remove(); + cont++; + } + } + return cont; + } + + /** + * MY adhoc criteria: two chunks are "equivalent" ("practically equal") if + * they have same id and (perhaps, if multiple are allowed) if the match + * also in some "internal key" (eg: key for string values, palette for sPLT, + * etc) + * + * Notice that the use of this is optional, and that the PNG standard allows + * Text chunks that have same key + * + * @return true if "equivalent" + */ + public static final boolean equivalent(PngChunk c1, PngChunk c2) { + if (c1 == c2) + return true; + if (c1 == null || c2 == null || !c1.id.equals(c2.id)) + return false; + // same id + if (c1.getClass() != c2.getClass()) + return false; // should not happen + if (!c2.allowsMultiple()) + return true; + if (c1 instanceof PngChunkTextVar) { + return ((PngChunkTextVar) c1).getKey().equals(((PngChunkTextVar) c2).getKey()); + } + if (c1 instanceof PngChunkSPLT) { + return ((PngChunkSPLT) c1).getPalName().equals(((PngChunkSPLT) c2).getPalName()); + } + // unknown chunks that allow multiple? consider they don't match + return false; + } + + public static boolean isText(PngChunk c) { + return c instanceof PngChunkTextVar; + } + + /** + * thread-local inflater, just reset : this should be only used for short + * individual chunks compression + */ + public static Inflater getInflater() { + Inflater inflater = inflaterProvider.get(); + inflater.reset(); + return inflater; + } + + /** + * thread-local deflater, just reset : this should be only used for short + * individual chunks decompression + */ + public static Deflater getDeflater() { + Deflater deflater = deflaterProvider.get(); + deflater.reset(); + return deflater; + } + +} diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkLoadBehaviour.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkLoadBehaviour.java index 03d50c2c4..82ab3bcf9 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkLoadBehaviour.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkLoadBehaviour.java @@ -1,7 +1,8 @@ package jogamp.opengl.util.pngj.chunks;
/**
- * Defines gral strategy about what to do with ancillary (non-critical) chunks when reading
+ * Defines gral strategy about what to do with ancillary (non-critical) chunks
+ * when reading
*/
public enum ChunkLoadBehaviour {
/**
@@ -9,7 +10,8 @@ public enum ChunkLoadBehaviour { */
LOAD_CHUNK_NEVER,
/**
- * Ancillary chunks are loaded only if 'known' (registered with the factory).
+ * Ancillary chunks are loaded only if 'known' (registered with the
+ * factory).
*/
LOAD_CHUNK_KNOWN,
/**
@@ -19,7 +21,8 @@ public enum ChunkLoadBehaviour { LOAD_CHUNK_IF_SAFE,
/**
* Load all chunks. <br>
- * Notice that other restrictions might apply, see PngReader.skipChunkMaxSize PngReader.skipChunkIds
+ * Notice that other restrictions might apply, see
+ * PngReader.skipChunkMaxSize PngReader.skipChunkIds
*/
LOAD_CHUNK_ALWAYS;
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkRaw.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkRaw.java index 8dd0ef476..3aba26cca 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkRaw.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkRaw.java @@ -13,13 +13,15 @@ import jogamp.opengl.util.pngj.PngjOutputException; /**
* Raw (physical) chunk.
* <p>
- * Short lived object, to be created while serialing/deserializing Do not reuse it for different chunks. <br>
+ * Short lived object, to be created while serialing/deserializing Do not reuse
+ * it for different chunks. <br>
* See http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
*/
public class ChunkRaw {
/**
- * The length counts only the data field, not itself, the chunk type code, or the CRC. Zero is a valid length.
- * Although encoders and decoders should treat the length as unsigned, its value must not exceed 231-1 bytes.
+ * The length counts only the data field, not itself, the chunk type code,
+ * or the CRC. Zero is a valid length. Although encoders and decoders should
+ * treat the length as unsigned, its value must not exceed 231-1 bytes.
*/
public final int len;
@@ -29,12 +31,14 @@ public class ChunkRaw { public final byte[] idbytes = new byte[4];
/**
- * The data bytes appropriate to the chunk type, if any. This field can be of zero length. Does not include crc
+ * The data bytes appropriate to the chunk type, if any. This field can be
+ * of zero length. Does not include crc
*/
public byte[] data = null;
/**
- * A 4-byte CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk, including the chunk type
- * code and chunk data fields, but not including the length field.
+ * A 4-byte CRC (Cyclic Redundancy Check) calculated on the preceding bytes
+ * in the chunk, including the chunk type code and chunk data fields, but
+ * not including the length field.
*/
private int crcval = 0;
@@ -71,7 +75,8 @@ public class ChunkRaw { }
/**
- * Computes the CRC and writes to the stream. If error, a PngjOutputException is thrown
+ * Computes the CRC and writes to the stream. If error, a
+ * PngjOutputException is thrown
*/
public void writeChunk(OutputStream os) {
if (idbytes.length != 4)
@@ -85,8 +90,8 @@ public class ChunkRaw { }
/**
- * position before: just after chunk id. positon after: after crc Data should be already allocated. Checks CRC
- * Return number of byte read.
+ * position before: just after chunk id. positon after: after crc Data
+ * should be already allocated. Checks CRC Return number of byte read.
*/
public int readChunkData(InputStream is, boolean checkCrc) {
PngHelperInternal.readBytes(is, data, 0, len);
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksList.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksList.java index ad788f154..5ce94ff9f 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksList.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksList.java @@ -49,8 +49,8 @@ public class ChunksList { }
/**
- * Returns a copy of the list (but the chunks are not copied) <b> This should not be used for general metadata
- * handling
+ * Returns a copy of the list (but the chunks are not copied) <b> This
+ * should not be used for general metadata handling
*/
public ArrayList<PngChunk> getChunks() {
return new ArrayList<PngChunk>(chunks);
@@ -96,7 +96,8 @@ public class ChunksList { }
/**
- * If innerid!=null and the chunk is PngChunkTextVar or PngChunkSPLT, it's filtered by that id
+ * If innerid!=null and the chunk is PngChunkTextVar or PngChunkSPLT, it's
+ * filtered by that id
*
* @param id
* @return innerid Only used for text and SPLT chunks
@@ -119,8 +120,9 @@ public class ChunksList { /**
* Returns only one chunk or null if nothing found - does not include queued
* <p>
- * If more than one chunk is found, then an exception is thrown (failifMultiple=true or chunk is single) or the last
- * one is returned (failifMultiple=false)
+ * If more than one chunk is found, then an exception is thrown
+ * (failifMultiple=true or chunk is single) or the last one is returned
+ * (failifMultiple=false)
**/
public PngChunk getById1(final String id, final boolean failIfMultiple) {
return getById1(id, null, failIfMultiple);
@@ -129,8 +131,9 @@ public class ChunksList { /**
* Returns only one chunk or null if nothing found - does not include queued
* <p>
- * If more than one chunk (after filtering by inner id) is found, then an exception is thrown (failifMultiple=true
- * or chunk is single) or the last one is returned (failifMultiple=false)
+ * If more than one chunk (after filtering by inner id) is found, then an
+ * exception is thrown (failifMultiple=true or chunk is single) or the last
+ * one is returned (failifMultiple=false)
**/
public PngChunk getById1(final String id, final String innerid, final boolean failIfMultiple) {
List<? extends PngChunk> list = getById(id, innerid);
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java index 204c4c2a5..e76456ad4 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java @@ -13,7 +13,8 @@ import jogamp.opengl.util.pngj.PngjOutputException; public class ChunksListForWrite extends ChunksList { /** - * chunks not yet writen - does not include IHDR, IDAT, END, perhaps yes PLTE + * chunks not yet writen - does not include IHDR, IDAT, END, perhaps yes + * PLTE */ private final List<PngChunk> queuedChunks = new ArrayList<PngChunk>(); @@ -67,8 +68,9 @@ public class ChunksListForWrite extends ChunksList { /** * Remove Chunk: only from queued * - * WARNING: this depends on c.equals() implementation, which is straightforward for SingleChunks. For - * MultipleChunks, it will normally check for reference equality! + * WARNING: this depends on c.equals() implementation, which is + * straightforward for SingleChunks. For MultipleChunks, it will normally + * check for reference equality! */ public boolean removeChunk(PngChunk c) { return queuedChunks.remove(c); @@ -87,7 +89,8 @@ public class ChunksListForWrite extends ChunksList { } /** - * this should be called only for ancillary chunks and PLTE (groups 1 - 3 - 5) + * this should be called only for ancillary chunks and PLTE (groups 1 - 3 - + * 5) **/ private static boolean shouldWrite(PngChunk c, int currentGroup) { if (currentGroup == CHUNK_GROUP_2_PLTE) diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java index 1d630591e..a45979ec2 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java @@ -12,13 +12,16 @@ import jogamp.opengl.util.pngj.PngjExceptionInternal; * Represents a instance of a PNG chunk.
* <p>
* See <a
- * href="http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html">http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks
- * .html</a> </a>
+ * href="http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html">http://www
+ * .libpng.org/pub/png/spec/1.2/PNG-Chunks .html</a> </a>
* <p>
- * Concrete classes should extend {@link PngChunkSingle} or {@link PngChunkMultiple}
+ * Concrete classes should extend {@link PngChunkSingle} or
+ * {@link PngChunkMultiple}
* <p>
- * Note that some methods/fields are type-specific (getOrderingConstraint(), allowsMultiple()),<br>
- * some are 'almost' type-specific (id,crit,pub,safe; the exception is PngUKNOWN), <br>
+ * Note that some methods/fields are type-specific (getOrderingConstraint(),
+ * allowsMultiple()),<br>
+ * some are 'almost' type-specific (id,crit,pub,safe; the exception is
+ * PngUKNOWN), <br>
* and the rest are instance-specific
*/
public abstract class PngChunk {
@@ -35,8 +38,9 @@ public abstract class PngChunk { protected final ImageInfo imgInfo;
/**
- * Possible ordering constraint for a PngChunk type -only relevant for ancillary chunks. Theoretically, there could
- * be more general constraints, but these cover the constraints for standard chunks.
+ * Possible ordering constraint for a PngChunk type -only relevant for
+ * ancillary chunks. Theoretically, there could be more general constraints,
+ * but these cover the constraints for standard chunks.
*/
public enum ChunkOrderingConstraint {
/**
@@ -83,8 +87,8 @@ public abstract class PngChunk { /**
* This static map defines which PngChunk class correspond to which ChunkID
* <p>
- * The client can add other chunks to this map statically, before reading an image, calling
- * PngChunk.factoryRegister(id,class)
+ * The client can add other chunks to this map statically, before reading an
+ * image, calling PngChunk.factoryRegister(id,class)
*/
private final static Map<String, Class<? extends PngChunk>> factoryMap = new HashMap<String, Class<? extends PngChunk>>();
static {
@@ -114,8 +118,9 @@ public abstract class PngChunk { /**
* Registers a chunk-id (4 letters) to be associated with a PngChunk class
* <p>
- * This method should be called by user code that wants to add some chunks (not implmemented in this library) to the
- * factory, so that the PngReader knows about it.
+ * This method should be called by user code that wants to add some chunks
+ * (not implmemented in this library) to the factory, so that the PngReader
+ * knows about it.
*/
public static void factoryRegister(String chunkId, Class<? extends PngChunk> chunkClass) {
factoryMap.put(chunkId, chunkClass);
@@ -124,9 +129,11 @@ public abstract class PngChunk { /**
* True if the chunk-id type is known.
* <p>
- * A chunk is known if we recognize its class, according with <code>factoryMap</code>
+ * A chunk is known if we recognize its class, according with
+ * <code>factoryMap</code>
* <p>
- * This is not necessarily the same as being "STANDARD", or being implemented in this library
+ * This is not necessarily the same as being "STANDARD", or being
+ * implemented in this library
* <p>
* Unknown chunks will be parsed as instances of {@link PngChunkUNKNOWN}
*/
@@ -143,7 +150,8 @@ public abstract class PngChunk { }
/**
- * This factory creates the corresponding chunk and parses the raw chunk. This is used when reading.
+ * This factory creates the corresponding chunk and parses the raw chunk.
+ * This is used when reading.
*/
public static PngChunk factory(ChunkRaw chunk, ImageInfo info) {
PngChunk c = factoryFromId(ChunkHelper.toString(chunk.idbytes), info);
@@ -153,7 +161,8 @@ public abstract class PngChunk { }
/**
- * Creates one new blank chunk of the corresponding type, according to factoryMap (PngChunkUNKNOWN if not known)
+ * Creates one new blank chunk of the corresponding type, according to
+ * factoryMap (PngChunkUNKNOWN if not known)
*/
public static PngChunk factoryFromId(String cid, ImageInfo info) {
PngChunk chunk = null;
@@ -189,7 +198,8 @@ public abstract class PngChunk { }
/**
- * In which "chunkGroup" (see {@link ChunksList}for definition) this chunks instance was read or written.
+ * In which "chunkGroup" (see {@link ChunksList}for definition) this chunks
+ * instance was read or written.
* <p>
* -1 if not read or written (eg, queued)
*/
@@ -236,16 +246,16 @@ public abstract class PngChunk { }
/**
- * Creates the physical chunk. This is used when writing (serialization). Each particular chunk class implements its
- * own logic.
+ * Creates the physical chunk. This is used when writing (serialization).
+ * Each particular chunk class implements its own logic.
*
* @return A newly allocated and filled raw chunk
*/
public abstract ChunkRaw createRawChunk();
/**
- * Parses raw chunk and fill inside data. This is used when reading (deserialization). Each particular chunk class
- * implements its own logic.
+ * Parses raw chunk and fill inside data. This is used when reading
+ * (deserialization). Each particular chunk class implements its own logic.
*/
public abstract void parseFromRaw(ChunkRaw c);
@@ -254,7 +264,8 @@ public abstract class PngChunk { * <p>
* This is used when copying chunks from a reader to a writer
* <p>
- * It should normally be a deep copy, and after the cloning this.equals(other) should return true
+ * It should normally be a deep copy, and after the cloning
+ * this.equals(other) should return true
*/
public abstract void cloneDataFromRead(PngChunk other);
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIDAT.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIDAT.java index b816db205..911513c0d 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIDAT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIDAT.java @@ -7,7 +7,8 @@ import jogamp.opengl.util.pngj.ImageInfo; * <p>
* see http://www.w3.org/TR/PNG/#11IDAT
* <p>
- * This is dummy placeholder - we write/read this chunk (actually several) by special code.
+ * This is dummy placeholder - we write/read this chunk (actually several) by
+ * special code.
*/
public class PngChunkIDAT extends PngChunkMultiple {
public final static String ID = ChunkHelper.IDAT;
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java index 696edd431..d44250a2f 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java @@ -17,7 +17,8 @@ public abstract class PngChunkMultiple extends PngChunk { }
/**
- * NOTE: this chunk uses the default Object's equals() hashCode() implementation.
+ * NOTE: this chunk uses the default Object's equals() hashCode()
+ * implementation.
*
* This is the right thing to do, normally.
*
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java index 286f39db0..5247169e0 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java @@ -3,7 +3,8 @@ package jogamp.opengl.util.pngj.chunks; import jogamp.opengl.util.pngj.ImageInfo;
/**
- * PNG chunk type (abstract) that does not allow multiple instances in same image.
+ * PNG chunk type (abstract) that does not allow multiple instances in same
+ * image.
*/
public abstract class PngChunkSingle extends PngChunk {
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTRNS.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTRNS.java index 1de5c0833..b68776477 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTRNS.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTRNS.java @@ -1,143 +1,141 @@ -package jogamp.opengl.util.pngj.chunks;
-
-import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelperInternal;
-import jogamp.opengl.util.pngj.PngjException;
-
-/**
- * tRNS chunk.
- * <p>
- * see http://www.w3.org/TR/PNG/#11tRNS
- * <p>
- * this chunk structure depends on the image type
- */
-public class PngChunkTRNS extends PngChunkSingle {
- public final static String ID = ChunkHelper.tRNS;
-
- // http://www.w3.org/TR/PNG/#11tRNS
-
- // only one of these is meaningful, depending on the image type
- private int gray;
- private int red, green, blue;
- private int[] paletteAlpha = new int[] {};
-
- public PngChunkTRNS(ImageInfo info) {
- super(ID, info);
- }
-
- @Override
- public ChunkOrderingConstraint getOrderingConstraint() {
- return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT;
- }
-
- @Override
- public ChunkRaw createRawChunk() {
- ChunkRaw c = null;
- if (imgInfo.greyscale) {
- c = createEmptyChunk(2, true);
- PngHelperInternal.writeInt2tobytes(gray, c.data, 0);
- } else if (imgInfo.indexed) {
- c = createEmptyChunk(paletteAlpha.length, true);
- for (int n = 0; n < c.len; n++) {
- c.data[n] = (byte) paletteAlpha[n];
- }
- } else {
- c = createEmptyChunk(6, true);
- PngHelperInternal.writeInt2tobytes(red, c.data, 0);
- PngHelperInternal.writeInt2tobytes(green, c.data, 0);
- PngHelperInternal.writeInt2tobytes(blue, c.data, 0);
- }
- return c;
- }
-
- @Override
- public void parseFromRaw(ChunkRaw c) {
- if (imgInfo.greyscale) {
- gray = PngHelperInternal.readInt2fromBytes(c.data, 0);
- } else if (imgInfo.indexed) {
- int nentries = c.data.length;
- paletteAlpha = new int[nentries];
- for (int n = 0; n < nentries; n++) {
- paletteAlpha[n] = (int) (c.data[n] & 0xff);
- }
- } else {
- red = PngHelperInternal.readInt2fromBytes(c.data, 0);
- green = PngHelperInternal.readInt2fromBytes(c.data, 2);
- blue = PngHelperInternal.readInt2fromBytes(c.data, 4);
- }
- }
-
- @Override
- public void cloneDataFromRead(PngChunk other) {
- PngChunkTRNS otherx = (PngChunkTRNS) other;
- gray = otherx.gray;
- red = otherx.red;
- green = otherx.red;
- blue = otherx.red;
- if (otherx.paletteAlpha != null) {
- paletteAlpha = new int[otherx.paletteAlpha.length];
- System.arraycopy(otherx.paletteAlpha, 0, paletteAlpha, 0, paletteAlpha.length);
- }
- }
-
- /**
- * Set rgb values
- *
- */
- public void setRGB(int r, int g, int b) {
- if (imgInfo.greyscale || imgInfo.indexed)
- throw new PngjException("only rgb or rgba images support this");
- red = r;
- green = g;
- blue = b;
- }
-
- public int[] getRGB() {
- if (imgInfo.greyscale || imgInfo.indexed)
- throw new PngjException("only rgb or rgba images support this");
- return new int[] { red, green, blue };
- }
-
- public void setGray(int g) {
- if (!imgInfo.greyscale)
- throw new PngjException("only grayscale images support this");
- gray = g;
- }
-
- public int getGray() {
- if (!imgInfo.greyscale)
- throw new PngjException("only grayscale images support this");
- return gray;
- }
-
- /**
- * WARNING: non deep copy
- */
- public void setPalletteAlpha(int[] palAlpha) {
- if (!imgInfo.indexed)
- throw new PngjException("only indexed images support this");
- paletteAlpha = palAlpha;
- }
-
- /**
- * to use when only one pallete index is set as totally transparent
- */
- public void setIndexEntryAsTransparent(int palAlphaIndex) {
- if (!imgInfo.indexed)
- throw new PngjException("only indexed images support this");
- paletteAlpha = new int[] { palAlphaIndex + 1 };
- for (int i = 0; i < palAlphaIndex; i++)
- paletteAlpha[i] = 255;
- paletteAlpha[palAlphaIndex] = 0;
- }
-
- /**
- * WARNING: non deep copy
- */
- public int[] getPalletteAlpha() {
- if (!imgInfo.indexed)
- throw new PngjException("only indexed images support this");
- return paletteAlpha;
- }
-
-}
+package jogamp.opengl.util.pngj.chunks; + +import jogamp.opengl.util.pngj.ImageInfo; +import jogamp.opengl.util.pngj.PngHelperInternal; +import jogamp.opengl.util.pngj.PngjException; + +/** + * tRNS chunk. + * <p> + * see http://www.w3.org/TR/PNG/#11tRNS + * <p> + * this chunk structure depends on the image type + */ +public class PngChunkTRNS extends PngChunkSingle { + public final static String ID = ChunkHelper.tRNS; + + // http://www.w3.org/TR/PNG/#11tRNS + + // only one of these is meaningful, depending on the image type + private int gray; + private int red, green, blue; + private int[] paletteAlpha = new int[] {}; + + public PngChunkTRNS(ImageInfo info) { + super(ID, info); + } + + @Override + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT; + } + + @Override + public ChunkRaw createRawChunk() { + ChunkRaw c = null; + if (imgInfo.greyscale) { + c = createEmptyChunk(2, true); + PngHelperInternal.writeInt2tobytes(gray, c.data, 0); + } else if (imgInfo.indexed) { + c = createEmptyChunk(paletteAlpha.length, true); + for (int n = 0; n < c.len; n++) { + c.data[n] = (byte) paletteAlpha[n]; + } + } else { + c = createEmptyChunk(6, true); + PngHelperInternal.writeInt2tobytes(red, c.data, 0); + PngHelperInternal.writeInt2tobytes(green, c.data, 0); + PngHelperInternal.writeInt2tobytes(blue, c.data, 0); + } + return c; + } + + @Override + public void parseFromRaw(ChunkRaw c) { + if (imgInfo.greyscale) { + gray = PngHelperInternal.readInt2fromBytes(c.data, 0); + } else if (imgInfo.indexed) { + int nentries = c.data.length; + paletteAlpha = new int[nentries]; + for (int n = 0; n < nentries; n++) { + paletteAlpha[n] = (int) (c.data[n] & 0xff); + } + } else { + red = PngHelperInternal.readInt2fromBytes(c.data, 0); + green = PngHelperInternal.readInt2fromBytes(c.data, 2); + blue = PngHelperInternal.readInt2fromBytes(c.data, 4); + } + } + + @Override + public void cloneDataFromRead(PngChunk other) { + PngChunkTRNS otherx = (PngChunkTRNS) other; + gray = otherx.gray; + red = otherx.red; + green = otherx.green; + blue = otherx.blue; + if (otherx.paletteAlpha != null) { + paletteAlpha = new int[otherx.paletteAlpha.length]; + System.arraycopy(otherx.paletteAlpha, 0, paletteAlpha, 0, paletteAlpha.length); + } + } + + /** + * Set rgb values + * + */ + public void setRGB(int r, int g, int b) { + if (imgInfo.greyscale || imgInfo.indexed) + throw new PngjException("only rgb or rgba images support this"); + red = r; + green = g; + blue = b; + } + + public int[] getRGB() { + if (imgInfo.greyscale || imgInfo.indexed) + throw new PngjException("only rgb or rgba images support this"); + return new int[] { red, green, blue }; + } + + public void setGray(int g) { + if (!imgInfo.greyscale) + throw new PngjException("only grayscale images support this"); + gray = g; + } + + public int getGray() { + if (!imgInfo.greyscale) + throw new PngjException("only grayscale images support this"); + return gray; + } + + /** + * WARNING: non deep copy + */ + public void setPalletteAlpha(int[] palAlpha) { + if (!imgInfo.indexed) + throw new PngjException("only indexed images support this"); + paletteAlpha = palAlpha; + } + + /** + * to use when only one pallete index is set as totally transparent + */ + public void setIndexEntryAsTransparent(int palAlphaIndex) { + if (!imgInfo.indexed) + throw new PngjException("only indexed images support this"); + paletteAlpha = new int[] { palAlphaIndex + 1 }; + for (int i = 0; i < palAlphaIndex; i++) + paletteAlpha[i] = 255; + paletteAlpha[palAlphaIndex] = 0; + } + + /** + * WARNING: non deep copy + */ + public int[] getPalletteAlpha() { + return paletteAlpha; + } + +} diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngMetadata.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngMetadata.java index 52d1b22c1..ecf8b98c3 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngMetadata.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngMetadata.java @@ -7,13 +7,13 @@ import jogamp.opengl.util.pngj.PngHelperInternal; import jogamp.opengl.util.pngj.PngjException; /** - * We consider "image metadata" every info inside the image except for the most basic image info (IHDR chunk - ImageInfo - * class) and the pixels values. + * We consider "image metadata" every info inside the image except for the most + * basic image info (IHDR chunk - ImageInfo class) and the pixels values. * <p> * This includes the palette (if present) and all the ancillary chunks * <p> - * This class provides a wrapper over the collection of chunks of a image (read or to write) and provides some high - * level methods to access them + * This class provides a wrapper over the collection of chunks of a image (read + * or to write) and provides some high level methods to access them */ public class PngMetadata { private final ChunksList chunkList; @@ -31,8 +31,9 @@ public class PngMetadata { /** * Queues the chunk at the writer * <p> - * lazyOverwrite: if true, checks if there is a queued "equivalent" chunk and if so, overwrites it. However if that - * not check for already written chunks. + * lazyOverwrite: if true, checks if there is a queued "equivalent" chunk + * and if so, overwrites it. However if that not check for already written + * chunks. */ public void queueChunk(final PngChunk c, boolean lazyOverwrite) { ChunksListForWrite cl = getChunkListW(); @@ -87,7 +88,8 @@ public class PngMetadata { * Creates a time chunk with current time, less secsAgo seconds * <p> * - * @return Returns the created-queued chunk, just in case you want to examine or modify it + * @return Returns the created-queued chunk, just in case you want to + * examine or modify it */ public PngChunkTIME setTimeNow(int secsAgo) { PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); @@ -104,7 +106,8 @@ public class PngMetadata { * Creates a time chunk with diven date-time * <p> * - * @return Returns the created-queued chunk, just in case you want to examine or modify it + * @return Returns the created-queued chunk, just in case you want to + * examine or modify it */ public PngChunkTIME setTimeYMDHMS(int yearx, int monx, int dayx, int hourx, int minx, int secx) { PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); @@ -137,7 +140,8 @@ public class PngMetadata { * (arbitrary, should be latin1 if useLatin1) * @param useLatin1 * @param compress - * @return Returns the created-queued chunks, just in case you want to examine, touch it + * @return Returns the created-queued chunks, just in case you want to + * examine, touch it */ public PngChunkTextVar setText(String k, String val, boolean useLatin1, boolean compress) { if (compress && !useLatin1) @@ -180,7 +184,8 @@ public class PngMetadata { } /** - * Returns empty if not found, concatenated (with newlines) if multiple! - and trimmed + * Returns empty if not found, concatenated (with newlines) if multiple! - + * and trimmed * <p> * Use getTxtsForKey() if you don't want this behaviour */ @@ -204,7 +209,8 @@ public class PngMetadata { } /** - * Creates a new empty palette chunk, queues it for write and return it to the caller, who should fill its entries + * Creates a new empty palette chunk, queues it for write and return it to + * the caller, who should fill its entries */ public PngChunkPLTE createPLTEChunk() { PngChunkPLTE plte = new PngChunkPLTE(chunkList.imageInfo); @@ -222,7 +228,8 @@ public class PngMetadata { } /** - * Creates a new empty TRNS chunk, queues it for write and return it to the caller, who should fill its entries + * Creates a new empty TRNS chunk, queues it for write and return it to the + * caller, who should fill its entries */ public PngChunkTRNS createTRNSChunk() { PngChunkTRNS trns = new PngChunkTRNS(chunkList.imageInfo); diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java index 4444e9d63..bf2d3fa47 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java @@ -80,7 +80,7 @@ public class WGLGLCapabilities extends GLCapabilities { public static final String PFD2String(PIXELFORMATDESCRIPTOR pfd, int pfdID) { final int dwFlags = pfd.getDwFlags(); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); boolean sep = false; if( 0 != (GDI.PFD_DRAW_TO_WINDOW & dwFlags ) ) { @@ -270,4 +270,4 @@ public class WGLGLCapabilities extends GLCapabilities { sink.append(": "); return super.toString(sink); } -}
\ No newline at end of file +} diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLUtil.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLUtil.java index f1598d580..3e788d286 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLUtil.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLUtil.java @@ -51,6 +51,7 @@ public class WGLUtil { public static final boolean USE_WGLVersion_Of_5WGLGDIFuncSet; static { + Debug.initSingleton(); USE_WGLVersion_Of_5WGLGDIFuncSet = Debug.isPropertyDefined("jogl.windows.useWGLVersionOf5WGLGDIFuncSet", true); if(USE_WGLVersion_Of_5WGLGDIFuncSet) { System.err.println("Use WGL version of 5 WGL/GDI functions."); diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java index 94153d96d..b8979c91e 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java @@ -563,7 +563,13 @@ public class WindowsWGLContext extends GLContextImpl { } @Override - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { - return getWGLExt().wglAllocateMemoryNV(arg0, arg1, arg2, arg3); + public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) { + return getWGLExt().wglAllocateMemoryNV(size, readFrequency, writeFrequency, priority); } + + @Override + public final void glFreeMemoryNV(ByteBuffer pointer) { + getWGLExt().wglFreeMemoryNV(pointer); + } + } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawable.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawable.java index 8b8cb2052..741e671eb 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawable.java @@ -51,7 +51,13 @@ import jogamp.opengl.GLDynamicLookupHelper; public abstract class WindowsWGLDrawable extends GLDrawableImpl { - private static final boolean PROFILING = Debug.isPropertyDefined("jogl.debug.GLDrawable.profiling", true); + private static final boolean PROFILING; + + static { + Debug.initSingleton(); + PROFILING = Debug.isPropertyDefined("jogl.debug.GLDrawable.profiling", true); + } + private static final int PROFILING_TICKS = 200; private int profilingSwapBuffersTicks; private long profilingSwapBuffersTime; diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java index 5fb01d1a3..338a351cb 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java @@ -43,6 +43,8 @@ package jogamp.opengl.windows.wgl; import java.nio.Buffer; import java.nio.ShortBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -88,19 +90,24 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { super(); synchronized(WindowsWGLDrawableFactory.class) { - if(null==windowsWGLDynamicLookupHelper) { - DesktopGLDynamicLookupHelper tmp = null; - try { - tmp = new DesktopGLDynamicLookupHelper(new WindowsWGLDynamicLibraryBundleInfo()); - } catch (GLException gle) { - if(DEBUG) { - gle.printStackTrace(); + if( null == windowsWGLDynamicLookupHelper ) { + windowsWGLDynamicLookupHelper = AccessController.doPrivileged(new PrivilegedAction<DesktopGLDynamicLookupHelper>() { + public DesktopGLDynamicLookupHelper run() { + DesktopGLDynamicLookupHelper tmp; + try { + tmp = new DesktopGLDynamicLookupHelper(new WindowsWGLDynamicLibraryBundleInfo()); + if(null!=tmp && tmp.isLibComplete()) { + WGL.getWGLProcAddressTable().reset(tmp); + } + } catch (Exception ex) { + tmp = null; + if(DEBUG) { + ex.printStackTrace(); + } + } + return tmp; } - } - if(null!=tmp && tmp.isLibComplete()) { - windowsWGLDynamicLookupHelper = tmp; - WGL.getWGLProcAddressTable().reset(windowsWGLDynamicLookupHelper); - } + } ); } } @@ -463,7 +470,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } @Override - public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { + public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { SharedResource sr = getOrCreateSharedResourceImpl( ( null != device ) ? device : defaultDevice ); if(null!=sr) { return sr.hasARBPBuffer(); diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java index a553bd4c2..7ec6c50f8 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java @@ -31,13 +31,13 @@ package jogamp.opengl.windows.wgl; import jogamp.opengl.*; import java.util.*; -public class WindowsWGLDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundleInfo { +public final class WindowsWGLDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundleInfo { protected WindowsWGLDynamicLibraryBundleInfo() { super(); } @Override - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> libsGL = new ArrayList<String>(); libsGL.add("OpenGL32"); diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java index c37bcee50..4bfe0cb86 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java @@ -524,16 +524,6 @@ public class X11GLXContext extends GLContextImpl { } @Override - public boolean isExtensionAvailable(String glExtensionName) { - if (glExtensionName.equals(GLExtensions.ARB_pbuffer) || - glExtensionName.equals(GLExtensions.ARB_pixel_format)) { - return getGLDrawable().getFactory().canCreateGLPbuffer( - drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice() ); - } - return super.isExtensionAvailable(glExtensionName); - } - - @Override protected boolean setSwapIntervalImpl(int interval) { if( !drawable.getChosenGLCapabilities().isOnscreen() ) { return false; } @@ -633,11 +623,16 @@ public class X11GLXContext extends GLContextImpl { } @Override - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { - return getGLXExt().glXAllocateMemoryNV(arg0, arg1, arg2, arg3); + public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) { + return getGLXExt().glXAllocateMemoryNV(size, readFrequency, writeFrequency, priority); } @Override + public final void glFreeMemoryNV(ByteBuffer pointer) { + getGLXExt().glXFreeMemoryNV(pointer); + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java index 394293bc0..52069b88f 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java @@ -39,6 +39,8 @@ package jogamp.opengl.x11.glx; import java.nio.Buffer; import java.nio.ShortBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -91,19 +93,24 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { super(); synchronized(X11GLXDrawableFactory.class) { - if(null==x11GLXDynamicLookupHelper) { - DesktopGLDynamicLookupHelper tmp = null; - try { - tmp = new DesktopGLDynamicLookupHelper(new X11GLXDynamicLibraryBundleInfo()); - } catch (GLException gle) { - if(DEBUG) { - gle.printStackTrace(); + if( null == x11GLXDynamicLookupHelper ) { + x11GLXDynamicLookupHelper = AccessController.doPrivileged(new PrivilegedAction<DesktopGLDynamicLookupHelper>() { + public DesktopGLDynamicLookupHelper run() { + DesktopGLDynamicLookupHelper tmp; + try { + tmp = new DesktopGLDynamicLookupHelper(new X11GLXDynamicLibraryBundleInfo()); + if(null!=tmp && tmp.isLibComplete()) { + GLX.getGLXProcAddressTable().reset(tmp); + } + } catch (Exception ex) { + tmp = null; + if(DEBUG) { + ex.printStackTrace(); + } + } + return tmp; } - } - if(null!=tmp && tmp.isLibComplete()) { - x11GLXDynamicLookupHelper = tmp; - GLX.getGLXProcAddressTable().reset(x11GLXDynamicLookupHelper); - } + } ); } } @@ -483,7 +490,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } @Override - public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { + public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { if(null == device) { SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(defaultDevice); if(null!=sr) { @@ -544,7 +551,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { @Override public final boolean canCreateExternalGLDrawable(AbstractGraphicsDevice device) { - return canCreateGLPbuffer(device); + return canCreateGLPbuffer(device, null /* GLProfile not used for query on X11 */); } @Override diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java index 108c157a8..f25f7ae2c 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java @@ -31,13 +31,13 @@ package jogamp.opengl.x11.glx; import jogamp.opengl.*; import java.util.*; -public class X11GLXDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundleInfo { +public final class X11GLXDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundleInfo { protected X11GLXDynamicLibraryBundleInfo() { super(); } @Override - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> libsGL = new ArrayList<String>(); @@ -60,15 +60,6 @@ public class X11GLXDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundl return libsList; } - /** - * This respects old DRI requirements:<br> - * <pre> - * http://dri.sourceforge.net/doc/DRIuserguide.html - * </pre> - */ - @Override - public boolean shallLinkGlobal() { return true; } - @Override public final List<String> getToolGetProcAddressFuncNameList() { List<String> res = new ArrayList<String>(); |