diff options
Diffstat (limited to 'src/jogl/classes/jogamp/opengl')
268 files changed, 26849 insertions, 12203 deletions
diff --git a/src/jogl/classes/jogamp/opengl/Debug.java b/src/jogl/classes/jogamp/opengl/Debug.java index 4287c1960..9332b670a 100644 --- a/src/jogl/classes/jogamp/opengl/Debug.java +++ b/src/jogl/classes/jogamp/opengl/Debug.java @@ -1,21 +1,22 @@ /* - * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,17 +29,20 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl; +import java.security.AccessController; +import java.security.PrivilegedAction; + import com.jogamp.common.util.PropertyAccess; /** Helper routines for logging and debugging. */ @@ -47,9 +51,15 @@ public class Debug extends PropertyAccess { // Some common properties private static final boolean verbose; private static final boolean debugAll; - + static { - PropertyAccess.addTrustedPrefix("jogl.", Debug.class); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + PropertyAccess.addTrustedPrefix("jogl."); + return null; + } } ); + verbose = isPropertyDefined("jogl.verbose", true); debugAll = isPropertyDefined("jogl.debug", true); if (verbose) { @@ -60,27 +70,18 @@ public class Debug extends PropertyAccess { } } - 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..578f416b7 100644 --- a/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,19 +20,19 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.opengl; 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>(); @@ -47,11 +47,11 @@ public abstract class DesktopGLDynamicLibraryBundleInfo extends GLDynamicLibrary public final List<String> getGlueLibNames() { return glueLibNames; } - + @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..c1e1d1821 100644 --- a/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLookupHelper.java +++ b/src/jogl/classes/jogamp/opengl/DesktopGLDynamicLookupHelper.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.opengl; import com.jogamp.common.os.NativeLibrary; @@ -37,9 +37,10 @@ public class DesktopGLDynamicLookupHelper extends GLDynamicLookupHelper { super(info); } - public DesktopGLDynamicLibraryBundleInfo getDesktopGLBundleInfo() { return (DesktopGLDynamicLibraryBundleInfo) getBundleInfo(); } + public final DesktopGLDynamicLibraryBundleInfo getDesktopGLBundleInfo() { return (DesktopGLDynamicLibraryBundleInfo) getBundleInfo(); } - public synchronized boolean loadGLULibrary() { + @Override + 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 610f08e21..fd59ecfd4 100644 --- a/src/jogl/classes/jogamp/opengl/ExtensionAvailabilityCache.java +++ b/src/jogl/classes/jogamp/opengl/ExtensionAvailabilityCache.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,20 +29,25 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl; -import javax.media.opengl.*; +import java.util.HashMap; +import java.util.StringTokenizer; -import java.util.*; +import javax.media.opengl.GL; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; + +import com.jogamp.common.util.VersionNumber; /** * A utility object intended to be used by implementations to act as a cache @@ -57,7 +62,7 @@ final class ExtensionAvailabilityCache { } /** - * Flush the cache. + * Flush the cache. */ final void flush() { @@ -78,24 +83,24 @@ final class ExtensionAvailabilityCache { } final boolean isInitialized() { - return initialized && !availableExtensionCache.isEmpty() ; + return initialized; } final int getTotalExtensionCount() { validateInitialization(); return availableExtensionCache.size(); } - + final boolean isExtensionAvailable(String glExtensionName) { validateInitialization(); - return availableExtensionCache.contains(glExtensionName); + return null != availableExtensionCache.get(glExtensionName); } final int getPlatformExtensionCount() { validateInitialization(); return glXExtensionCount; } - + final String getPlatformExtensionsString() { validateInitialization(); return glXExtensions; @@ -105,7 +110,7 @@ final class ExtensionAvailabilityCache { validateInitialization(); return glExtensionCount; } - + final String getGLExtensionsString() { validateInitialization(); if(DEBUG) { @@ -149,7 +154,6 @@ final class ExtensionAvailabilityCache { ", use "+ ( useGetStringi ? "glGetStringi" : "glGetString" ) ); } - HashSet<String> glExtensionSet = new HashSet<String>(gl.isGLES() ? 50 : 320); // far less gl extension expected on mobile if(useGetStringi) { GL2GL3 gl2gl3 = gl.getGL2GL3(); final int count; @@ -160,78 +164,84 @@ final class ExtensionAvailabilityCache { } StringBuilder sb = new StringBuilder(); for (int i = 0; i < count; i++) { - if(i > 0) { - sb.append(" "); - } final String ext = gl2gl3.glGetStringi(GL.GL_EXTENSIONS, i); - glExtensionSet.add(ext); - sb.append(ext); + if( null == availableExtensionCache.put(ext, ext) ) { + // new one + if( 0 < i ) { + sb.append(" "); + } + sb.append(ext); + } } if(0==count || sb.length()==0) { // fall back .. useGetStringi=false; } else { glExtensions = sb.toString(); + glExtensionCount = count; } } if(!useGetStringi) { glExtensions = gl.glGetString(GL.GL_EXTENSIONS); if(null != glExtensions) { - StringTokenizer tok = new StringTokenizer(glExtensions); + final StringTokenizer tok = new StringTokenizer(glExtensions); + int count = 0; while (tok.hasMoreTokens()) { - glExtensionSet.add(tok.nextToken().trim()); + final String ext = tok.nextToken().trim(); + if( null == availableExtensionCache.put(ext, ext) ) { + count++; + } } + glExtensionCount = count; } } - glExtensionCount = glExtensionSet.size(); if (DEBUG) { System.err.println(getThreadName() + ":ExtensionAvailabilityCache: GL_EXTENSIONS: "+glExtensionCount+ ", used "+ ( useGetStringi ? "glGetStringi" : "glGetString" ) ); } // Platform Extensions - HashSet<String> glXExtensionSet = new HashSet<String>(50); - { - // unify platform extension .. might have duplicates - StringTokenizer tok = new StringTokenizer(context.getPlatformExtensionsStringImpl().toString()); - while (tok.hasMoreTokens()) { - glXExtensionSet.add(tok.nextToken().trim()); - } + { + // unify platform extension .. might have duplicates final StringBuilder sb = new StringBuilder(); - for(Iterator<String> iter = glXExtensionSet.iterator(); iter.hasNext(); ) { - sb.append(iter.next()); - if(iter.hasNext()) { - sb.append(" "); + final StringTokenizer tok = new StringTokenizer(context.getPlatformExtensionsStringImpl().toString()); + int count = 0; + while (tok.hasMoreTokens()) { + final String ext = tok.nextToken().trim(); + if( null == availableExtensionCache.put(ext, ext) ) { + // new one + if( 0 < count ) { + sb.append(" "); + } + sb.append(ext); + count++; } } glXExtensions = sb.toString(); - glXExtensionCount = glXExtensionSet.size(); + glXExtensionCount = count; } - availableExtensionCache.addAll(glExtensionSet); - availableExtensionCache.addAll(glXExtensionSet); - if (DEBUG) { System.err.println(getThreadName() + ":ExtensionAvailabilityCache: GLX_EXTENSIONS: "+glXExtensionCount); System.err.println(getThreadName() + ":ExtensionAvailabilityCache: GL vendor: " + gl.glGetString(GL.GL_VENDOR)); System.err.println(getThreadName() + ":ExtensionAvailabilityCache: ALL EXTENSIONS: "+availableExtensionCache.size()); } - if(!context.isGLES()) { - int major[] = new int[] { context.getGLVersionMajor() }; - int minor[] = new int[] { context.getGLVersionMinor() }; - 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() }; + do{ + final String GL_XX_VERSION = ( context.isGLES() ? "GL_ES_VERSION_" : "GL_VERSION_" ) + major[0] + "_" + minor[0]; + availableExtensionCache.put(GL_XX_VERSION, GL_XX_VERSION); + if (DEBUG) { + System.err.println(getThreadName() + ":ExtensionAvailabilityCache: Added "+GL_XX_VERSION+" to known extensions"); } - } + } while( GLContext.decrementGLVersion(ctxOptions, major, minor) ); // put a dummy var in here so that the cache is no longer empty even if // no extensions are in the GL_EXTENSIONS string - availableExtensionCache.add("<INTERNAL_DUMMY_PLACEHOLDER>"); + availableExtensionCache.put("<INTERNAL_DUMMY_PLACEHOLDER>", "<INTERNAL_DUMMY_PLACEHOLDER>"); initialized = true; } @@ -245,10 +255,8 @@ final class ExtensionAvailabilityCache { private int glExtensionCount = 0; private String glXExtensions = null; private int glXExtensionCount = 0; - private HashSet<String> availableExtensionCache = new HashSet<String>(50); + private final HashMap<String, String> availableExtensionCache = new HashMap<String, String>(100); - static String getThreadName() { - return Thread.currentThread().getName(); - } + static String getThreadName() { return Thread.currentThread().getName(); } } diff --git a/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java b/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java index 27569d210..08a1fe882 100644 --- a/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java +++ b/src/jogl/classes/jogamp/opengl/FPSCounterImpl.java @@ -41,39 +41,39 @@ public class FPSCounterImpl implements FPSCounter { private long fpsStartTime, fpsLastUpdateTime, fpsLastPeriod, fpsTotalDuration; private int fpsTotalFrames; private float fpsLast, fpsTotal; - + /** Creates a disabled instance */ public FPSCounterImpl() { setUpdateFPSFrames(0, null); } - + /** * Increases total frame count and updates values if feature is enabled and * update interval is reached.<br> - * + * * Shall be called by actual FPSCounter implementing renderer, after display a new frame. - * + * */ public final synchronized void tickFPS() { fpsTotalFrames++; if(fpsUpdateFramesInterval>0 && fpsTotalFrames%fpsUpdateFramesInterval == 0) { final long now = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); fpsLastPeriod = now - fpsLastUpdateTime; - fpsLastPeriod = Math.max(fpsLastPeriod, 1); // div 0 - fpsLast = ( (float)fpsUpdateFramesInterval * 1000f ) / ( (float) fpsLastPeriod ) ; - + fpsLastPeriod = Math.max(fpsLastPeriod, 1); // div 0 + fpsLast = ( (float)fpsUpdateFramesInterval * 1000f ) / ( (float) fpsLastPeriod ) ; + fpsTotalDuration = now - fpsStartTime; fpsTotalDuration = Math.max(fpsTotalDuration, 1); // div 0 fpsTotal= ( (float)fpsTotalFrames * 1000f ) / ( (float) fpsTotalDuration ) ; - + if(null != fpsOutputStream) { fpsOutputStream.println(toString()); } - + fpsLastUpdateTime = now; } } - + public StringBuilder toString(StringBuilder sb) { if(null==sb) { sb = new StringBuilder(); @@ -81,59 +81,71 @@ public class FPSCounterImpl implements FPSCounter { String fpsLastS = String.valueOf(fpsLast); fpsLastS = fpsLastS.substring(0, fpsLastS.indexOf('.') + 2); String fpsTotalS = String.valueOf(fpsTotal); - fpsTotalS = fpsTotalS.substring(0, fpsTotalS.indexOf('.') + 2); + fpsTotalS = fpsTotalS.substring(0, fpsTotalS.indexOf('.') + 2); sb.append(fpsTotalDuration/1000 +" s: "+ fpsUpdateFramesInterval+" f / "+ fpsLastPeriod+" ms, " + fpsLastS+" fps, "+ fpsLastPeriod/fpsUpdateFramesInterval+" ms/f; "+ "total: "+ fpsTotalFrames+" f, "+ fpsTotalS+ " fps, "+ fpsTotalDuration/fpsTotalFrames+" ms/f"); return sb; } - + + @Override public String toString() { return toString(null).toString(); } - + + @Override public final synchronized void setUpdateFPSFrames(int frames, PrintStream out) { fpsUpdateFramesInterval = frames; fpsOutputStream = out; resetFPSCounter(); } - + + @Override public final synchronized void resetFPSCounter() { fpsStartTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); // overwrite startTime to real init one fpsLastUpdateTime = fpsStartTime; fpsLastPeriod = 0; fpsTotalFrames = 0; fpsLast = 0f; fpsTotal = 0f; + fpsLastPeriod = 0; fpsTotalDuration=0; } + @Override public final synchronized int getUpdateFPSFrames() { return fpsUpdateFramesInterval; } - - public final synchronized long getFPSStartTime() { - return fpsStartTime; + + @Override + public final synchronized long getFPSStartTime() { + return fpsStartTime; } + @Override public final synchronized long getLastFPSUpdateTime() { return fpsLastUpdateTime; } + @Override public final synchronized long getLastFPSPeriod() { return fpsLastPeriod; } - + + @Override public final synchronized float getLastFPS() { return fpsLast; } - - public final synchronized int getTotalFPSFrames() { - return fpsTotalFrames; + + @Override + public final synchronized int getTotalFPSFrames() { + return fpsTotalFrames; } - public final synchronized long getTotalFPSDuration() { - return fpsTotalDuration; + @Override + public final synchronized long getTotalFPSDuration() { + return fpsTotalDuration; } - + + @Override public final synchronized float getTotalFPS() { return fpsTotal; - } + } } diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java new file mode 100644 index 000000000..7cd685d5a --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -0,0 +1,739 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl; + +import java.io.PrintStream; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; +import javax.media.opengl.FPSCounter; +import javax.media.opengl.GL; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLOffscreenAutoDrawable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLSharedContextSetter; + +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.opengl.GLAutoDrawableDelegate; +import com.jogamp.opengl.GLEventListenerState; +import com.jogamp.opengl.GLStateKeeper; + + +/** + * Abstract common code for GLAutoDrawable implementations. + * + * @see GLAutoDrawable + * @see GLAutoDrawableDelegate + * @see GLOffscreenAutoDrawable + * @see GLOffscreenAutoDrawableImpl + * @see GLPBufferImpl + * @see com.jogamp.newt.opengl.GLWindow + */ +public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeeper, FPSCounter, GLSharedContextSetter { + public static final boolean DEBUG = GLDrawableImpl.DEBUG; + protected final GLDrawableHelper helper = new GLDrawableHelper(); + protected final FPSCounterImpl fpsCounter = new FPSCounterImpl(); + + protected volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access + protected volatile GLContextImpl context; + protected boolean preserveGLELSAtDestroy; + protected GLEventListenerState glels; + protected GLStateKeeper.Listener glStateKeeperListener; + protected final boolean ownsDevice; + protected int additionalCtxCreationFlags = 0; + protected volatile boolean sendReshape = false; // volatile: maybe written by WindowManager thread w/o locking + protected volatile boolean sendDestroy = false; // volatile: maybe written by WindowManager thread w/o locking + + /** + * <p> + * The {@link GLContext} can be assigned later manually via {@link GLAutoDrawable#setContext(GLContext, boolean) setContext(ctx)} + * <i>or</i> it will be created <i>lazily</i> at the 1st {@link GLAutoDrawable#display() display()} method call.<br> + * <i>Lazy</i> {@link GLContext} creation will take a shared {@link GLContext} into account + * which has been set {@link #setSharedContext(GLContext) directly} + * or {@link #setSharedAutoDrawable(GLAutoDrawable) via another GLAutoDrawable}. + * </p> + * @param drawable upstream {@link GLDrawableImpl} instance, + * may be <code>null</code> for lazy initialization + * @param context upstream {@link GLContextImpl} instance, + * may not have been made current (created) yet, + * may not be associated w/ <code>drawable<code> yet, + * may be <code>null</code> for lazy initialization at 1st {@link #display()}. + * @param ownsDevice pass <code>true</code> if {@link AbstractGraphicsDevice#close()} shall be issued, + * otherwise pass <code>false</code>. Closing the device is required in case + * the drawable is created w/ it's own new instance, e.g. offscreen drawables, + * and no further lifecycle handling is applied. + */ + public GLAutoDrawableBase(GLDrawableImpl drawable, GLContextImpl context, boolean ownsDevice) { + this.drawable = drawable; + this.context = context; + this.preserveGLELSAtDestroy = false; + this.glels = null; + this.glStateKeeperListener = null; + this.ownsDevice = ownsDevice; + if(null != context && null != drawable) { + context.setGLDrawable(drawable, false); + } + resetFPSCounter(); + } + + @Override + public final void setSharedContext(GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.context, sharedContext); + } + + @Override + public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + + /** Returns the recursive lock object of the upstream implementation, which synchronizes multithreaded access on top of {@link NativeSurface#lockSurface()}. */ + protected abstract RecursiveLock getLock(); + + @Override + public final GLStateKeeper.Listener setGLStateKeeperListener(Listener l) { + final GLStateKeeper.Listener pre = glStateKeeperListener; + glStateKeeperListener = l; + return pre; + } + + @Override + public final boolean preserveGLStateAtDestroy(boolean value) { + final boolean res = isGLStatePreservationSupported() ? true : false; + if( res ) { + if( DEBUG ) { + final long surfaceHandle = null != getNativeSurface() ? getNativeSurface().getSurfaceHandle() : 0; + System.err.println("GLAutoDrawableBase.setPreserveGLStateAtDestroy: ("+getThreadName()+"): "+preserveGLELSAtDestroy+" -> "+value+" - surfaceHandle 0x"+Long.toHexString(surfaceHandle)); + } + preserveGLELSAtDestroy = value; + } + return res; + } + + @Override + public boolean isGLStatePreservationSupported() { return false; } + + @Override + public final GLEventListenerState getPreservedGLState() { + return glels; + } + + @Override + public final GLEventListenerState clearPreservedGLState() { + final GLEventListenerState r = glels; + glels = null; + return r; + } + + /** + * Preserves the {@link GLEventListenerState} from this {@link GLAutoDrawable}. + * + * @return <code>true</code> if the {@link GLEventListenerState} is preserved successfully from this {@link GLAutoDrawable}, + * otherwise <code>false</code>. + * + * @throws IllegalStateException if the {@link GLEventListenerState} is already preserved + * + * @see #restoreGLEventListenerState() + */ + protected final boolean preserveGLEventListenerState() throws IllegalStateException { + if( null != glels ) { + throw new IllegalStateException("GLEventListenerState already pulled"); + } + if( null != context && context.isCreated() ) { + if( null!= glStateKeeperListener) { + glStateKeeperListener.glStatePreserveNotify(this); + } + glels = GLEventListenerState.moveFrom(this); + return null != glels; + } + return false; + } + + /** + * Restores a previously {@link #preserveGLEventListenerState() preserved} {@link GLEventListenerState} to this {@link GLAutoDrawable}. + * + * @return <code>true</code> if the {@link GLEventListenerState} was previously {@link #preserveGLEventListenerState() preserved} + * and is moved successfully to this {@link GLAutoDrawable}, + * otherwise <code>false</code>. + * + * @see #preserveGLEventListenerState() + */ + protected final boolean restoreGLEventListenerState() { + if( null != glels ) { + glels.moveTo(this); + glels = null; + if( null!= glStateKeeperListener) { + glStateKeeperListener.glStateRestored(this); + } + return true; + } + return false; + } + + /** Default implementation to handle repaint events from the windowing system */ + protected final void defaultWindowRepaintOp() { + final GLDrawable _drawable = drawable; + if( null != _drawable && _drawable.isRealized() ) { + if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimatingOnOtherThread() ) { + display(); + } + } + } + + /** Default implementation to handle resize events from the windowing system. All required locks are being claimed. */ + protected final void defaultWindowResizedOp(int newWidth, int newHeight) throws NativeWindowException, GLException { + GLDrawableImpl _drawable = drawable; + if( null!=_drawable ) { + if(DEBUG) { + final long surfaceHandle = null != getNativeSurface() ? getNativeSurface().getSurfaceHandle() : 0; + System.err.println("GLAutoDrawableBase.sizeChanged: ("+getThreadName()+"): "+newWidth+"x"+newHeight+" - surfaceHandle 0x"+Long.toHexString(surfaceHandle)); + } + if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, newWidth, newHeight); + if(_drawable != _drawableNew) { + // write back + _drawable = _drawableNew; + drawable = _drawableNew; + } + } finally { + _lock.unlock(); + } + } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock + if( _drawable.isRealized() ) { + if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimatingOnOtherThread() ) { + display(); + } + } + } + } + + /** + * Default implementation to handle destroy notifications from the windowing system. + * + * <p> + * If the {@link NativeSurface} does not implement {@link WindowClosingProtocol} + * or {@link WindowClosingMode#DISPOSE_ON_CLOSE} is enabled (default), + * a thread safe destruction is being induced. + * </p> + */ + protected final void defaultWindowDestroyNotifyOp() { + final NativeSurface ns = getNativeSurface(); + final boolean shallClose; + if(ns instanceof WindowClosingProtocol) { + shallClose = WindowClosingMode.DISPOSE_ON_CLOSE == ((WindowClosingProtocol)ns).getDefaultCloseOperation(); + } else { + shallClose = true; + } + if( shallClose ) { + destroyAvoidAwareOfLocking(); + } + } + + /** + * Calls {@link #destroy()} + * directly if the following requirements are met: + * <ul> + * <li>An {@link GLAnimatorControl} is bound (see {@link #getAnimator()}) and running on another thread. + * Here we pause the animation while issuing the destruction.</li> + * <li>Surface is not locked by another thread (considered anonymous).</li> + * </ul> + * <p> + * Otherwise destroy is being flagged to be called within the next + * call of display(). + * </p> + * <p> + * This method is being used to avoid deadlock if + * destruction is desired by <i>other</i> threads, e.g. the window manager. + * </p> + * @see #defaultWindowDestroyNotifyOp() + * @see #defaultDisplay() + */ + protected final void destroyAvoidAwareOfLocking() { + final NativeSurface ns = getNativeSurface(); + + final GLAnimatorControl ctrl = helper.getAnimator(); + + // Is an animator thread perform rendering? + if ( helper.isAnimatorStartedOnOtherThread() ) { + // Pause animations before initiating safe destroy. + final boolean isPaused = ctrl.pause(); + destroy(); + if(isPaused) { + ctrl.resume(); + } + } else if (null != ns && ns.isSurfaceLockedByOtherThread()) { + // Surface is locked by another thread. + // Flag that destroy should be performed on the next + // attempt to display. + sendDestroy = true; // async, but avoiding deadlock + } else { + // Without an external thread animating or locking the + // surface, we are safe. + destroy(); + } + } + + /** + * Calls {@link #destroyImplInLock()} while claiming the lock. + */ + protected final void defaultDestroy() { + final RecursiveLock lock = getLock(); + lock.lock(); + try { + destroyImplInLock(); + } finally { + lock.unlock(); + } + } + + /** + * Default implementation to destroys the drawable and context of this GLAutoDrawable: + * <ul> + * <li>issues the GLEventListener dispose call, if drawable and context are valid</li> + * <li>destroys the GLContext, if valid</li> + * <li>destroys the GLDrawable, if valid</li> + * </ul> + * <p>Method assumes the lock is being hold.</p> + * <p>Override it to extend it to destroy your resources, i.e. the actual window. + * In such case call <code>super.destroyImplInLock</code> first.</p> + */ + protected void destroyImplInLock() { + if( preserveGLELSAtDestroy ) { + preserveGLStateAtDestroy(false); + preserveGLEventListenerState(); + } + if( null != context ) { + if( context.isCreated() ) { + // Catch dispose GLExceptions by GLEventListener, just 'print' them + // so we can continue with the destruction. + try { + helper.disposeGL(this, context, true); + } catch (GLException gle) { + gle.printStackTrace(); + } + } + context = null; + } + if( null != drawable ) { + final AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + drawable.setRealized(false); + drawable = null; + if( ownsDevice ) { + device.close(); + } + } + } + + public final void defaultSwapBuffers() throws GLException { + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + if(null != drawable) { + drawable.swapBuffers(); + } + } finally { + _lock.unlock(); + } + } + + // + // GLAutoDrawable + // + + protected final Runnable defaultInitAction = new Runnable() { + @Override + public final void run() { + // Lock: Locked Surface/Window by MakeCurrent/Release + helper.init(GLAutoDrawableBase.this, !sendReshape); + resetFPSCounter(); + } }; + + protected final Runnable defaultDisplayAction = new Runnable() { + @Override + public final void run() { + // Lock: Locked Surface/Window by display _and_ MakeCurrent/Release + if (sendReshape) { + helper.reshape(GLAutoDrawableBase.this, 0, 0, getWidth(), getHeight()); + sendReshape = false; + } + helper.display(GLAutoDrawableBase.this); + fpsCounter.tickFPS(); + } }; + + protected final void defaultDisplay() { + if( sendDestroy ) { + sendDestroy=false; + destroy(); + return; + } + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + if( null == context ) { + boolean contextCreated = false; + final GLDrawableImpl _drawable = drawable; + if ( null != _drawable && _drawable.isRealized() && 0<_drawable.getWidth()*_drawable.getHeight() ) { + final GLContext[] shareWith = { null }; + if( !helper.isSharedGLContextPending(shareWith) ) { + if( !restoreGLEventListenerState() ) { + context = (GLContextImpl) _drawable.createContext(shareWith[0]); + context.setContextCreationFlags(additionalCtxCreationFlags); + contextCreated = true; + // surface is locked/unlocked implicit by context's makeCurrent/release + helper.invokeGL(_drawable, context, defaultDisplayAction, defaultInitAction); + } + } + } + if(DEBUG) { + System.err.println("GLAutoDrawableBase.defaultDisplay: contextCreated "+contextCreated); + } + } else { + // surface is locked/unlocked implicit by context's makeCurrent/release + helper.invokeGL(drawable, context, defaultDisplayAction, defaultInitAction); + } + } finally { + _lock.unlock(); + } + } + + protected final GLEventListener defaultDisposeGLEventListener(GLEventListener listener, boolean remove) { + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + return helper.disposeGLEventListener(GLAutoDrawableBase.this, drawable, context, listener, remove); + } finally { + _lock.unlock(); + } + } + + @Override + public final GLDrawable getDelegatedDrawable() { + return drawable; + } + + @Override + public final GLContext getContext() { + return context; + } + + @Override + public final GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { + final RecursiveLock lock = getLock(); + lock.lock(); + try { + final GLContext oldCtx = context; + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + return oldCtx; + } finally { + lock.unlock(); + } + } + + @Override + public final GL getGL() { + final GLContext _context = context; + if (_context == null) { + return null; + } + return _context.getGL(); + } + + @Override + public final GL setGL(GL gl) { + final GLContext _context = context; + if (_context != null) { + _context.setGL(gl); + return gl; + } + return null; + } + + @Override + public final void addGLEventListener(GLEventListener listener) { + helper.addGLEventListener(listener); + } + + @Override + public final void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException { + helper.addGLEventListener(index, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); + } + + @Override + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) { + return defaultDisposeGLEventListener(listener, remove); + } + + @Override + public final GLEventListener removeGLEventListener(GLEventListener listener) { + return helper.removeGLEventListener(listener); + } + + @Override + public final void setAnimator(GLAnimatorControl animatorControl) + throws GLException { + helper.setAnimator(animatorControl); + } + + @Override + public final GLAnimatorControl getAnimator() { + return helper.getAnimator(); + } + + @Override + public final Thread setExclusiveContextThread(Thread t) throws GLException { + return helper.setExclusiveContextThread(t, context); + } + + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override + public final boolean invoke(boolean wait, GLRunnable glRunnable) { + return helper.invoke(this, wait, glRunnable); + } + + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) { + return helper.invoke(this, wait, glRunnables); + } + + @Override + public final void setAutoSwapBufferMode(boolean enable) { + helper.setAutoSwapBufferMode(enable); + } + + @Override + public final boolean getAutoSwapBufferMode() { + return helper.getAutoSwapBufferMode(); + } + + @Override + public final void setContextCreationFlags(int flags) { + additionalCtxCreationFlags = flags; + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); + } + } + + @Override + public final int getContextCreationFlags() { + return additionalCtxCreationFlags; + } + + // + // FPSCounter + // + + @Override + public final void setUpdateFPSFrames(int frames, PrintStream out) { + fpsCounter.setUpdateFPSFrames(frames, out); + } + + @Override + public final void resetFPSCounter() { + fpsCounter.resetFPSCounter(); + } + + @Override + public final int getUpdateFPSFrames() { + return fpsCounter.getUpdateFPSFrames(); + } + + @Override + public final long getFPSStartTime() { + return fpsCounter.getFPSStartTime(); + } + + @Override + public final long getLastFPSUpdateTime() { + return fpsCounter.getLastFPSUpdateTime(); + } + + @Override + public final long getLastFPSPeriod() { + return fpsCounter.getLastFPSPeriod(); + } + + @Override + public final float getLastFPS() { + return fpsCounter.getLastFPS(); + } + + @Override + public final int getTotalFPSFrames() { + return fpsCounter.getTotalFPSFrames(); + } + + @Override + public final long getTotalFPSDuration() { + return fpsCounter.getTotalFPSDuration(); + } + + @Override + public final float getTotalFPS() { + return fpsCounter.getTotalFPS(); + } + + // + // GLDrawable delegation + // + + @Override + public final GLContext createContext(final GLContext shareWith) { + final RecursiveLock lock = getLock(); + lock.lock(); + try { + if(drawable != null) { + final GLContext _ctx = drawable.createContext(shareWith); + _ctx.setContextCreationFlags(additionalCtxCreationFlags); + return _ctx; + } + return null; + } finally { + lock.unlock(); + } + } + + @Override + public final void setRealized(boolean realized) { + final RecursiveLock _lock = getLock(); + _lock.lock(); + try { + final GLDrawable _drawable = drawable; + if( null == _drawable || realized && ( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) ) { + return; + } + _drawable.setRealized(realized); + if( realized && _drawable.isRealized() ) { + sendReshape=true; // ensure a reshape is being send .. + } + } finally { + _lock.unlock(); + } + } + + @Override + public final boolean isRealized() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isRealized() : false; + } + + @Override + public int getWidth() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getWidth() : 0; + } + + @Override + public int getHeight() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getHeight() : 0; + } + + @Override + public boolean isGLOriented() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isGLOriented() : true; + } + + @Override + public final GLCapabilitiesImmutable getChosenGLCapabilities() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getChosenGLCapabilities() : null; + } + + @Override + public final GLProfile getGLProfile() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getGLProfile() : null; + } + + @Override + public final NativeSurface getNativeSurface() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getNativeSurface() : null; + } + + @Override + public final long getHandle() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getHandle() : 0; + } + + protected static String getThreadName() { return Thread.currentThread().getName(); } + + @Override + public String toString() { + return getClass().getSimpleName()+"[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + + ", \n\tContext: " + context + /** ", \n\tWindow: "+window+ ", \n\tFactory: "+factory+ */ "]"; + } +} diff --git a/src/jogl/classes/jogamp/opengl/GLBufferObjectTracker.java b/src/jogl/classes/jogamp/opengl/GLBufferObjectTracker.java new file mode 100644 index 000000000..472bfbd58 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/GLBufferObjectTracker.java @@ -0,0 +1,520 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import javax.media.opengl.*; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IntObjectHashMap; + +/** + * Tracking of {@link GLBufferStorage} instances via GL API callbacks. + * <p> + * See {@link GLBufferStorage} for generic details. + * </p> + * <p> + * Buffer storage is created via + * <ul> + * <li><code>glBufferStorage</code> - storage creation with target via {@link #createBufferStorage(GLBufferStateTracker, GL, int, long, Buffer, int, int, CreateStorageDispatch, long)}</li> + * <li>{@link GL#glBufferData(int, long, java.nio.Buffer, int)} - storage recreation with target via {@link #createBufferStorage(GLBufferStateTracker, GL, int, long, Buffer, int, int, CreateStorageDispatch, long)}</li> + * <li>{@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)} - storage recreation, direct, via {@link #createBufferStorage(GL, int, long, Buffer, int, CreateStorageDispatch, long)}</li> + * </ul> + * Note that storage <i>recreation</i> as mentioned above also invalidate a previous storage instance, + * i.e. disposed the buffer's current storage if exist and attaches a new storage instance. + * </p> + * <p> + * Buffers storage is disposed via + * <ul> + * <li>{@link GL#glDeleteBuffers(int, IntBuffer)} - explicit, direct, via {@link #notifyBuffersDeleted(int, IntBuffer)} or {@link #notifyBuffersDeleted(int, int[], int)}</li> + * <li>{@link GL#glBufferData(int, long, java.nio.Buffer, int)} - storage recreation via target</li> + * <li>{@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)} - storage recreation, direct</li> + * </ul> + * </p> + * + * <p> + * Implementation throws a {@link GLException} in all <i>construction</i> methods as listed below, + * if GL-function constraints are not met. <code>createBufferStorage</code> also throws an exception + * if the native GL-function fails. + * <ul> + * <li>{@link #createBufferStorage(GLBufferStateTracker, GL, int, long, Buffer, int, int, CreateStorageDispatch, long)}, etc ..</li> + * <li>{@link #mapBuffer(GLBufferStateTracker, GL, int, int, MapBufferAllDispatch, long)}, etc ..</li> + * </ul> + * In <i>destruction</i> and informal methods, i.e. all others, implementation only issues a WARNING debug message, if enabled. + * </p> + * + * <p> + * Buffer mapping methods like {@link GL#mapBuffer(int, int)} ..., + * require knowledge of the buffer's storage size as determined via it's creation with + * {@link GL#glBufferData(int, long, java.nio.Buffer, int)} .... + * </p> + * <p> + * Hence we track the OpenGL buffer's {@link GLBufferStorage} to be able to + * access the buffer's storage size. The tracked {@link GLBufferStorage} instances + * also allow users to conveniently access details of their created and maybe mapped buffer storage. + * </p> + * <p> + * The {@link GLBufferObjectTracker} and it's tracked {@link GLBufferStorage} instances + * maybe shared across multiple OpenGL context, hence this class is thread safe and employs synchronization. + * </p> + * <p> + * Implementation requires and utilizes a local {@link GLBufferStateTracker} + * to resolve the actual buffer-name bound to the given target. + * </p> + * <p> + * Note: This tracker requires to be notified about all OpenGL buffer storage operations, + * as well as the local {@link GLBufferStateTracker} to be notified about all + * OpenGL buffer binding operations. + * Hence buffer storage cannot be accessed properly if managed via native code. + * </p> + */ +public class GLBufferObjectTracker { + protected static final boolean DEBUG; + + static { + Debug.initSingleton(); + DEBUG = Debug.isPropertyDefined("jogl.debug.GLBufferObjectTracker", true); + } + + static final class GLBufferStorageImpl extends GLBufferStorage { + GLBufferStorageImpl(final int name, final long size, final int mutableUsage, final int immutableFlags) { + super(name, size, mutableUsage, immutableFlags); + } + final void setMappedBuffer(final ByteBuffer bb) { + if (DEBUG) { + System.err.printf("%s.GLBufferStorage.setMappedBuffer: %s: %s -> %s%n", msgClazzName, toString(true), mappedBuffer, bb); + } + mappedBuffer = bb; + } + } + + /** + * Map from buffer names to GLBufferObject. + */ + private final IntObjectHashMap bufferName2StorageMap; + + public GLBufferObjectTracker() { + bufferName2StorageMap = new IntObjectHashMap(); + bufferName2StorageMap.setKeyNotFoundValue(null); + } + + public static interface CreateStorageDispatch { + void create(final int targetOrBufferName, final long size, final Buffer data, final int mutableUsageOrImmutableFlags, final long glProcAddress); + } + + /** + * Must be called when [re]creating the GL buffer object via <code>glBufferStorage</code> and {@link GL#glBufferData(int, long, java.nio.Buffer, int)}, + * i.e. implies destruction of the buffer. + * + * @param bufferStateTracker + * @param caller + * @param target + * @param size + * @param mutableUsage <code>glBufferData</code>, <code>glNamedBufferDataEXT</code> usage + * @param immutableFlags <code>glBufferStorage</code> flags + * @throws GLException if buffer is not bound to target + * @throws GLException if size is less-or-eqaul zero for <code>glBufferStorage</code>, or size is less-than zero otherwise + * @throws GLException if a native GL-Error occurs + */ + public synchronized final void createBufferStorage(final GLBufferStateTracker bufferStateTracker, final GL caller, + final int target, final long size, final Buffer data, int mutableUsage, int immutableFlags, + final CreateStorageDispatch dispatch, final long glProcAddress) throws GLException { + final int glerrPre = caller.glGetError(); // clear + if (DEBUG && GL.GL_NO_ERROR != glerrPre) { + System.err.printf("%s.%s glerr-pre 0x%X%n", msgClazzName, msgCreateBound, glerrPre); + } + final int bufferName = bufferStateTracker.getBoundBufferObject(target, caller); + if ( 0 == bufferName ) { + throw new GLException(String.format("%s: Buffer for target 0x%X not bound", GL_INVALID_OPERATION, target)); + } + final boolean mutableBuffer = 0 != mutableUsage; + final boolean invalidSize = ( mutableBuffer && 0 > size ) // glBufferData, glNamedBufferDataEXT + || ( !mutableBuffer && 0 >= size ); // glBufferStorage + if( invalidSize ) { + throw new GLException(String.format("%s: Invalid size %d for buffer %d on target 0x%X", GL_INVALID_VALUE, size, bufferName, target)); + } + + dispatch.create(target, size, data, mutableBuffer ? mutableUsage : immutableFlags, glProcAddress); + final int glerrPost = caller.glGetError(); // be safe, catch failure! + if(GL.GL_NO_ERROR != glerrPost) { + throw new GLException(String.format("GL-Error 0x%X while creating %s storage for target 0x%X -> buffer %d of size %d with data %s", + glerrPost, mutableBuffer ? "mutable" : "immutable", target, bufferName, size, data)); + } + final GLBufferStorageImpl objNew = new GLBufferStorageImpl(bufferName, size, mutableUsage, immutableFlags); + final GLBufferStorageImpl objOld = (GLBufferStorageImpl) bufferName2StorageMap.put(bufferName, objNew); + if (DEBUG) { + System.err.printf("%s.%s target: 0x%X -> %d: %s -> %s%n", msgClazzName, msgCreateBound, target, bufferName, objOld, objNew); + } + if( null != objOld ) { + objOld.setMappedBuffer(null); + } + } + + /** + * Must be called when [re]creating the GL buffer object via {@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)}, + * i.e. implies destruction of the buffer. + * + * @param bufferName + * @param size + * @param mutableUsage + * @throws GLException if size is less-than zero + * @throws GLException if a native GL-Error occurs + */ + public synchronized final void createBufferStorage(final GL caller, + final int bufferName, final long size, final Buffer data, final int mutableUsage, int immutableFlags, + final CreateStorageDispatch dispatch, final long glProcAddress) throws GLException { + final int glerrPre = caller.glGetError(); // clear + if (DEBUG && GL.GL_NO_ERROR != glerrPre) { + System.err.printf("%s.%s glerr-pre 0x%X%n", msgClazzName, msgCreateNamed, glerrPre); + } + if ( 0 > size ) { // glBufferData, glNamedBufferDataEXT + throw new GLException(String.format("%s: Invalid size %d for buffer %d", GL_INVALID_VALUE, size, bufferName)); + } + final boolean mutableBuffer = 0 != mutableUsage; + if( !mutableBuffer ) { + throw new InternalError("Immutable glNamedBufferStorageEXT not supported yet"); + } + dispatch.create(bufferName, size, data, mutableUsage, glProcAddress); + final int glerrPost = caller.glGetError(); // be safe, catch failure! + if(GL.GL_NO_ERROR != glerrPost) { + throw new GLException(String.format("GL-Error 0x%X while creating %s storage for buffer %d of size %d with data %s", + glerrPost, "mutable", bufferName, size, data)); + } + final GLBufferStorageImpl objNew = new GLBufferStorageImpl(bufferName, size, mutableUsage, 0 /* immutableFlags */); + final GLBufferStorageImpl objOld = (GLBufferStorageImpl) bufferName2StorageMap.put(bufferName, objNew); + if (DEBUG) { + System.err.printf("%s.%s direct: %d: %s -> %s%n", msgClazzName, msgCreateNamed, bufferName, objOld, objNew); + } + if( null != objOld ) { + objOld.setMappedBuffer(null); + } + } + + /** + * Must be called when deleting GL buffer objects vis <code>glDeleteBuffers</code>. + * @param count + * @param bufferNames + * @param offset + */ + public synchronized final void notifyBuffersDeleted(final int count, final int[] bufferNames, final int offset) { + for(int i=0; i<count; i++) { + notifyBufferDeleted(bufferNames[i+offset], i, count); + } + } + /** + * Must be called when deleting GL buffer objects vis <code>glDeleteBuffers</code>. + * @param n + * @param bufferNames + */ + public synchronized final void notifyBuffersDeleted(final int n, final IntBuffer bufferNames) { + final int offset = bufferNames.position(); + for(int i=0; i<n; i++) { + notifyBufferDeleted(bufferNames.get(i+offset), i, n); + } + } + /** + * Must be called when deleting GL buffer objects vis {@link GL#glDeleteBuffers(int, IntBuffer)}. + * @param bufferName + * @param i + * @param count + */ + private synchronized final void notifyBufferDeleted(final int bufferName, final int i, final int count) { + final GLBufferStorageImpl objOld = (GLBufferStorageImpl) bufferName2StorageMap.put(bufferName, null); + if (DEBUG) { + System.err.printf("%s.notifyBuffersDeleted()[%d/%d]: %d: %s -> null%n", msgClazzName, i+1, count, bufferName, objOld); + } + if( null == objOld ) { + if (DEBUG) { + System.err.printf("%s: %s.notifyBuffersDeleted()[%d/%d]: Buffer %d not tracked%n", warning, msgClazzName, i+1, count, bufferName); + Thread.dumpStack(); + } + return; + } + objOld.setMappedBuffer(null); + } + + public static interface MapBufferDispatch { + ByteBuffer allocNioByteBuffer(final long addr, final long length); + } + public static interface MapBufferRangeDispatch extends MapBufferDispatch { + long mapBuffer(final int targetOrBufferName, final long offset, final long length, final int access, final long glProcAddress); + } + public static interface MapBufferAllDispatch extends MapBufferDispatch { + long mapBuffer(final int targetOrBufferName, final int access, final long glProcAddress); + } + + private static final String GL_INVALID_OPERATION = "GL_INVALID_OPERATION"; + private static final String GL_INVALID_VALUE = "GL_INVALID_VALUE"; + + /** + * Must be called when mapping GL buffer objects via {@link GL#mapBuffer(int, int)}. + * @throws GLException if buffer is not bound to target + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + */ + public synchronized final GLBufferStorage mapBuffer(final GLBufferStateTracker bufferStateTracker, + final GL caller, final int target, final int access, + final MapBufferAllDispatch dispatch, final long glProcAddress) throws GLException { + return this.mapBufferImpl(bufferStateTracker, caller, target, false /* useRange */, 0 /* offset */, 0 /* length */, access, dispatch, glProcAddress); + } + /** + * Must be called when mapping GL buffer objects via {@link GL#mapBufferRange(int, long, long, int)}. + * @throws GLException if buffer is not bound to target + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + * @throws GLException if buffer mapping range does not fit, incl. offset + */ + public synchronized final GLBufferStorage mapBuffer(final GLBufferStateTracker bufferStateTracker, + final GL caller, final int target, final long offset, final long length, final int access, + final MapBufferRangeDispatch dispatch, final long glProcAddress) throws GLException { + return this.mapBufferImpl(bufferStateTracker, caller, target, true /* useRange */, length, access, access, dispatch, glProcAddress); + } + /** + * Must be called when mapping GL buffer objects via {@link GL2#mapNamedBuffer(int, int)}. + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + */ + public synchronized final GLBufferStorage mapBuffer(final int bufferName, final int access, final MapBufferAllDispatch dispatch, + final long glProcAddress) throws GLException { + return this.mapBufferImpl(0 /* target */, bufferName, true /* isNamedBuffer */, false /* useRange */, 0 /* offset */, 0 /* length */, access, dispatch, glProcAddress); + } + /** + * Must be called when mapping GL buffer objects via {@link GL2#mapNamedBufferRange(int, long, long, int)}. + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + * @throws GLException if buffer mapping range does not fit, incl. offset + */ + public synchronized final GLBufferStorage mapBuffer(final int bufferName, final long offset, final long length, final int access, final MapBufferRangeDispatch dispatch, + final long glProcAddress) throws GLException { + return this.mapBufferImpl(0 /* target */, bufferName, true /* isNamedBuffer */, false /* useRange */, 0 /* offset */, 0 /* length */, access, dispatch, glProcAddress); + } + /** + * @throws GLException if buffer is not bound to target + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + * @throws GLException if buffer mapping range does not fit, incl. optional offset + */ + private synchronized final GLBufferStorage mapBufferImpl(final GLBufferStateTracker bufferStateTracker, + final GL caller, final int target, final boolean useRange, + long offset, long length, final int access, + final MapBufferDispatch dispatch, final long glProcAddress) throws GLException { + final int bufferName = bufferStateTracker.getBoundBufferObject(target, caller); + if( 0 == bufferName ) { + throw new GLException(String.format("%s.%s: %s Buffer for target 0x%X not bound", msgClazzName, msgMapBuffer, GL_INVALID_OPERATION, target)); + } + return this.mapBufferImpl(target, bufferName, false /* isNamedBuffer */, useRange, offset, length, access, dispatch, glProcAddress); + } + /** + * <p> + * A zero store size will avoid a native call and returns the unmapped {@link GLBufferStorage}. + * </p> + * <p> + * A null native mapping result indicating an error will + * not cause a GLException but returns the unmapped {@link GLBufferStorage}. + * This allows the user to handle this case. + * </p> + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + * @throws GLException if buffer mapping range does not fit, incl. optional offset + */ + private synchronized final GLBufferStorage mapBufferImpl(final int target, final int bufferName, final boolean isNamedBuffer, final boolean useRange, long offset, + long length, final int access, final MapBufferDispatch dispatch, + final long glProcAddress) throws GLException { + final GLBufferStorageImpl store = (GLBufferStorageImpl)bufferName2StorageMap.get(bufferName); + if ( null == store ) { + throw new GLException("Buffer with name "+bufferName+" not tracked"); + } + if( null != store.getMappedBuffer() ) { + throw new GLException(String.format("%s.%s: %s Buffer storage of target 0x%X -> %d: %s is already mapped", msgClazzName, msgMapBuffer, GL_INVALID_OPERATION, target, bufferName, store)); + } + final long storeSize = store.getSize(); + if ( 0 > storeSize ) { + throw new GLException(String.format("%s.%s: %s Buffer storage of target 0x%X -> %d: %s is of less-than zero", msgClazzName, msgMapBuffer, GL_INVALID_OPERATION, target, bufferName, store)); + } + if( !useRange ) { + length = storeSize; + offset = 0; + } + if( length + offset > storeSize ) { + throw new GLException(String.format("%s.%s: %s Out of range: offset %d, length %d, buffer storage of target 0x%X -> %d: %s", msgClazzName, msgMapBuffer, GL_INVALID_VALUE, offset, length, target, bufferName, store)); + } + if( 0 >= length || 0 > offset ) { + throw new GLException(String.format("%s.%s: %s Invalid values: offset %d, length %d, buffer storage of target 0x%X -> %d: %s", msgClazzName, msgMapBuffer, GL_INVALID_VALUE, offset, length, target, bufferName, store)); + } + if( 0 == storeSize ) { + return store; + } + final long addr; + if( isNamedBuffer ) { + if( useRange ) { + addr = ((MapBufferRangeDispatch)dispatch).mapBuffer(bufferName, offset, length, access, glProcAddress); + } else { + addr = ((MapBufferAllDispatch)dispatch).mapBuffer(bufferName, access, glProcAddress); + } + } else { + if( useRange ) { + addr = ((MapBufferRangeDispatch)dispatch).mapBuffer(target, offset, length, access, glProcAddress); + } else { + addr = ((MapBufferAllDispatch)dispatch).mapBuffer(target, access, glProcAddress); + } + } + // GL's map-buffer implementation always returns NULL on error, + // user shall validate the result and the corresponding getGLError() value! + if ( 0 == addr ) { + if( DEBUG ) { + System.err.printf("%s.%s: %s MapBuffer null result for target 0x%X -> %d: %s, off %d, len %d, acc 0x%X%n", msgClazzName, msgMapBuffer, warning, target, bufferName, store, offset, length, access); + Thread.dumpStack(); + } + // User shall handle the glError ! + } else { + final ByteBuffer buffer = dispatch.allocNioByteBuffer(addr, length); + Buffers.nativeOrder(buffer); + if( DEBUG ) { + System.err.printf("%s.%s: Target 0x%X -> %d: %s, off %d, len %d, acc 0x%X%n", msgClazzName, msgClazzName, target, bufferName, store.toString(false), offset, length, access); + } + store.setMappedBuffer(buffer); + } + return store; + } + + public static interface UnmapBufferDispatch { + boolean unmap(final int targetOrBufferName, final long glProcAddress); + } + + /** + * Must be called when unmapping GL buffer objects via {@link GL#glUnmapBuffer(int)}. + * <p> + * Only clear mapped buffer reference of {@link GLBufferStorage} + * if native unmapping was successful. + * </p> + */ + public synchronized final boolean unmapBuffer(final GLBufferStateTracker bufferStateTracker, final GL caller, + final int target, + final UnmapBufferDispatch dispatch, final long glProcAddress) { + final int bufferName = bufferStateTracker.getBoundBufferObject(target, caller); + final GLBufferStorageImpl store; + if( 0 == bufferName ) { + if (DEBUG) { + System.err.printf("%s: %s.%s: Buffer for target 0x%X not bound%n", warning, msgClazzName, msgUnmapped, target); + Thread.dumpStack(); + } + store = null; + } else { + store = (GLBufferStorageImpl) bufferName2StorageMap.get(bufferName); + if( DEBUG && null == store ) { + System.err.printf("%s: %s.%s: Buffer %d not tracked%n", warning, msgClazzName, msgUnmapped, bufferName); + Thread.dumpStack(); + } + } + final boolean res = dispatch.unmap(target, glProcAddress); + if( res && null != store ) { + store.setMappedBuffer(null); + } + if( DEBUG ) { + System.err.printf("%s.%s %s target: 0x%X -> %d: %s%n", msgClazzName, msgUnmapped, res ? "OK" : "Failed", target, bufferName, store.toString(false)); + if(!res) { + Thread.dumpStack(); + } + } + return res; + } + /** + * Must be called when unmapping GL buffer objects via {@link GL2#glUnmapNamedBufferEXT(int)}. + * <p> + * Only clear mapped buffer reference of {@link GLBufferStorage} + * if native unmapping was successful. + * </p> + */ + public synchronized final boolean unmapBuffer(final int bufferName, + final UnmapBufferDispatch dispatch, final long glProcAddress) { + final GLBufferStorageImpl store = (GLBufferStorageImpl) bufferName2StorageMap.get(bufferName); + if (DEBUG && null == store ) { + System.err.printf("%s: %s.%s: Buffer %d not tracked%n", warning, msgClazzName, msgUnmapped, bufferName); + Thread.dumpStack(); + } + final boolean res = dispatch.unmap(bufferName, glProcAddress); + if( res && null != store ) { + store.setMappedBuffer(null); + } + if (DEBUG) { + System.err.printf("%s.%s %s %d: %s%n", msgClazzName, msgUnmapped, res ? "OK" : "Failed", bufferName, store.toString(false)); + if(!res) { + Thread.dumpStack(); + } + } + return res; + } + + public synchronized final long getBufferSize(final int bufferName) { + final GLBufferStorageImpl store = (GLBufferStorageImpl)bufferName2StorageMap.get(bufferName); + if ( null == store ) { + if (DEBUG) { + System.err.printf("%s: %s.getBufferSize(): Buffer %d not tracked%n", warning, msgClazzName, bufferName); + Thread.dumpStack(); + } + return 0; + } + return store.getSize(); + } + + public synchronized final GLBufferStorage getBufferStorage(final int bufferName) { + return (GLBufferStorageImpl)bufferName2StorageMap.get(bufferName); + } + + /** + * Clear all tracked buffer object knowledge. + * <p> + * Shall only be called at GLContext destruction <i>iff</i> + * there are no other shared GLContext instances left. + * </p> + */ + public synchronized final void clear() { + if (DEBUG) { + System.err.printf("%s.clear() - Thread %s%n", msgClazzName, Thread.currentThread().getName()); + // Thread.dumpStack(); + } + bufferName2StorageMap.clear(); + } + + private static final String warning = "WARNING"; + private static final String msgClazzName = "GLBufferObjectTracker"; + private static final String msgUnmapped = "notifyBufferUnmapped()"; + private static final String msgCreateBound = "createBoundBufferStorage()"; + private static final String msgCreateNamed = "createNamedBufferStorage()"; + private static final String msgMapBuffer = "mapBuffer()"; +} diff --git a/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java b/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java deleted file mode 100644 index 78ab7cc93..000000000 --- a/src/jogl/classes/jogamp/opengl/GLBufferSizeTracker.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2006 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 - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl; - -import javax.media.opengl.*; -import com.jogamp.common.util.IntLongHashMap; - -/** - * Tracks as closely as possible the sizes of allocated OpenGL buffer - * objects. When glMapBuffer or glMapBufferARB is called, in order to - * turn the resulting base address into a java.nio.ByteBuffer, we need - * to know the size in bytes of the allocated OpenGL buffer object. - * Previously we would compute this size by using - * glGetBufferParameterivARB with a pname of GL_BUFFER_SIZE, but - * it appears doing so each time glMapBuffer is called is too costly - * on at least Apple's new multithreaded OpenGL implementation. <P> - * - * Instead we now try to track the sizes of allocated buffer objects. - * We watch calls to glBindBuffer to see which buffer is bound to - * which target and to glBufferData to see how large the buffer's - * allocated size is. When glMapBuffer is called, we consult our table - * of buffer sizes to see if we can return an answer without a glGet - * call. <P> - * - * We share the GLBufferSizeTracker objects among all GLContexts for - * which sharing is enabled, because the namespace for buffer objects - * is the same for these contexts. <P> - * - * Tracking the state of which buffer objects are bound is done in the - * GLBufferStateTracker and is not completely trivial. In the face of - * calls to glPushClientAttrib / glPopClientAttrib we currently punt - * and re-fetch the bound buffer object for the state in question; - * see, for example, glVertexPointer and the calls down to - * GLBufferStateTracker.getBoundBufferObject(). Note that we currently - * ignore new binding targets such as GL_TRANSFORM_FEEDBACK_BUFFER_NV; - * the fact that new binding targets may be added in the future makes - * it impossible to cache state for these new targets. <P> - * - * Ignoring new binding targets, the primary situation in which we may - * not be able to return a cached answer is in the case of an error, - * where glBindBuffer may not have been called before trying to call - * glBufferData. Also, if external native code modifies a buffer - * object, we may return an incorrect answer. (FIXME: this case - * requires more thought, and perhaps stochastic and - * exponential-fallback checking. However, note that it can only occur - * in the face of external native code which requires that the - * application be signed anyway, so there is no security risk in this - * area.) - */ - -public class GLBufferSizeTracker { - // 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 - // keys; would have to always track creation and deletion of buffer - // objects, which is probably sub-optimal. The expected usage - // 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); - } - - public final void setBufferSize(GLBufferStateTracker bufferStateTracker, - int target, GL caller, long size) { - // Need to do some similar queries to getBufferSize below - int buffer = bufferStateTracker.getBoundBufferObject(target, caller); - if (buffer != 0) { - setDirectStateBufferSize(buffer, caller, size); - } - // We don't know the current buffer state. Note that the buffer - // state tracker will have made the appropriate OpenGL query if it - // didn't know what was going on, so at this point we have nothing - // left to do except drop this piece of information on the floor. - } - - public final void setDirectStateBufferSize(int buffer, GL caller, long size) { - bufferSizeMap.put(buffer, size); - } - - public final long getBufferSize(GLBufferStateTracker bufferStateTracker, - int target, - GL caller) { - // See whether we know what buffer is currently bound to the given - // state - final int buffer = bufferStateTracker.getBoundBufferObject(target, caller); - if (0 != buffer) { - return getBufferSizeImpl(target, buffer, caller); - } - // We don't know what's going on in this case; query the GL for an answer - // FIXME: both functions return 'int' types, which is not suitable, - // since buffer lenght is 64bit ? - int[] tmp = new int[1]; - caller.glGetBufferParameteriv(target, GL.GL_BUFFER_SIZE, tmp, 0); - if (DEBUG) { - System.err.println("GLBufferSizeTracker.getBufferSize(): no cached buffer information"); - } - return (long) tmp[0]; - } - - public final long getDirectStateBufferSize(int buffer, GL caller) { - return getBufferSizeImpl(0, buffer, caller); - } - - private final long getBufferSizeImpl(int target, int buffer, GL caller) { - // See whether we know the size of this buffer object; at this - // point we almost certainly should if the application is - // written correctly - long sz = bufferSizeMap.get(buffer); - if (0xFFFFFFFFFFFFFFFFL == sz) { - // For robustness, try to query this value from the GL as we used to - // FIXME: both functions return 'int' types, which is not suitable, - // since buffer lenght is 64bit ? - int[] tmp = new int[1]; - if(0==target) { - // DirectState .. - if(caller.isFunctionAvailable("glGetNamedBufferParameterivEXT")) { - caller.getGL2().glGetNamedBufferParameterivEXT(buffer, GL.GL_BUFFER_SIZE, tmp, 0); - } else { - throw new GLException("Error: getDirectStateBufferSize called with unknown state and GL function 'glGetNamedBufferParameterivEXT' n/a to query size"); - } - } else { - caller.glGetBufferParameteriv(target, GL.GL_BUFFER_SIZE, tmp, 0); - } - if (tmp[0] == 0) { - // Assume something is wrong rather than silently going along - throw new GLException("Error: buffer size returned by "+ - ((0==target)?"glGetNamedBufferParameterivEXT":"glGetBufferParameteriv")+ - " was zero; probably application error"); - } - // Assume we just don't know what's happening - sz = (long) tmp[0]; - bufferSizeMap.put(buffer, sz); - if (DEBUG) { - System.err.println("GLBufferSizeTracker.getBufferSize(): made slow query to cache size " + - sz + - " for buffer " + - buffer); - } - } - return sz; - } - - // This should be called on any major event where we might start - // producing wrong answers, such as OpenGL context creation and - // destruction if we don't know whether there are other currently- - // created contexts that might be keeping the buffer objects alive - // that we're dealing with - public final void clearCachedBufferSizes() { - bufferSizeMap.clear(); - } -} diff --git a/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java b/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java index 92e27cbd4..511c1b9b9 100644 --- a/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java +++ b/src/jogl/classes/jogamp/opengl/GLBufferStateTracker.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2006 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,6 +41,7 @@ package jogamp.opengl; import javax.media.opengl.*; + import com.jogamp.common.util.IntIntHashMap; /** @@ -49,8 +50,13 @@ import com.jogamp.common.util.IntIntHashMap; * GLBufferStateTracker objects are allocated on a per-OpenGL-context basis. * This class is used to verify that e.g. the vertex * buffer object extension is in use when the glVertexPointer variant - * taking a long as argument is called. <P> - * + * taking a long as argument is called. + * <p> + * The buffer binding state is local to it's OpenGL context, + * i.e. not shared across multiple OpenGL context. + * Hence this code is thread safe due to no multithreading usage. + * </p> + * <p> * Note that because the enumerated value used for the binding of a * buffer object (e.g. GL_ARRAY_BUFFER) is different than that used to * query the binding using glGetIntegerv (e.g. @@ -60,50 +66,147 @@ import com.jogamp.common.util.IntIntHashMap; * to a particular state. It turns out that for some uses, such as * finding the size of the currently bound buffer, this doesn't * matter, though of course without knowing the buffer object we can't - * re-associate the queried size with the buffer object ID. <P> - * - * Because the namespace of buffer objects is the unsigned integers - * with 0 reserved by the GL, and because we have to be able to return - * both 0 and other integers as valid answers from - * getBoundBufferObject(), we need a second query, which is to ask - * whether we know the state of the binding for a given target. For - * "unknown" targets such as GL_TRANSFORM_FEEDBACK_BUFFER_NV we return + * re-associate the queried size with the buffer object ID. + * </p> + * <p> + * For <i>unknown</i> targets such as GL_TRANSFORM_FEEDBACK_BUFFER_NV we return * false from this, but we also clear the valid bit and later refresh * the binding state if glPushClientAttrib / glPopClientAttrib are * called, since we don't want the complexity of tracking stacks of * these attributes. - * + * </p> */ 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 - // OpenGL specifications. + // known that no buffer is bound to the target, according to the + // OpenGL specifications. // http://www.opengl.org/sdk/docs/man/xhtml/glBindBuffer.xml - private IntIntHashMap bindingMap; + private final IntIntHashMap bindingMap; + private final int bindingNotFound = 0xFFFFFFFF; - private int[] bufTmp = new int[1]; + private final int[] bufTmp = new int[1]; public GLBufferStateTracker() { bindingMap = new IntIntHashMap(); - bindingMap.setKeyNotFoundValue(0xFFFFFFFF); + bindingMap.setKeyNotFoundValue(bindingNotFound); // Start with known unbound targets for known keys + // setBoundBufferObject(GL2ES3.GL_VERTEX_ARRAY_BINDING, 0); // not using default VAO (removed in GL3 core) - only explicit setBoundBufferObject(GL.GL_ARRAY_BUFFER, 0); + setBoundBufferObject(GL4.GL_DRAW_INDIRECT_BUFFER, 0); setBoundBufferObject(GL.GL_ELEMENT_ARRAY_BUFFER, 0); setBoundBufferObject(GL2.GL_PIXEL_PACK_BUFFER, 0); setBoundBufferObject(GL2.GL_PIXEL_UNPACK_BUFFER, 0); } - public final void setBoundBufferObject(int target, int value) { - bindingMap.put(target, value); + + /** + * GL_ARRAY_BUFFER​, + * GL_ATOMIC_COUNTER_BUFFER​, + * GL_COPY_READ_BUFFER​, + * GL_COPY_WRITE_BUFFER​, + * GL_DRAW_INDIRECT_BUFFER​, + * GL_DISPATCH_INDIRECT_BUFFER​, + * GL_ELEMENT_ARRAY_BUFFER​, + * GL_PIXEL_PACK_BUFFER​, + * GL_PIXEL_UNPACK_BUFFER​, + * GL_SHADER_STORAGE_BUFFER​, + * GL_TEXTURE_BUFFER​, + * GL_TRANSFORM_FEEDBACK_BUFFER​ or + * GL_UNIFORM_BUFFER​. + * + * GL_VERTEX_ARRAY_BINDING + * + */ + private static final int getQueryName(final int target) { + switch (target) { + case GL.GL_ARRAY_BUFFER: return GL.GL_ARRAY_BUFFER_BINDING; + case GL4.GL_ATOMIC_COUNTER_BUFFER: return GL4.GL_ATOMIC_COUNTER_BUFFER_BINDING; + case GL2ES3.GL_COPY_READ_BUFFER: return GL2ES3.GL_COPY_READ_BUFFER_BINDING; + case GL2ES3.GL_COPY_WRITE_BUFFER: return GL2ES3.GL_COPY_WRITE_BUFFER_BINDING; + case GL4.GL_DRAW_INDIRECT_BUFFER: return GL4.GL_DRAW_INDIRECT_BUFFER_BINDING; + case GL4.GL_DISPATCH_INDIRECT_BUFFER: return GL4.GL_DISPATCH_INDIRECT_BUFFER_BINDING; + case GL.GL_ELEMENT_ARRAY_BUFFER: return GL.GL_ELEMENT_ARRAY_BUFFER_BINDING; + case GL2.GL_PIXEL_PACK_BUFFER: return GL2.GL_PIXEL_PACK_BUFFER_BINDING; + case GL2.GL_PIXEL_UNPACK_BUFFER: return GL2.GL_PIXEL_UNPACK_BUFFER_BINDING; + // FIXME case GL4.GL_QUERY_BUFFER: return GL4.GL_QUERY_BUFFER_BINDING; + case GL4.GL_SHADER_STORAGE_BUFFER: return GL4.GL_SHADER_STORAGE_BUFFER_BINDING; + case GL2GL3.GL_TEXTURE_BUFFER: return GL2GL3.GL_TEXTURE_BINDING_BUFFER; + case GL2ES3.GL_TRANSFORM_FEEDBACK_BUFFER: return GL2ES3.GL_TRANSFORM_FEEDBACK_BUFFER_BINDING; + case GL2ES3.GL_UNIFORM_BUFFER: return GL2ES3.GL_UNIFORM_BUFFER_BINDING; + + case GL2ES3.GL_VERTEX_ARRAY_BINDING: return GL2ES3.GL_VERTEX_ARRAY_BINDING; + + default: + throw new GLException(String.format("GL_INVALID_ENUM​: Invalid binding target 0x%X", target)); + } + } + private static final void checkTargetName(final int target) { + switch (target) { + case GL.GL_ARRAY_BUFFER: + case GL4.GL_ATOMIC_COUNTER_BUFFER: + case GL2ES3.GL_COPY_READ_BUFFER: + case GL2ES3.GL_COPY_WRITE_BUFFER: + case GL4.GL_DRAW_INDIRECT_BUFFER: + case GL4.GL_DISPATCH_INDIRECT_BUFFER: + case GL.GL_ELEMENT_ARRAY_BUFFER: + case GL2.GL_PIXEL_PACK_BUFFER: + case GL2.GL_PIXEL_UNPACK_BUFFER: + // FIXME case GL4.GL_QUERY_BUFFER: + case GL4.GL_SHADER_STORAGE_BUFFER: + case GL2GL3.GL_TEXTURE_BUFFER: + case GL2ES3.GL_TRANSFORM_FEEDBACK_BUFFER: + case GL2ES3.GL_UNIFORM_BUFFER: + + case GL2ES3.GL_VERTEX_ARRAY_BINDING: + return; + + default: + throw new GLException(String.format("GL_INVALID_ENUM​: Invalid binding target 0x%X", target)); + } + } + + /** + * Must be called when binding a buffer, e.g.: + * <ul> + * <li><code>glBindBuffer</code></li> + * <li><code>glBindBufferBase</code></li> + * <li><code>glBindBufferRange</code></li> + * </ul> + * @param target + * @param bufferName + */ + public final void setBoundBufferObject(int target, int bufferName) { + checkTargetName(target); + final int oldBufferName = bindingMap.put(target, bufferName); + /*** + * Test for clearing bound buffer states when unbinding VAO, + * Bug 692 Comment 5 is invalid, i.e. <https://jogamp.org/bugzilla/show_bug.cgi?id=692#c5>. + * However spec doesn't mention such behavior, and rendering w/ CPU sourced data + * after unbinding a VAO w/o unbinding the VBOs resulted to no visible image. + * Leaving code in here for discussion - in case I am wrong. + * + final int pre = bindingMap.put(target, bufferName); + if( GL2ES3.GL_VERTEX_ARRAY_BINDING == target && keyNotFound != pre && 0 == bufferName ) { + // Unbinding a previous bound VAO leads to unbinding of all buffers! + bindingMap.put(GL.GL_ARRAY_BUFFER, 0); + bindingMap.put(GL.GL_ELEMENT_ARRAY_BUFFER, 0); + bindingMap.put(GL2.GL_PIXEL_PACK_BUFFER, 0); + bindingMap.put(GL2.GL_PIXEL_UNPACK_BUFFER, 0); + bindingMap.put(GL4.GL_DRAW_INDIRECT_BUFFER, 0); + } */ if (DEBUG) { - System.err.println(); - System.err.println("GLBufferStateTracker.setBoundBufferObject() target 0x" + - Integer.toHexString(target) + " -> mapped bound buffer 0x" + - Integer.toHexString(value)); + System.err.println("GLBufferStateTracker.setBoundBufferObject() target " + + toHexString(target) + ": " + toHexString(oldBufferName) + " -> " + toHexString(bufferName)); // Thread.dumpStack(); } } @@ -114,27 +217,24 @@ public class GLBufferStateTracker { return value is valid. */ public final int getBoundBufferObject(int target, GL caller) { int value = bindingMap.get(target); - if (0xFFFFFFFF == value) { + if (bindingNotFound == value) { // User probably either called glPushClientAttrib / // glPopClientAttrib or is querying an unknown target. See // whether we know how to fetch this state. - boolean gotQueryTarget = true; - int queryTarget = 0; - switch (target) { - case GL.GL_ARRAY_BUFFER: queryTarget = GL.GL_ARRAY_BUFFER_BINDING; break; - case GL.GL_ELEMENT_ARRAY_BUFFER: queryTarget = GL.GL_ELEMENT_ARRAY_BUFFER_BINDING; break; - case GL2.GL_PIXEL_PACK_BUFFER: queryTarget = GL2.GL_PIXEL_PACK_BUFFER_BINDING; break; - case GL2.GL_PIXEL_UNPACK_BUFFER: queryTarget = GL2.GL_PIXEL_UNPACK_BUFFER_BINDING; break; - default: gotQueryTarget = false; break; - } - if (gotQueryTarget) { + final int queryTarget = getQueryName(target); + if ( 0 != queryTarget ) { + final int glerrPre = caller.glGetError(); // clear caller.glGetIntegerv(queryTarget, bufTmp, 0); - value = bufTmp[0]; + final int glerrPost = caller.glGetError(); // be safe, e.g. GL '3.0 Mesa 8.0.4' may produce an error querying GL_PIXEL_UNPACK_BUFFER_BINDING, ignore value + if(GL.GL_NO_ERROR == glerrPost) { + value = bufTmp[0]; + } else { + value = 0; + } if (DEBUG) { - System.err.println(); - System.err.println("GLBufferStateTracker.getBoundBufferObject() [queried value]: target 0x" + - Integer.toHexString(target) + " / query 0x"+Integer.toHexString(queryTarget)+ - " -> mapped bound buffer 0x" + Integer.toHexString(value)); + System.err.println("GLBufferStateTracker.getBoundBufferObject() glerr[pre "+toHexString(glerrPre)+", post "+toHexString(glerrPost)+"], [queried value]: target " + + toHexString(target) + " / query "+toHexString(queryTarget)+ + " -> mapped bound buffer " + toHexString(value)); } setBoundBufferObject(target, value); return value; @@ -142,8 +242,7 @@ public class GLBufferStateTracker { return 0; } if (DEBUG) { - System.err.println(); - System.err.println("GLBufferStateTracker.getBoundBufferObject() [mapped value]: target 0x" + + System.err.println("GLBufferStateTracker.getBoundBufferObject() [mapped value]: target 0x" + Integer.toHexString(target) + " -> mapped bound buffer 0x" + Integer.toHexString(value)); } @@ -157,12 +256,12 @@ public class GLBufferStateTracker { from GLContext.makeCurrent() in the future to possibly increase the robustness of these caches in the face of external native code manipulating OpenGL state. */ - public final void clearBufferObjectState() { + public final void clear() { + if (DEBUG) { + System.err.println("GLBufferStateTracker.clear() - Thread "+Thread.currentThread().getName()); + // Thread.dumpStack(); + } bindingMap.clear(); - if (DEBUG) { - System.err.println(); - System.err.println("GLBufferStateTracker.clearBufferObjectState()"); - //Thread.dumpStack(); - } } + private final String toHexString(int i) { return Integer.toHexString(i); } } diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index 51201b3a9..9ccd78589 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,39 +29,51 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ 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; import com.jogamp.common.os.DynamicLookupHelper; +import com.jogamp.common.os.Platform; import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.gluegen.runtime.FunctionAddressResolver; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.common.util.VersionNumberString; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.gluegen.runtime.ProcAddressTable; -import com.jogamp.gluegen.runtime.opengl.GLExtensionNames; +import com.jogamp.gluegen.runtime.opengl.GLNameResolver; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowFactory; import javax.media.opengl.GL; -import javax.media.opengl.GL3; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDebugListener; import javax.media.opengl.GLDebugMessage; import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLPipelineFactory; import javax.media.opengl.GLProfile; @@ -74,7 +86,7 @@ public abstract class GLContextImpl extends GLContext { private String contextFQN; private int additionalCtxCreationFlags; - + // Cache of the functions that are available to be called at the current // moment in time protected ExtensionAvailabilityCache extensionAvailability; @@ -82,19 +94,26 @@ public abstract class GLContextImpl extends GLContext { // OpenGL functions. private ProcAddressTable glProcAddressTable; + private String glVendor; private String glRenderer; private String glRendererLowerCase; - - // Tracks creation and initialization of buffer objects to avoid + private String glVersion; + + // Tracks lifecycle of buffer objects to avoid // repeated glGet calls upon glMapBuffer operations - private GLBufferSizeTracker bufferSizeTracker; // Singleton - Set by GLContextShareSet - private GLBufferStateTracker bufferStateTracker = new GLBufferStateTracker(); - private GLStateTracker glStateTracker = new GLStateTracker(); + private final GLBufferObjectTracker bufferObjectTracker; + private final GLBufferStateTracker bufferStateTracker; + private final GLStateTracker glStateTracker = new GLStateTracker(); private GLDebugMessageHandler glDebugHandler = null; - + private final int[] boundFBOTarget = new int[] { 0, 0 }; // { draw, read } + private int defaultVAO = 0; + protected GLDrawableImpl drawable; protected GLDrawableImpl drawableRead; + private volatile boolean pixelDataEvaluated; + private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType; + protected GL gl; protected static final Object mappedContextTypeObjectLock; @@ -112,72 +131,128 @@ public abstract class GLContextImpl extends GLContext { public static void shutdownImpl() { mappedExtensionAvailabilityCache.clear(); mappedGLProcAddress.clear(); - mappedGLXProcAddress.clear(); + mappedGLXProcAddress.clear(); } - + public GLContextImpl(GLDrawableImpl drawable, GLContext shareWith) { super(); - if (shareWith != null) { + bufferStateTracker = new GLBufferStateTracker(); + if ( null != shareWith ) { GLContextShareSet.registerSharing(this, shareWith); + bufferObjectTracker = ((GLContextImpl)shareWith).getBufferObjectTracker(); + assert (bufferObjectTracker != null) : "shared context hash null GLBufferObjectTracker: "+shareWith; + } else { + bufferObjectTracker = new GLBufferObjectTracker(); } - GLContextShareSet.synchronizeBufferObjectSharing(shareWith, this); this.drawable = drawable; this.drawableRead = drawable; - + this.glDebugHandler = new GLDebugMessageHandler(this); } - @Override - protected void resetStates() { - // Because we don't know how many other contexts we might be - // sharing with (and it seems too complicated to implement the - // GLObjectTracker's ref/unref scheme for the buffer-related - // optimizations), simply clear the cache of known buffers' sizes - // when we destroy contexts - if (bufferSizeTracker != null) { - bufferSizeTracker.clearCachedBufferSizes(); - } - - if (bufferStateTracker != null) { - bufferStateTracker.clearBufferObjectState(); + private final void clearStates() { + if( !GLContextShareSet.hasCreatedSharedLeft(this) ) { + bufferObjectTracker.clear(); } + bufferStateTracker.clear(); + glStateTracker.setEnabled(false); + glStateTracker.clearStates(); + } - if (glStateTracker != null) { - glStateTracker.clearStates(false); + @Override + protected void resetStates(boolean isInit) { + if( !isInit ) { + clearStates(); } - extensionAvailability = null; glProcAddressTable = null; gl = null; contextFQN = null; additionalCtxCreationFlags = 0; - glRenderer = ""; + glVendor = ""; + glRenderer = glVendor; glRendererLowerCase = glRenderer; - - super.resetStates(); + glVersion = glVendor; + + if (boundFBOTarget != null) { // <init> + boundFBOTarget[0] = 0; // draw + boundFBOTarget[1] = 0; // read + } + + pixelDataEvaluated = false; + + super.resetStates(isInit); } - public final void setGLReadDrawable(GLDrawable read) { - if(null!=read && drawable!=read && !isGLReadDrawableAvailable()) { - throw new GLException("GL Read Drawable not available"); + @Override + public final GLDrawable setGLReadDrawable(GLDrawable read) { + if(!isGLReadDrawableAvailable()) { + throw new GLException("Setting read drawable feature not available"); } - boolean lockHeld = lock.isOwner(); + final boolean lockHeld = lock.isOwner(Thread.currentThread()); if(lockHeld) { release(); + } else if(lock.isLockedByOtherThread()) { // still could glitch .. + throw new GLException("GLContext current by other thread ("+lock.getOwner()+"), operation not allowed."); } + final GLDrawable old = drawableRead; drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable; if(lockHeld) { makeCurrent(); } + return old; } + @Override public final GLDrawable getGLReadDrawable() { return drawableRead; } + @Override + public final GLDrawable setGLDrawable(GLDrawable readWrite, boolean setWriteOnly) { + if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) { + return drawable; // no change. + } + final Thread currentThread = Thread.currentThread(); + if( lock.isLockedByOtherThread() ) { + throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); + } + final boolean lockHeld = lock.isOwner(currentThread); + if( lockHeld && lock.getHoldCount() > 1 ) { + // would need to makeCurrent * holdCount + throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)"); + } + final GLDrawableImpl old = drawable; + if( isCreated() && null != old && old.isRealized() ) { + if(!lockHeld) { + makeCurrent(); + } + associateDrawable(false); + if(!lockHeld) { + release(); + } + } + if(lockHeld) { + release(); + } + if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable + drawableRead = (GLDrawableImpl) readWrite; + } + drawableRetargeted |= null != drawable && readWrite != drawable; + drawable = (GLDrawableImpl) readWrite ; + if( isCreated() && null != drawable && drawable.isRealized() ) { + makeCurrent(true); // implicit: associateDrawable(true) + if( !lockHeld ) { + release(); + } + } + return old; + } + + @Override public final GLDrawable getGLDrawable() { return drawable; } @@ -186,30 +261,48 @@ public abstract class GLContextImpl extends GLContext { return (GLDrawableImpl) getGLDrawable(); } + @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; } + @Override public GL setGL(GL gl) { - if(DEBUG) { - String sgl1 = (null!=this.gl)?this.gl.getClass().getSimpleName()+", "+this.gl.toString():"<null>"; - String sgl2 = (null!=gl)?gl.getClass().getSimpleName()+", "+gl.toString():"<null>"; - Exception e = new Exception("Info: setGL (OpenGL "+getGLVersion()+"): "+Thread.currentThread().getName()+", "+sgl1+" -> "+sgl2); - e.printStackTrace(); + if( DEBUG ) { + final String sgl1 = (null!=this.gl)?this.gl.getClass().getSimpleName()+", "+this.gl.toString():"<null>"; + final String sgl2 = (null!=gl)?gl.getClass().getSimpleName()+", "+gl.toString():"<null>"; + System.err.println("Info: setGL (OpenGL "+getGLVersion()+"): "+getThreadName()+", "+sgl1+" -> "+sgl2); + Thread.dumpStack(); } this.gl = gl; 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). - * + * * <p> - * This is currently being used and overridden by Mac OSX, + * This is currently being used and overridden by Mac OSX, * which issues the {@link jogamp.opengl.macosx.cgl.CGL#updateContext(long) NSOpenGLContext update()} call. * </p> - * + * * @throws GLException */ protected void drawableUpdatedNotify() throws GLException { } @@ -217,22 +310,40 @@ public abstract class GLContextImpl extends GLContext { public abstract Object getPlatformGLExtensions(); // Note: the surface is locked within [makeCurrent .. swap .. release] + @Override public void release() throws GLException { release(false); } - private void release(boolean force) throws GLException { - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - release() - force: "+force+", "+lock); + private void release(boolean inDestruction) throws GLException { + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock); } - if ( !lock.isOwner() ) { - throw new GLException("Context not current on current thread "+Thread.currentThread().getName()+": "+this); + if ( !lock.isOwner(Thread.currentThread()) ) { + final String msg = getThreadName() +": Context not current on thread, obj " + toHexString(hashCode())+", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock; + if( DEBUG_TRACE_SWITCH ) { + System.err.println(msg); + if( null != lastCtxReleaseStack ) { + System.err.print("Last release call: "); + lastCtxReleaseStack.printStackTrace(); + } else { + System.err.println("Last release call: NONE"); + } + } + throw new GLException(msg); } - final boolean actualRelease = force || lock.getHoldCount() == 1 ; - try { + + Throwable drawableContextMadeCurrentException = null; + final boolean actualRelease = ( inDestruction || lock.getHoldCount() == 1 ) && 0 != contextHandle; + try { if( actualRelease ) { - if (contextHandle != 0) { // allow dbl-release - releaseImpl(); + if( !inDestruction ) { + try { + contextMadeCurrent(false); + } catch (Throwable t) { + drawableContextMadeCurrentException = t; + } } + releaseImpl(); } } finally { // exception prone .. @@ -241,68 +352,106 @@ public abstract class GLContextImpl extends GLContext { } drawable.unlockSurface(); lock.unlock(); - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - "+(actualRelease?"switch":"keep ")+" - CONTEXT_RELEASE - "+lock); + if( DEBUG_TRACE_SWITCH ) { + final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep ")+" - "+lock; + lastCtxReleaseStack = new Throwable(msg); + if( TRACE_SWITCH ) { + System.err.println(msg); + // Thread.dumpStack(); + } } } + if(null != drawableContextMadeCurrentException) { + throw new GLException("GLContext.release(false) during GLDrawableImpl.contextMadeCurrent(this, false)", drawableContextMadeCurrentException); + } + } + private Throwable lastCtxReleaseStack = null; protected abstract void releaseImpl() throws GLException; + @Override public final void destroy() { - if (DEBUG || TRACE_SWITCH) { - System.err.println(getThreadName() + ": GLContextImpl.destroy.0: " + toHexString(contextHandle) + - ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); + if ( DEBUG_TRACE_SWITCH ) { + final long drawH = null != drawable ? drawable.getHandle() : 0; + System.err.println(getThreadName() + ": GLContextImpl.destroy.0: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + + ", surf "+toHexString(drawH)+", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } - if (contextHandle != 0) { - int lockRes = drawable.lockSurface(); - if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { + if ( 0 != contextHandle ) { // isCreated() ? + if ( null == drawable ) { + throw new GLException("GLContext created but drawable is null: "+toString()); + } + final int lockRes = drawable.lockSurface(); + if ( NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes ) { // this would be odd .. throw new GLException("Surface not ready to lock: "+drawable); } + Throwable associateDrawableException = null; try { + if ( !drawable.isRealized() ) { + throw new GLException("GLContext created but drawable not realized: "+toString()); + } // Must hold the lock around the destroy operation to make sure we // don't destroy the context while another thread renders to it. - // FIXME: This is actually impossible now, since we acquired the surface lock already, - // which is a prerequisite to acquire the context lock. - lock.lock(); // holdCount++ -> 1 or 2 + lock.lock(); // holdCount++ -> 1 - n (1: not locked, 2-n: destroy while rendering) if ( lock.getHoldCount() > 2 ) { - throw new GLException(getThreadName() + ": Lock was hold more than once - makeCurrent/release imbalance: "+lock); + final String msg = getThreadName() + ": GLContextImpl.destroy: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle); + if ( DEBUG_TRACE_SWITCH ) { + System.err.println(msg+" - Lock was hold more than once - makeCurrent/release imbalance: "+lock); + Thread.dumpStack(); + } } - try { - // release current context - if(null != glDebugHandler) { - if(lock.getHoldCount() == 1) { - // needs current context to disable debug handler - makeCurrent(); + try { + // if not current, makeCurrent(), to call associateDrawable(..) and to disable debug handler + if ( lock.getHoldCount() == 1 ) { + if ( GLContext.CONTEXT_NOT_CURRENT == makeCurrent() ) { + throw new GLException("GLContext.makeCurrent() failed: "+toString()); } - glDebugHandler.enable(false); } + try { + associateDrawable(false); + } catch (Throwable t) { + associateDrawableException = t; + } + if ( 0 != defaultVAO ) { + final int[] tmp = new int[] { defaultVAO }; + final GL2ES3 gl2es3 = gl.getRootGL().getGL2ES3(); + gl2es3.glBindVertexArray(0); + gl2es3.glDeleteVertexArrays(1, tmp, 0); + defaultVAO = 0; + } + glDebugHandler.enable(false); if(lock.getHoldCount() > 1) { // pending release() after makeCurrent() - release(true); + release(true); } destroyImpl(); contextHandle = 0; glDebugHandler = null; // this maybe impl. in a platform specific way to release remaining shared ctx. - if(GLContextShareSet.contextDestroyed(this) && !GLContextShareSet.hasCreatedSharedLeft(this)) { + if( GLContextShareSet.contextDestroyed(this) && !GLContextShareSet.hasCreatedSharedLeft(this) ) { GLContextShareSet.unregisterSharing(this); } + resetStates(false); } finally { lock.unlock(); - if (TRACE_SWITCH) { - System.err.println(getThreadName() + ": GLContextImpl.destroy.X: " + toHexString(contextHandle) + + if ( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() + ": GLContextImpl.destroy.X: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } } } finally { drawable.unlockSurface(); } + if( null != associateDrawableException ) { + throw new GLException("Exception @ destroy's associateDrawable(false)", associateDrawableException); + } + } else { + resetStates(false); } - resetStates(); } protected abstract void destroyImpl() throws GLException; + @Override public final void copy(GLContext source, int mask) throws GLException { if (source.getHandle() == 0) { throw new GLException("Source OpenGL context has not been created"); @@ -311,8 +460,8 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("Destination OpenGL context has not been created"); } - int lockRes = drawable.lockSurface(); - if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { + final int lockRes = drawable.lockSurface(); + if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { // this would be odd .. throw new GLException("Surface not ready to lock"); } @@ -364,98 +513,110 @@ public abstract class GLContextImpl extends GLContext { * @see #mapVersionAvailable * @see #destroyContextARBImpl */ - public int makeCurrent() throws GLException { - boolean unlockContextAndDrawable = false; - int res = CONTEXT_NOT_CURRENT; + @Override + public final int makeCurrent() throws GLException { + return makeCurrent(false); + } + + protected final int makeCurrent(boolean forceDrawableAssociation) throws GLException { + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+lock); + } // Note: the surface is locked within [makeCurrent .. swap .. release] - int lockRes = drawable.lockSurface(); + final int lockRes = drawable.lockSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { + if( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X1]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - Surface Not Ready - CONTEXT_NOT_CURRENT - "+lock); + } return CONTEXT_NOT_CURRENT; } + + boolean unlockResources = true; // Must be cleared if successful, otherwise finally block will release context and/or surface! + int res = CONTEXT_NOT_CURRENT; try { - if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { - drawable.updateHandle(); - } - - lock.lock(); - try { - // One context can only be current by one thread, - // and one thread can only have one context current! - final GLContext current = getCurrent(); - if (current != null) { - if (current == this) { - // Assume we don't need to make this context current again - // For Mac OS X, however, we need to update the context to track resizes - drawableUpdatedNotify(); - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - keep - CONTEXT_CURRENT - "+lock); - } - return CONTEXT_CURRENT; - } else { - current.release(); - } - } - if (0 == drawable.getHandle()) { + if ( drawable.isRealized() ) { + if ( 0 == drawable.getHandle() ) { throw new GLException("drawable has invalid handle: "+drawable); } - res = makeCurrentWithinLock(lockRes); - unlockContextAndDrawable = CONTEXT_NOT_CURRENT == res; - - /** - * FIXME: refactor dependence on Java 2D / JOGL bridge - if ((tracker != null) && - (res == CONTEXT_CURRENT_NEW)) { - // Increase reference count of GLObjectTracker - tracker.ref(); + lock.lock(); + try { + // One context can only be current by one thread, + // and one thread can only have one context current! + final GLContext current = getCurrent(); + if (current != null) { + if (current == this) { // implicit recursive locking! + // Assume we don't need to make this context current again + // For Mac OS X, however, we need to update the context to track resizes + drawableUpdatedNotify(); + unlockResources = false; // success + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X2]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - keep - CONTEXT_CURRENT - "+lock); + } + return CONTEXT_CURRENT; + } else { + current.release(); + } } - */ - } catch (RuntimeException e) { - unlockContextAndDrawable = true; - throw e; - } finally { - if (unlockContextAndDrawable) { - lock.unlock(); - } - } + res = makeCurrentWithinLock(lockRes); + unlockResources = CONTEXT_NOT_CURRENT == res; // success ? + + /** + * FIXME: refactor dependence on Java 2D / JOGL bridge + if ( tracker != null && res == CONTEXT_CURRENT_NEW ) { + // Increase reference count of GLObjectTracker + tracker.ref(); + } + */ + } catch (RuntimeException e) { + unlockResources = true; + throw e; + } finally { + if (unlockResources) { + if( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.1]: Context lock.unlock() due to error, res "+makeCurrentResultToString(res)+", "+lock); + } + lock.unlock(); + } + } + } /* if ( drawable.isRealized() ) */ } catch (RuntimeException e) { - unlockContextAndDrawable = true; + unlockResources = true; throw e; } finally { - if (unlockContextAndDrawable) { + if (unlockResources) { drawable.unlockSurface(); - } - } - - if (res == CONTEXT_NOT_CURRENT) { - if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - switch - CONTEXT_NOT_CURRENT - "+lock); } - } else { + } + + if (res != CONTEXT_NOT_CURRENT) { setCurrent(this); if(res == CONTEXT_CURRENT_NEW) { // check if the drawable's and the GL's GLProfile are equal - // throws an GLException if not + // throws an GLException if not getGLDrawable().getGLProfile().verifyEquality(gl.getGLProfile()); - + glDebugHandler.init( isGL2GL3() && isGLDebugEnabled() ); if(DEBUG_GL) { - gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) ); + setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) ); if(glDebugHandler.isEnabled()) { glDebugHandler.addListener(new GLDebugMessageHandler.StdErrGLDebugListener(true)); } } if(TRACE_GL) { - gl = gl.getContext().setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); - } - if(DEBUG || TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - switch - CONTEXT_CURRENT_NEW - "+lock); + setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); } - } else if(TRACE_SWITCH) { - System.err.println(getThreadName() +": GLContext.ContextSwitch: - switch - CONTEXT_CURRENT - "+lock); + + forceDrawableAssociation = true; } + if( forceDrawableAssociation ) { + associateDrawable(true); + } + + contextMadeCurrent(true); + /* FIXME: refactor dependence on Java 2D / JOGL bridge // Try cleaning up any stale server-side OpenGL objects @@ -465,68 +626,94 @@ public abstract class GLContextImpl extends GLContext { } */ } + if( TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X3]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - switch - "+makeCurrentResultToString(res)+" - stateTracker.on "+glStateTracker.isEnabled()+" - "+lock); + } return res; } private final int makeCurrentWithinLock(int surfaceLockRes) throws GLException { if (!isCreated()) { + if( 0 >= drawable.getWidth() || 0 >= drawable.getHeight() ) { + if ( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() + ": Create GL context REJECTED (zero surface size) obj " + toHexString(hashCode()) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()); + System.err.println(drawable.toString()); + } + return CONTEXT_NOT_CURRENT; + } if(DEBUG_GL) { // only impacts w/ createContextARB(..) additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ; } - - final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this); + + final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getCreatedShare(this); + final long shareWithHandle; if (null != shareWith) { shareWith.getDrawableImpl().lockSurface(); + shareWithHandle = shareWith.getHandle(); + if (0 == shareWithHandle) { + throw new GLException("GLContextShareSet returned an invalid OpenGL context: "+this); + } + } else { + shareWithHandle = 0; } final boolean created; try { - created = createImpl(shareWith); // may throws exception if fails! + created = createImpl(shareWithHandle); // may throws exception if fails + if( created && hasNoDefaultVAO() ) { + final int[] tmp = new int[1]; + final GL rootGL = gl.getRootGL(); + if( rootGL.isGL2ES3() ) { // FIXME remove if ES2 == ES3 later + final GL2ES3 gl2es3 = rootGL.getGL2ES3(); + gl2es3.glGenVertexArrays(1, tmp, 0); + defaultVAO = tmp[0]; + gl2es3.glBindVertexArray(defaultVAO); + } + } } finally { if (null != shareWith) { shareWith.getDrawableImpl().unlockSurface(); - } + } } - if (DEBUG) { + if ( DEBUG_TRACE_SWITCH ) { if(created) { - System.err.println(getThreadName() + ": Create GL context OK: " + toHexString(contextHandle) + " for " + getClass().getName()+" - "+getGLVersion()); + System.err.println(getThreadName() + ": Create GL context OK: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()+" - "+getGLVersion()); + // Thread.dumpStack(); } else { - System.err.println(getThreadName() + ": Create GL context FAILED for " + getClass().getName()); + System.err.println(getThreadName() + ": Create GL context FAILED obj " + toHexString(hashCode()) + ", surf "+toHexString(drawable.getHandle())+" for " + getClass().getName()); } - } + } if(!created) { return CONTEXT_NOT_CURRENT; } - + // finalize mapping the available GLVersions, in case it's not done yet - { + { final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice device = config.getScreen().getDevice(); - - if( !GLContext.getAvailableGLVersionsSet(device) ) { - final int reqMajor; - final int reqProfile; - if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_ES) ) { - // ES1 or ES2 - reqMajor = ctxMajorVersion; - reqProfile = GLContext.CTX_PROFILE_ES; - } else { - if(ctxMajorVersion<3 || ctxMajorVersion==3 && ctxMinorVersion==0) { + + // Non ARB desktop profiles may not have been registered + if( !GLContext.getAvailableGLVersionsSet(device) ) { // not yet set + if( 0 == ( ctxOptions & GLContext.CTX_PROFILE_ES) ) { // not ES profile + final int reqMajor; + final int reqProfile; + if( ctxVersion.compareTo(Version300) <= 0 ) { reqMajor = 2; } else { - reqMajor = ctxMajorVersion; + reqMajor = ctxVersion.getMajor(); } if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_CORE) ) { reqProfile = GLContext.CTX_PROFILE_CORE; } else { reqProfile = GLContext.CTX_PROFILE_COMPAT; } - } - GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, - ctxMajorVersion, ctxMinorVersion, ctxOptions); - GLContext.setAvailableGLVersionsSet(device); - if (DEBUG) { - System.err.println(getThreadName() + ": createContextOLD-MapVersionsAvailable HAVE: " + device+" -> "+reqMajor+"."+reqProfile+ " -> "+getGLVersion()); + GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, + ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + GLContext.setAvailableGLVersionsSet(device); + + if (DEBUG) { + System.err.println(getThreadName() + ": createContextOLD-MapVersionsAvailable HAVE: " + device+" -> "+reqMajor+"."+reqProfile+ " -> "+getGLVersion()); + } } } } @@ -537,30 +724,44 @@ public abstract class GLContextImpl extends GLContext { return CONTEXT_CURRENT; } protected abstract void makeCurrentImpl() throws GLException; - - /** + + /** + * Calls {@link GLDrawableImpl#associateContext(GLContext, boolean)} + */ + protected void associateDrawable(boolean bound) { + drawable.associateContext(this, bound); + } + + /** + * Calls {@link GLDrawableImpl#contextMadeCurrent(GLContext, boolean)} + */ + protected void contextMadeCurrent(boolean current) { + drawable.contextMadeCurrent(this, current); + } + + /** * Platform dependent entry point for context creation.<br> * * This method is called from {@link #makeCurrentWithinLock()} .. {@link #makeCurrent()} .<br> * - * The implementation shall verify this context with a + * The implementation shall verify this context with a * <code>MakeContextCurrent</code> call.<br> * * The implementation <b>must</b> leave the context current.<br> - * + * * @param share the shared context or null * @return the valid and current context if successful, or null * @throws GLException */ - protected abstract boolean createImpl(GLContextImpl sharedWith) throws GLException ; + protected abstract boolean createImpl(long sharedWithHandle) throws GLException ; - /** + /** * Platform dependent but harmonized implementation of the <code>ARB_create_context</code> * mechanism to create a context.<br> * - * This method is called from {@link #createContextARB}, {@link #createImpl(GLContextImpl)} .. {@link #makeCurrent()} .<br> + * This method is called from {@link #createContextARB}, {@link #createImpl(long)} .. {@link #makeCurrent()} .<br> * - * The implementation shall verify this context with a + * The implementation shall verify this context with a * <code>MakeContextCurrent</code> call.<br> * * The implementation <b>must</b> leave the context current.<br> @@ -582,8 +783,7 @@ public abstract class GLContextImpl extends GLContext { * @see #createContextARBImpl * @see #destroyContextARBImpl */ - protected abstract long createContextARBImpl(long share, boolean direct, int ctxOptionFlags, - int major, int minor); + protected abstract long createContextARBImpl(long share, boolean direct, int ctxOptionFlags, int major, int minor); /** * Destroy the context created by {@link #createContextARBImpl}. @@ -616,13 +816,10 @@ public abstract class GLContextImpl extends GLContext { * @see #createContextARBImpl * @see #destroyContextARBImpl */ - protected final long createContextARB(long share, boolean direct) + protected final long createContextARB(final long share, final boolean direct) { - AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); - AbstractGraphicsDevice device = config.getScreen().getDevice(); - GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - GLProfile glp = glCaps.getGLProfile(); - GLProfile glpImpl = glp.getImpl(); + final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); + final AbstractGraphicsDevice device = config.getScreen().getDevice(); if (DEBUG) { System.err.println(getThreadName() + ": createContextARB: mappedVersionsAvailableSet("+device.getConnection()+"): "+ @@ -631,64 +828,166 @@ public abstract class GLContextImpl extends GLContext { if ( !GLContext.getAvailableGLVersionsSet(device) ) { if(!mapGLVersions(device)) { - // none of the ARB context creation calls was successful, bail out + // none of the ARB context creation calls was successful, bail out return 0; } } - int reqMajor; - if(glpImpl.isGL4()) { - reqMajor=4; - } else if (glpImpl.isGL3()) { - reqMajor=3; - } else /* if (glpImpl.isGL2()) */ { - reqMajor=2; - } + final GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final int[] reqMajorCTP = new int[] { 0, 0 }; + GLContext.getRequestMajorAndCompat(glCaps.getGLProfile(), reqMajorCTP); - boolean compat = glpImpl.isGL2(); // incl GL3bc and GL4bc + if(DEBUG) { + System.err.println(getThreadName() + ": createContextARB: Requested "+GLContext.getGLVersion(reqMajorCTP[0], 0, reqMajorCTP[0], null)); + } int _major[] = { 0 }; int _minor[] = { 0 }; int _ctp[] = { 0 }; long _ctx = 0; - - if( GLContext.getAvailableGLVersion(device, reqMajor, compat?CTX_PROFILE_COMPAT:CTX_PROFILE_CORE, + if( GLContext.getAvailableGLVersion(device, reqMajorCTP[0], reqMajorCTP[1], _major, _minor, _ctp)) { _ctp[0] |= additionalCtxCreationFlags; + if(DEBUG) { + System.err.println(getThreadName() + ": createContextARB: Mapped "+GLContext.getGLVersion(_major[0], _minor[0], _ctp[0], null)); + } _ctx = createContextARBImpl(share, direct, _ctp[0], _major[0], _minor[0]); if(0!=_ctx) { - setGLFunctionAvailability(true, _major[0], _minor[0], _ctp[0]); + if( !setGLFunctionAvailability(true, _major[0], _minor[0], _ctp[0], false /* strictMatch */, false /* withinGLVersionsMapping */) ) { + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } } } return _ctx; } - private final boolean mapGLVersions(AbstractGraphicsDevice device) { + private final boolean mapGLVersions(AbstractGraphicsDevice device) { synchronized (GLContext.deviceVersionAvailable) { + final long t0 = ( DEBUG ) ? System.nanoTime() : 0; boolean success = false; // Following GLProfile.GL_PROFILE_LIST_ALL order of profile detection { GL4bc, GL3bc, GL2, GL4, GL3, GL2GL3, GLES2, GL2ES2, GLES1, GL2ES1 } - success |= createContextARBMapVersionsAvailable(4, true /* compat */); // GL4bc - success |= createContextARBMapVersionsAvailable(3, true /* compat */); // GL3bc - success |= createContextARBMapVersionsAvailable(2, true /* compat */); // GL2 - success |= createContextARBMapVersionsAvailable(4, false /* core */); // GL4 - success |= createContextARBMapVersionsAvailable(3, false /* core */); // GL3 + boolean hasGL4bc = false; + boolean hasGL3bc = false; + boolean hasGL2 = false; + boolean hasGL4 = false; + boolean hasGL3 = false; + + // Even w/ PROFILE_ALIASING, try to use true core GL profiles + // ensuring proper user behavior across platforms due to different feature sets! + // + if( Platform.OSType.MACOS == Platform.getOSType() && + Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Mavericks) >= 0 ) { + /** + * OSX 10.9 GLRendererQuirks.GL4NeedsGL3Request, quirk is added as usual @ setRendererQuirks(..) + */ + if( !hasGL4 && !hasGL3 ) { + hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 + success |= hasGL3; + if( hasGL3 ) { + final boolean isHWAccel = 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ); + if( isHWAccel && ctxVersion.getMajor() >= 4 ) { + // Gotcha: Creating a '3.2' ctx delivers a >= 4 ctx. + GLContext.mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL4 = true; + if(DEBUG) { + System.err.println("Quirk Triggerd: "+GLRendererQuirks.toString(GLRendererQuirks.GL4NeedsGL3Request)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()); + } + } + resetStates(false); // clean this context states, since creation was temporary + } + } + } + if( !hasGL4 ) { + hasGL4 = createContextARBMapVersionsAvailable(4, CTX_PROFILE_CORE); // GL4 + success |= hasGL4; + if( hasGL4 ) { + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL4 to all lower core profiles: GL3 + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if( PROFILE_ALIASING ) { + hasGL3 = true; + } + } + resetStates(false); // clean context states, since creation was temporary + } + } + if( !hasGL3 ) { + hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 + success |= hasGL3; + if( hasGL3 ) { + resetStates(false); // clean this context states, since creation was temporary + } + } + if( !hasGL4bc ) { + hasGL4bc = createContextARBMapVersionsAvailable(4, CTX_PROFILE_COMPAT); // GL4bc + success |= hasGL4bc; + if( hasGL4bc ) { + if( !hasGL4 ) { // last chance .. ignore hw-accel + GLContext.mapAvailableGLVersion(device, 4, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL4 = true; + } + if( !hasGL3 ) { // last chance .. ignore hw-accel + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL3 = true; + } + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL4bc to all lower compatible profiles: GL3bc, GL2 + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + GLContext.mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if(PROFILE_ALIASING) { + hasGL3bc = true; + hasGL2 = true; + } + } + resetStates(false); // clean this context states, since creation was temporary + } + } + if( !hasGL3bc ) { + hasGL3bc = createContextARBMapVersionsAvailable(3, CTX_PROFILE_COMPAT); // GL3bc + success |= hasGL3bc; + if( hasGL3bc ) { + if(!hasGL3) { // last chance .. ignore hw-accel + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + hasGL3 = true; + } + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL3bc to all lower compatible profiles: GL2 + GLContext.mapAvailableGLVersion(device, 2, CTX_PROFILE_COMPAT, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if(PROFILE_ALIASING) { + hasGL2 = true; + } + } + resetStates(false); // clean this context states, since creation was temporary + } + } + if( !hasGL2 ) { + hasGL2 = createContextARBMapVersionsAvailable(2, CTX_PROFILE_COMPAT); // GL2 + success |= hasGL2; + if( hasGL2 ) { + resetStates(false); // 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); + if(DEBUG) { + final long t1 = System.nanoTime(); + System.err.println("GLContextImpl.mapGLVersions: "+device+", profileAliasing: "+PROFILE_ALIASING+", total "+(t1-t0)/1e6 +"ms"); + System.err.println(GLContext.dumpAvailableGLVersions(null).toString()); + } } else if (DEBUG) { System.err.println(getThreadName() + ": createContextARB-MapVersions NONE for :"+device); - } + } return success; } } - private final boolean createContextARBMapVersionsAvailable(int reqMajor, boolean compat) { + /** + * Note: Since context creation is temporary, caller need to issue {@link #resetStates(boolean)}, if creation was successful, i.e. returns true. + * This method does not reset the states, allowing the caller to utilize the state variables. + **/ + private final boolean createContextARBMapVersionsAvailable(int reqMajor, int reqProfile) { long _context; - int reqProfile = compat ? CTX_PROFILE_COMPAT : CTX_PROFILE_CORE ; - int ctp = CTX_IS_ARB_CREATED | CTX_PROFILE_CORE; // default - if(compat) { - ctp &= ~CTX_PROFILE_CORE ; - ctp |= CTX_PROFILE_COMPAT ; - } + 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, @@ -698,104 +997,93 @@ 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, - // nevertheless we restrict ARB context creation to 2.0 to spare us futile attempts + // our minimum desktop OpenGL runtime requirements are 1.1, + // nevertheless we restrict ARB context creation to 2.0 to spare us futile attempts majorMax=3; minorMax=0; majorMin=2; minorMin=0; } - _context = createContextARBVersions(0, true, ctp, + _context = createContextARBVersions(0, true, ctp, /* max */ majorMax, minorMax, /* min */ majorMin, minorMin, /* res */ major, minor); - if(0==_context && !compat) { + if( 0 == _context && CTX_PROFILE_CORE == reqProfile && !PROFILE_ALIASING ) { // try w/ FORWARD instead of CORE ctp &= ~CTX_PROFILE_CORE ; ctp |= CTX_OPTION_FORWARD ; - _context = createContextARBVersions(0, true, ctp, + _context = createContextARBVersions(0, true, ctp, /* max */ majorMax, minorMax, /* min */ majorMin, minorMin, /* res */ major, minor); - if(0==_context) { + if( 0 == _context ) { // Try a compatible one .. even though not requested .. last resort ctp &= ~CTX_PROFILE_CORE ; ctp &= ~CTX_OPTION_FORWARD ; ctp |= CTX_PROFILE_COMPAT ; - _context = createContextARBVersions(0, true, ctp, + _context = createContextARBVersions(0, true, ctp, /* max */ majorMax, minorMax, /* min */ majorMin, minorMin, /* res */ major, minor); } } - if(0!=_context) { - AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - // ctxMajorVersion, ctxMinorVersion, ctxOptions is being set by + final boolean res; + if( 0 != _context ) { + AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + // ctxMajorVersion, ctxMinorVersion, ctxOptions is being set by // createContextARBVersions(..) -> setGLFunctionAvailbility(..) -> setContextVersion(..) - GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxMajorVersion, ctxMinorVersion, ctxOptions); + GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); destroyContextARBImpl(_context); if (DEBUG) { System.err.println(getThreadName() + ": createContextARB-MapVersionsAvailable HAVE: " +reqMajor+"."+reqProfile+ " -> "+getGLVersion()); } - // only reset [and hence modify] this context state if ARB context creation was successful - resetStates(); - return true; + res = true; } else { if (DEBUG) { System.err.println(getThreadName() + ": createContextARB-MapVersionsAvailable NOPE: "+reqMajor+"."+reqProfile); - } - return false; + } + res = false; } + return res; } - private final long createContextARBVersions(long share, boolean direct, int ctxOptionFlags, - int majorMax, int minorMax, - int majorMin, int minorMin, + private final long createContextARBVersions(long share, boolean direct, int ctxOptionFlags, + int majorMax, int minorMax, + int majorMin, int minorMin, int major[], int minor[]) { major[0]=majorMax; minor[0]=minorMax; long _context=0; - boolean ok = false; - - while ( !ok && - GLContext.isValidGLVersion(major[0], minor[0]) && - ( major[0]>majorMin || major[0]==majorMin && minor[0] >=minorMin ) ) { + int i=0; + do { if (DEBUG) { - System.err.println(getThreadName() + ": createContextARBVersions: share "+share+", direct "+direct+", version "+major[0]+"."+minor[0]); + i++; + System.err.println(getThreadName() + ": createContextARBVersions."+i+": share "+share+", direct "+direct+ + ", version "+major[0]+"."+minor[0]+", major["+majorMin+".."+majorMax+"], minor["+minorMin+".."+minorMax+"]"); } _context = createContextARBImpl(share, direct, ctxOptionFlags, major[0], minor[0]); if(0 != _context) { - ok = true; - setGLFunctionAvailability(true, major[0], minor[0], ctxOptionFlags); - } else { - ok = false; - } - - if(ok && major[0]>=3) { - int[] hasMajor = new int[1]; int[] hasMinor = new int[1]; - gl.glGetIntegerv(GL3.GL_MAJOR_VERSION, hasMajor, 0); - gl.glGetIntegerv(GL3.GL_MINOR_VERSION, hasMinor, 0); - ok = hasMajor[0]>major[0] || ( hasMajor[0]==major[0] && hasMinor[0]>=minor[0] ) ; - if(!ok) { - removeCachedVersion(major[0], minor[0], ctxOptionFlags); + if( setGLFunctionAvailability(true, major[0], minor[0], ctxOptionFlags, true /* strictMatch */, true /* withinGLVersionsMapping */) ) { + break; + } else { destroyContextARBImpl(_context); _context = 0; } - if (DEBUG) { - System.err.println(getThreadName() + ": createContextARBVersions: version verification - expected "+major[0]+"."+minor[0]+", has "+hasMajor[0]+"."+hasMinor[0]+" == "+ok); - } - } - - if(!ok) { - if(!GLContext.decrementGLVersion(major, minor)) break; } + + } while ( ( major[0]>majorMin || major[0]==majorMin && minor[0] >minorMin ) && // #1 check whether version is above lower limit + GLContext.decrementGLVersion(ctxOptionFlags, major, minor) // #2 decrement version + ); + if (DEBUG) { + System.err.println(getThreadName() + ": createContextARBVersions.X: ctx "+toHexString(_context)+", share "+share+", direct "+direct+ + ", version "+major[0]+"."+minor[0]+", major["+majorMin+".."+majorMax+"], minor["+minorMin+".."+minorMax+"]"); } return _context; } @@ -805,52 +1093,31 @@ public abstract class GLContextImpl extends GLContext { // As a last resort, the GL_VERSION string may be used .. // - /** - * If major > 0 || minor > 0 : Use passed values, determined at creation time - * If major==0 && minor == 0 : Use GL_VERSION + /** + * If major > 0 || minor > 0 : Use passed values, determined at creation time * Otherwise .. don't touch .. */ - private final void setContextVersion(int major, int minor, int ctp, boolean setVersionString) { - if (0==ctp) { + private final void setContextVersion(int major, int minor, int ctp, VersionNumberString glVendorVersion, boolean useGL) { + if ( 0 == ctp ) { throw new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp)); } - if(major>0 || minor>0) { - if (!GLContext.isValidGLVersion(major, minor)) { - GLException e = new GLException("Invalid GL Version "+major+"."+minor+", ctp "+toHexString(ctp)); - throw e; - } - ctxMajorVersion = major; - ctxMinorVersion = minor; - ctxOptions = ctp; - if(setVersionString) { - ctxVersionString = getGLVersion(ctxMajorVersion, ctxMinorVersion, ctxOptions, getGL().glGetString(GL.GL_VERSION)); - } - return; - } - - if(major==0 && minor==0) { - String versionStr = getGL().glGetString(GL.GL_VERSION); - if(null==versionStr) { - throw new GLException("GL_VERSION is NULL: "+this); - } - ctxOptions = ctp; - - // Set version - GLVersionNumber version = new GLVersionNumber(versionStr); - if (version.isValid()) { - ctxMajorVersion = version.getMajor(); - ctxMinorVersion = version.getMinor(); - // We cannot promote a non ARB context to >= 3.1, - // reduce it to 3.0 then. - if ( ( ctxMajorVersion>3 || ctxMajorVersion==3 && ctxMinorVersion>=1 ) - && 0 == (ctxOptions & CTX_IS_ARB_CREATED) ) { - ctxMajorVersion = 3; - ctxMinorVersion = 0; - } - if(setVersionString) { - ctxVersionString = getGLVersion(ctxMajorVersion, ctxMinorVersion, ctxOptions, versionStr); - } - return; + ctxVersion = new VersionNumber(major, minor, 0); + ctxVersionString = getGLVersion(major, minor, ctp, glVersion); + ctxVendorVersion = glVendorVersion; + ctxOptions = ctp; + if(useGL) { + ctxGLSLVersion = VersionNumber.zeroVersion; + if( hasGLSL() ) { // >= ES2 || GL2.0 + final String glslVersion = isGLES() ? null : gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION) ; // Use static GLSL version for ES to be safe! + if( null != glslVersion ) { + ctxGLSLVersion = new VersionNumber(glslVersion); + if( ctxGLSLVersion.getMajor() < 1 ) { + ctxGLSLVersion = VersionNumber.zeroVersion; // failed .. + } + } + if( ctxGLSLVersion.isZero() ) { + ctxGLSLVersion = getStaticGLSLVersionNumber(major, minor, ctxOptions); + } } } } @@ -859,18 +1126,18 @@ public abstract class GLContextImpl extends GLContext { // Helpers for various context implementations // - private Object createInstance(GLProfile glp, String suffix, Class<?>[] cstrArgTypes, Object[] cstrArgs) { - return ReflectionUtil.createInstance(glp.getGLImplBaseClassName()+suffix, cstrArgTypes, cstrArgs, getClass().getClassLoader()); + private Object createInstance(GLProfile glp, boolean glObject, Object[] cstrArgs) { + return ReflectionUtil.createInstance(glp.getGLCtor(glObject), cstrArgs); } private boolean verifyInstance(GLProfile glp, String suffix, Object instance) { - return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix); + return ReflectionUtil.instanceOf(instance, glp.getGLImplBaseClassName()+suffix); } /** Create the GL for this context. */ protected GL createGL(GLProfile glp) { - GL gl = (GL) createInstance(glp, "Impl", new Class[] { GLProfile.class, GLContextImpl.class }, new Object[] { glp, this } ); - + final GL gl = (GL) createInstance(glp, true, new Object[] { glp, this } ); + /* FIXME: refactor dependence on Java 2D / JOGL bridge if (tracker != null) { gl.setObjectTracker(tracker); @@ -878,11 +1145,32 @@ 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; } - + /** * Shall return the platform extension ProcAddressTable, * ie for GLXExt, EGLExt, .. @@ -890,18 +1178,22 @@ public abstract class GLContextImpl extends GLContext { public abstract ProcAddressTable getPlatformExtProcAddressTable(); /** - * Pbuffer support; given that this is a GLContext associated with a - * pbuffer, binds this pbuffer to its texture target. + * Part of <code>GL_NV_vertex_array_range</code>. + * <p> + * Provides platform-independent access to the <code>wglAllocateMemoryNV</code> / + * <code>glXAllocateMemoryNV</code>. + * </p> */ - public abstract void bindPbufferToTexture(); + 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. + * 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 releasePbufferFromTexture(); - - public abstract ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3); + 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 @@ -918,8 +1210,8 @@ public abstract class GLContextImpl extends GLContext { /** Maps the given "platform-independent" extension name to a real function name. Currently this is only used to map - "GL_ARB_pbuffer" to "WGL_ARB_pbuffer/GLX_SGIX_pbuffer" and - "GL_ARB_pixel_format" to "WGL_ARB_pixel_format/n.a." + "GL_ARB_pbuffer" to "WGL_ARB_pbuffer/GLX_SGIX_pbuffer" and + "GL_ARB_pixel_format" to "WGL_ARB_pixel_format/n.a." */ protected final String mapToRealGLExtensionName(String glExtensionName) { Map<String, String> map = getExtensionNameMap(); @@ -933,41 +1225,118 @@ 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>() { + @Override + public Object run() { + table.reset(getDrawableImpl().getGLDynamicLookupHelper() ); + return null; + } + } ); } - private final void initGLRendererStrings() { + private final boolean initGLRendererAndGLVersionStrings() { final GLDynamicLookupHelper glDynLookupHelper = getDrawableImpl().getGLDynamicLookupHelper(); final long _glGetString = glDynLookupHelper.dynamicLookupFunction("glGetString"); if(0 == _glGetString) { - // FIXME - System.err.println("Warning: Entry point to 'glGetString' is NULL."); - Thread.dumpStack(); + System.err.println("Error: Entry point to 'glGetString' is NULL."); + if(DEBUG) { + Thread.dumpStack(); + } + return false; } else { + final String _glVendor = glGetStringInt(GL.GL_VENDOR, _glGetString); + if(null == _glVendor) { + if(DEBUG) { + System.err.println("Warning: GL_VENDOR is NULL."); + Thread.dumpStack(); + } + return false; + } + glVendor = _glVendor; + final String _glRenderer = glGetStringInt(GL.GL_RENDERER, _glGetString); if(null == _glRenderer) { + if(DEBUG) { + System.err.println("Warning: GL_RENDERER is NULL."); + Thread.dumpStack(); + } + return false; + } + glRenderer = _glRenderer; + glRendererLowerCase = glRenderer.toLowerCase(); + + final String _glVersion = glGetStringInt(GL.GL_VERSION, _glGetString); + if(null == _glVersion) { // FIXME - System.err.println("Warning: GL_RENDERER is NULL."); - Thread.dumpStack(); - } else { - glRenderer = _glRenderer; - glRendererLowerCase = glRenderer.toLowerCase(); + if(DEBUG) { + System.err.println("Warning: GL_VERSION is NULL."); + Thread.dumpStack(); + } + return false; + } + glVersion = _glVersion; + + return true; + } + } + + /** + * Returns null if version string is invalid, otherwise a valid instance. + * <p> + * Note: Non ARB ctx is limited to GL 3.0. + * </p> + */ + private static final VersionNumber getGLVersionNumber(int ctp, String glVersionStr) { + if( null != glVersionStr ) { + final GLVersionNumber version = GLVersionNumber.create(glVersionStr); + if ( version.isValid() ) { + int[] major = new int[] { version.getMajor() }; + int[] minor = new int[] { version.getMinor() }; + if ( GLContext.isValidGLVersion(ctp, major[0], minor[0]) ) { + return new VersionNumber(major[0], minor[0], 0); + } + } + } + return null; + } + + /** + * Returns false if <code>glGetIntegerv</code> is inaccessible, otherwise queries major.minor + * version for given arrays. + * <p> + * If the GL query fails, major will be zero. + * </p> + */ + private final boolean getGLIntVersion(int[] glIntMajor, int[] glIntMinor) { + glIntMajor[0] = 0; // clear + final GLDynamicLookupHelper glDynLookupHelper = getDrawableImpl().getGLDynamicLookupHelper(); + final long _glGetIntegerv = glDynLookupHelper.dynamicLookupFunction("glGetIntegerv"); + if( 0 == _glGetIntegerv ) { + System.err.println("Error: Entry point to 'glGetIntegerv' is NULL."); + if(DEBUG) { + Thread.dumpStack(); } + return false; + } else { + glGetIntegervInt(GL2GL3.GL_MAJOR_VERSION, glIntMajor, 0, _glGetIntegerv); + glGetIntegervInt(GL2GL3.GL_MINOR_VERSION, glIntMinor, 0, _glGetIntegerv); + return true; } } - - protected final String getGLRendererString(boolean lowerCase) { - return lowerCase ? glRendererLowerCase : glRenderer; + + protected final int getCtxOptions() { + return ctxOptions; } - + + /** * Sets the OpenGL implementation class and * the cache of which GL functions are available for calling through this * context. See {@link #isFunctionAvailable(String)} for more information on * the definition of "available". * <br> - * All ProcaddressTables are being determined, the GL version is being set + * All ProcaddressTables are being determined and cached, the GL version is being set * and the extension cache is determined as well. * * @param force force the setting, even if is already being set. @@ -975,33 +1344,227 @@ public abstract class GLContextImpl extends GLContext { * @param major OpenGL major version * @param minor OpenGL minor version * @param ctxProfileBits OpenGL context profile and option bits, see {@link javax.media.opengl.GLContext#CTX_OPTION_ANY} - * + * @param strictMatch if <code>true</code> the ctx must + * <ul> + * <li>be greater or equal than the requested <code>major.minor</code> version, and</li> + * <li>match the ctxProfileBits</li> + * <li>match ES major versions</li> + * </ul>, otherwise method aborts and returns <code>false</code>.<br> + * if <code>false</code> no version check is performed. + * @param withinGLVersionsMapping if <code>true</code> GL version mapping is in process, i.e. quering avail versions. + * Otherwise normal user context creation. + * @return returns <code>true</code> if successful, otherwise <code>false</code>.<br> + * If <code>strictMatch</code> is <code>false</code> method shall always return <code>true</code> or throw an exception. + * If <code>false</code> is returned, no data has been cached or mapped, i.e. ProcAddressTable, Extensions, Version, etc. * @see #setContextVersion * @see javax.media.opengl.GLContext#CTX_OPTION_ANY * @see javax.media.opengl.GLContext#CTX_PROFILE_COMPAT * @see javax.media.opengl.GLContext#CTX_IMPL_ES2_COMPAT */ - protected final void setGLFunctionAvailability(boolean force, int major, int minor, int ctxProfileBits) { + protected final boolean setGLFunctionAvailability(boolean force, int major, int minor, int ctxProfileBits, + boolean strictMatch, boolean withinGLVersionsMapping) { if(null!=this.gl && null!=glProcAddressTable && !force) { - return; // already done and not forced + return true; // already done and not forced + } + + if ( 0 < major && !GLContext.isValidGLVersion(ctxProfileBits, major, minor) ) { + throw new GLException("Invalid GL Version Request "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); } if(null==this.gl || !verifyInstance(gl.getGLProfile(), "Impl", this.gl)) { - setGL(createGL(getGLDrawable().getGLProfile())); + setGL( createGL( getGLDrawable().getGLProfile() ) ); } updateGLXProcAddressTable(); - initGLRendererStrings(); - - if(!isCurrentContextHardwareRasterizer()) { - ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; - } final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); - + final int reqCtxProfileBits = ctxProfileBits; + final VersionNumber reqGLVersion = new VersionNumber(major, minor, 0); + final VersionNumber hasGLVersionByString; + { + final boolean initGLRendererAndGLVersionStringsOK = initGLRendererAndGLVersionStrings(); + if( !initGLRendererAndGLVersionStringsOK ) { + final String errMsg = "Intialization of GL renderer strings failed. "+adevice+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null); + if( strictMatch ) { + // query mode .. simply fail + if(DEBUG) { + System.err.println("Warning: setGLFunctionAvailability: "+errMsg); + } + return false; + } else { + // unusable GL context - non query mode - hard fail! + throw new GLException(errMsg); + } + } else { + hasGLVersionByString = getGLVersionNumber(ctxProfileBits, glVersion); + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Given "+adevice+ + " - "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)+ + ", Number(Str) "+hasGLVersionByString); + } + } + } + + final boolean isES = 0 != ( CTX_PROFILE_ES & ctxProfileBits ); + + // + // Validate GL version either by GL-Integer or GL-String + // + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Pre version verification - expected "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+", strictMatch "+strictMatch+", glVersionsMapping " +withinGLVersionsMapping); + } + + final boolean versionGL3IntOK; + { + // Validate the requested version w/ the GL-version from an integer query, + // as supported by GL [ES] >= 3.0 implementation. + final VersionNumber hasGLVersionByInt; + { + final int[] glIntMajor = new int[] { 0 }, glIntMinor = new int[] { 0 }; + final boolean getGLIntVersionOK = getGLIntVersion(glIntMajor, glIntMinor); + if( !getGLIntVersionOK ) { + final String errMsg = "Fetching GL Integer Version failed. "+adevice+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null); + if( strictMatch ) { + // query mode .. simply fail + if(DEBUG) { + System.err.println("Warning: setGLFunctionAvailability: "+errMsg); + } + return false; + } else { + // unusable GL context - non query mode - hard fail! + throw new GLException(errMsg); + } + } + hasGLVersionByInt = new VersionNumber(glIntMajor[0], glIntMinor[0], 0); + } + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Version verification (Int): String "+glVersion+", Number(Int) "+hasGLVersionByInt); + } + + // Only validate integer based version if: + // - ctx >= 3.0 is requested _or_ string-version >= 3.0 + // - _and_ a valid int version was fetched, + // otherwise cont. w/ version-string method -> 3.0 > Version || Version > MAX! + // + if ( ( major >= 3 || hasGLVersionByString.compareTo(Version300) >= 0 ) && + GLContext.isValidGLVersion(ctxProfileBits, hasGLVersionByInt.getMajor(), hasGLVersionByInt.getMinor()) ) { + // Strict Match (GLVersionMapping): + // Relaxed match for versions ( !isES && major < 3 ) requests, last resort! + // Otherwise: + // - fail if hasVersion < reqVersion (desktop and ES) + // - fail if ES major-version mismatch: + // - request 1, >= 3 must be equal + // - request 2 must be [2..3] + // + final int hasMajor = hasGLVersionByInt.getMajor(); + if( strictMatch && + ( ( ( isES || major >= 3 ) && hasGLVersionByInt.compareTo(reqGLVersion) < 0 ) || + ( isES && + ( + ( 2 == major && ( 2 > hasMajor || hasMajor > 3 ) ) || // 2 -> [2..3] + ( ( 1 == major || 3 <= major ) && major != hasMajor ) // 1,3,.. -> equal + ) + ) + ) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (Int): "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByInt); + } + return false; + } + // Use returned GL version! + major = hasGLVersionByInt.getMajor(); + minor = hasGLVersionByInt.getMinor(); + versionGL3IntOK = true; + } else { + versionGL3IntOK = false; + } + } + final boolean versionValidated; + + if( versionGL3IntOK ) { + versionValidated = true; + } else { + // Validate the requested version w/ the GL-version from the version string. + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Version verification (String): String "+glVersion+", Number(Str) "+hasGLVersionByString); + } + + // Only validate if a valid string version was fetched -> MIN > Version || Version > MAX! + if( null != hasGLVersionByString ) { + // Strict Match (GLVersionMapping): + // Relaxed match for versions ( !isES && major < 3 ) requests, last resort! + // Otherwise: + // - fail if hasVersion < reqVersion (desktop and ES) + // - fail if ES major-version mismatch: + // - request 1, >= 3 must be equal + // - request 2 must be [2..3] + // + final int hasMajor = hasGLVersionByString.getMajor(); + if( strictMatch && + ( ( ( isES || major >= 3 ) && hasGLVersionByString.compareTo(reqGLVersion) < 0 ) || + ( isES && + ( + ( 2 == major && ( 2 > hasMajor || hasMajor > 3 ) ) || // 2 -> [2..3] + ( ( 1 == major || 3 <= major ) && major != hasMajor ) // 1,3,.. -> equal + ) + ) + ) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL version mismatch (String): "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByString); + } + return false; + } + if( strictMatch && !versionGL3IntOK && major >= 3 ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL3/ES3 version Int failed, String: "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion+", "+hasGLVersionByString); + } + return false; + } + // Use returned GL version! + major = hasGLVersionByString.getMajor(); + minor = hasGLVersionByString.getMinor(); + versionValidated = true; + } else { + versionValidated = false; + } + } + if( strictMatch && !versionValidated ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, No GL version validation possible: "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)+" -> "+glVersion); + } + return false; + } + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail: Post version verification req "+ + GLContext.getGLVersion(reqGLVersion.getMajor(), reqGLVersion.getMinor(), reqCtxProfileBits, null)+" -> has "+ + GLContext.getGLVersion(major, minor, ctxProfileBits, null)+ + ", strictMatch "+strictMatch+", versionValidated "+versionValidated+", versionGL3IntOK "+versionGL3IntOK); + } + + if( major < 2 ) { // 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); + + setRendererQuirks(adevice, getDrawableImpl().getFactoryImpl(), + reqGLVersion.getMajor(), reqGLVersion.getMinor(), reqCtxProfileBits, + major, minor, ctxProfileBits, vendorVersion, withinGLVersionsMapping); + + if( strictMatch && glRendererQuirks.exist(GLRendererQuirks.GLNonCompliant) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: FAIL, GL is not compliant: "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)+", "+glRenderer); + } + return false; + } + + if(!isCurrentContextHardwareRasterizer()) { + ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; + } + contextFQN = getContextFQN(adevice, major, minor, ctxProfileBits); if (DEBUG) { - System.err.println(getThreadName() + ": Context FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.0 validated FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)); } // @@ -1011,7 +1574,7 @@ public abstract class GLContextImpl extends GLContext { synchronized(mappedContextTypeObjectLock) { table = mappedGLProcAddress.get( contextFQN ); if(null != table && !verifyInstance(gl.getGLProfile(), "ProcAddressTable", table)) { - throw new InternalError("GLContext GL ProcAddressTable mapped key("+contextFQN+" - " + GLContext.getGLVersion(major, minor, ctxProfileBits, null)+ + throw new InternalError("GLContext GL ProcAddressTable mapped key("+contextFQN+" - " + GLContext.getGLVersion(major, minor, ctxProfileBits, null)+ ") -> "+ table.getClass().getName()+" not matching "+gl.getGLProfile().getGLImplBaseClassName()); } } @@ -1021,8 +1584,7 @@ public abstract class GLContextImpl extends GLContext { System.err.println(getThreadName() + ": GLContext GL ProcAddressTable reusing key("+contextFQN+") -> "+toHexString(table.hashCode())); } } else { - glProcAddressTable = (ProcAddressTable) createInstance(gl.getGLProfile(), "ProcAddressTable", - new Class[] { FunctionAddressResolver.class } , + glProcAddressTable = (ProcAddressTable) createInstance(gl.getGLProfile(), false, new Object[] { new GLProcAddressResolver() } ); resetProcAddressTable(getGLProcAddressTable()); synchronized(mappedContextTypeObjectLock) { @@ -1032,7 +1594,7 @@ public abstract class GLContextImpl extends GLContext { } } } - + // // Update ExtensionAvailabilityCache // @@ -1047,7 +1609,7 @@ public abstract class GLContextImpl extends GLContext { } } else { extensionAvailability = new ExtensionAvailabilityCache(); - setContextVersion(major, minor, ctxProfileBits, false); // pre-set of GL version, required for extension cache usage + setContextVersion(major, minor, ctxProfileBits, vendorVersion, false); // pre-set of GL version, required for extension cache usage extensionAvailability.reset(this); synchronized(mappedContextTypeObjectLock) { mappedExtensionAvailabilityCache.put(contextFQN, extensionAvailability); @@ -1055,116 +1617,430 @@ public abstract class GLContextImpl extends GLContext { System.err.println(getThreadName() + ": GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+toHexString(extensionAvailability.hashCode()) + " - entries: "+extensionAvailability.getTotalExtensionCount()); } } - } - if( isExtensionAvailable("GL_ARB_ES2_compatibility") ) { + } + + if( isES ) { + 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( ( isES && major == 1 ) || isExtensionAvailable(GLExtensions.OES_single_precision) ) { + ctxProfileBits |= CTX_IMPL_FP32_COMPAT_API; + } + + if(FORCE_NO_FBO_SUPPORT) { + ctxProfileBits &= ~CTX_IMPL_FBO ; + } + // // Set GL Version (complete w/ version string) // - setContextVersion(major, minor, ctxProfileBits, true); - + setContextVersion(major, minor, ctxProfileBits, vendorVersion, true); + + finalizeInit(gl); + setDefaultSwapInterval(); + + final int glErrX = gl.glGetError(); // clear GL error, maybe caused by above operations + + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.X: OK "+contextFQN+" - "+GLContext.getGLVersion(ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions, null)+" - glErr "+toHexString(glErrX)); + } + return true; } - protected final void removeCachedVersion(int major, int minor, int ctxProfileBits) { + private final void setRendererQuirks(final AbstractGraphicsDevice adevice, final GLDrawableFactoryImpl factory, + int reqMajor, int reqMinor, int reqCTP, + int major, int minor, int ctp, final VersionNumberString vendorVersion, + boolean withinGLVersionsMapping) { + int[] quirks = new int[GLRendererQuirks.COUNT + 1]; // + 1 ( NoFullFBOSupport ) + 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 esCtx = 0 != ( ctp & GLContext.CTX_PROFILE_ES ); + final boolean isX11 = NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true); + final boolean isWindows = Platform.getOSType() == Platform.OSType.WINDOWS; + final boolean isDriverMesa = glRenderer.contains(MesaSP) || glRenderer.contains("Gallium "); + final boolean isDriverATICatalyst = !isDriverMesa && ( glVendor.contains("ATI Technologies") || glRenderer.startsWith("ATI ") ); + final boolean isDriverNVIDIAGeForce = !isDriverMesa && ( glVendor.contains("NVIDIA Corporation") || glRenderer.contains("NVIDIA ") ); + + // + // General Quirks + // + if( esCtx ) { + if( 2 == reqMajor && 2 < major ) { + final int quirk = GLRendererQuirks.GLES3ViaEGLES2Config; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: ES req "+reqMajor+" and 2 < "+major); + } + quirks[i++] = quirk; + if( withinGLVersionsMapping ) { + // Thread safe due to single threaded initialization! + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + } else { + // FIXME: Remove when moving EGL/ES to ARB ctx creation + synchronized(GLContextImpl.class) { + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + } + } + } + } + + // + // OS related quirks + // + if( Platform.getOSType() == Platform.OSType.MACOS ) { + // + // OSX + // + { + final int quirk = GLRendererQuirks.NoOffscreenBitmap; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); + } + quirks[i++] = quirk; + } + if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Mavericks) >= 0 && 3==reqMajor && 4==major ) { + final int quirk = GLRendererQuirks.GL4NeedsGL3Request; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", req "+reqMajor+"."+reqMinor); + } + quirks[i++] = quirk; + if( withinGLVersionsMapping ) { + // Thread safe due to single threaded initialization! + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + } + } + if( isDriverNVIDIAGeForce ) { + final VersionNumber osxVersionNVFlushClean = new VersionNumber(10,7,3); // < OSX 10.7.3 w/ NV needs glFlush + if( Platform.getOSVersionNumber().compareTo(osxVersionNVFlushClean) < 0 ) { + final int quirk = GLRendererQuirks.GLFlushBeforeRelease; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); + } + quirks[i++] = quirk; + } + if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Lion) < 0 ) { // < OSX 10.7.0 w/ NV has unstable GLSL + final int quirk = GLRendererQuirks.GLSLNonCompliant; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); + } + quirks[i++] = quirk; + } + } + } else if( isWindows ) { + // + // WINDOWS + // + { + final int quirk = GLRendererQuirks.NoDoubleBufferedBitmap; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); + } + quirks[i++] = quirk; + } + + if( isDriverATICatalyst ) { + final VersionNumber winXPVersionNumber = new VersionNumber ( 5, 1, 0); + final VersionNumber amdSafeMobilityVersion = new VersionNumber(12, 102, 3); + + if ( vendorVersion.compareTo(amdSafeMobilityVersion) < 0 ) { // includes: vendorVersion.isZero() + final int quirk = GLRendererQuirks.NeedCurrCtx4ARBCreateContext; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"], driverVersion "+vendorVersion); + } + quirks[i++] = quirk; + } + + if( Platform.getOSVersionNumber().compareTo(winXPVersionNumber) <= 0 ) { + final int quirk = GLRendererQuirks.NeedCurrCtx4ARBPixFmtQueries; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS-Version "+Platform.getOSType()+" "+Platform.getOSVersionNumber()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"]"); + } + quirks[i++] = quirk; + } + } + } else if( Platform.OSType.ANDROID == Platform.getOSType() ) { + // + // ANDROID + // + // Renderer related quirks, may also involve OS + if( glRenderer.contains("PowerVR") ) { + final int quirk = GLRendererQuirks.NoSetSwapInterval; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + if( glRenderer.contains("Immersion.16") ) { + final int quirk = GLRendererQuirks.GLSharedContextBuggy; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + } + + // + // Windowing Toolkit related quirks + // + if( isX11 ) { + // + // X11 + // + { + // + // Quirk: DontCloseX11Display + // + final int quirk = GLRendererQuirks.DontCloseX11Display; + if( glRenderer.contains(MesaSP) ) { + 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+"]"); + } + quirks[i++] = quirk; + } + } else if( isDriverATICatalyst ) { + { + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer); + } + quirks[i++] = quirk; + } + } else if( jogamp.nativewindow.x11.X11Util.getMarkAllDisplaysUnclosable() ) { + { + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11Util Downstream"); + } + quirks[i++] = quirk; + } + } + } + } + + + // + // RENDERER related quirks + // + if( isDriverMesa ) { + final VersionNumber mesaIntelBuggySharedCtx921 = new VersionNumber(9, 2, 1); + + { + final int quirk = GLRendererQuirks.NoSetSwapIntervalPostRetarget; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); + } + quirks[i++] = quirk; + } + if( hwAccel /* glRenderer.contains( MesaRendererIntelsp ) || glRenderer.contains( MesaRendererAMDsp ) */ ) + { + final int quirk = GLRendererQuirks.NoDoubleBufferedPBuffer; + 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( glRenderer.contains( MesaRendererIntelsp ) && + vendorVersion.compareTo(mesaIntelBuggySharedCtx921) >= 0 && isX11 ) { + final int quirk = GLRendererQuirks.GLSharedContextBuggy; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); + } + quirks[i++] = quirk; + } + if( isWindows && glRenderer.contains("SVGA3D") ) { + final VersionNumber mesaSafeFBOVersion = new VersionNumber(8, 0, 0); + if ( vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { // includes: vendorVersion.isZero() + final int quirk = GLRendererQuirks.NoFullFBOSupport; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); + } + quirks[i++] = quirk; + } + } + } + + // + // Property related quirks + // + if( FORCE_MIN_FBO_SUPPORT ) { + final int quirk = GLRendererQuirks.NoFullFBOSupport; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: property"); + } + quirks[i++] = quirk; + } + + glRendererQuirks = new GLRendererQuirks(quirks, 0, i); + if(DEBUG) { + System.err.println("Quirks local.0: "+glRendererQuirks); + } + { + // Merge sticky quirks, thread safe due to single threaded initialization! + GLRendererQuirks.pushStickyDeviceQuirks(adevice, glRendererQuirks); + + final AbstractGraphicsDevice factoryDefaultDevice = factory.getDefaultDevice(); + if( !GLRendererQuirks.areSameStickyDevice(factoryDefaultDevice, adevice) ) { + GLRendererQuirks.pushStickyDeviceQuirks(factoryDefaultDevice, glRendererQuirks); + } + if( esCtx ) { + final AbstractGraphicsDevice eglFactoryDefaultDevice = GLDrawableFactory.getEGLFactory().getDefaultDevice(); + if( !GLRendererQuirks.areSameStickyDevice(eglFactoryDefaultDevice, adevice) && + !GLRendererQuirks.areSameStickyDevice(eglFactoryDefaultDevice, factoryDefaultDevice) ) { + GLRendererQuirks.pushStickyDeviceQuirks(eglFactoryDefaultDevice, glRendererQuirks); + } + } + } + if(DEBUG) { + System.err.println("Quirks local.X: "+glRendererQuirks); + System.err.println("Quirks sticky on "+adevice+": "+GLRendererQuirks.getStickyDeviceQuirks(adevice)); + } + } + + private static final boolean hasFBOImpl(int major, int ctp, ExtensionAvailabilityCache extCache) { + return ( 0 != (ctp & CTX_PROFILE_ES) && major >= 2 ) || // ES >= 2.0 + + major >= 3 || // any >= 3.0 GL ctx (core, compat and ES) + + ( null != extCache && + + extCache.isExtensionAvailable(GLExtensions.ARB_ES2_compatibility) || // ES 2.0 compatible + + extCache.isExtensionAvailable(GLExtensions.ARB_framebuffer_object) || // ARB_framebuffer_object + + extCache.isExtensionAvailable(GLExtensions.EXT_framebuffer_object) || // EXT_framebuffer_object + + extCache.isExtensionAvailable(GLExtensions.OES_framebuffer_object) ) ; // OES_framebuffer_object excluded + } + + private final void removeCachedVersion(int major, int minor, int ctxProfileBits) { if(!isCurrentContextHardwareRasterizer()) { ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; } final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); - + contextFQN = getContextFQN(adevice, major, minor, ctxProfileBits); if (DEBUG) { System.err.println(getThreadName() + ": RM Context FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, null)); } synchronized(mappedContextTypeObjectLock) { - ProcAddressTable table = mappedGLProcAddress.remove( contextFQN ); + final ProcAddressTable table = mappedGLProcAddress.remove( contextFQN ); if(DEBUG) { - System.err.println(getThreadName() + ": RM GLContext GL ProcAddressTable mapping key("+contextFQN+") -> "+table.hashCode()); + final int hc = null != table ? table.hashCode() : 0; + System.err.println(getThreadName() + ": RM GLContext GL ProcAddressTable mapping key("+contextFQN+") -> "+toHexString(hc)); } } synchronized(mappedContextTypeObjectLock) { - ExtensionAvailabilityCache eCache = mappedExtensionAvailabilityCache.remove( contextFQN ); + final ExtensionAvailabilityCache eCache = mappedExtensionAvailabilityCache.remove( contextFQN ); if(DEBUG) { - System.err.println(getThreadName() + ": RM GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+eCache.hashCode()); + final int hc = null != eCache ? eCache.hashCode() : 0; + System.err.println(getThreadName() + ": RM GLContext GL ExtensionAvailabilityCache mapping key("+contextFQN+") -> "+toHexString(hc)); } } } private final boolean isCurrentContextHardwareRasterizer() { boolean isHardwareRasterizer = true; - + if(!drawable.getChosenGLCapabilities().getHardwareAccelerated()) { isHardwareRasterizer = false; } else { - isHardwareRasterizer = ! ( glRendererLowerCase.contains("software") /* Mesa3D */ || - glRendererLowerCase.contains("mesa x11") /* Mesa3D*/ || + isHardwareRasterizer = ! ( glRendererLowerCase.contains("software") /* Mesa3D, Apple */ || + glRendererLowerCase.contains("mesa x11") /* Mesa3D */ || glRendererLowerCase.contains("softpipe") /* Gallium */ || - glRendererLowerCase.contains("llvmpipe") /* Gallium */ + glRendererLowerCase.contains("llvmpipe") /* Gallium */ ); } return isHardwareRasterizer; } - + /** * Updates the platform's 'GLX' function cache */ protected abstract void updateGLXProcAddressTable(); protected abstract StringBuilder getPlatformExtensionsStringImpl(); - + + @Override public final boolean isFunctionAvailable(String glFunctionName) { // 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) {} } // Check platform extensions 2nd (cached) - context had to be enabled once - final ProcAddressTable pTable = getPlatformExtProcAddressTable(); - if(null!=pTable) { + 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 = GLExtensionNames.normalizeVEN(GLExtensionNames.normalizeARB(glFunctionName, true), true); - long addr = 0; - int variants = GLExtensionNames.getFuncNamePermutationNumber(tmpBase); - for(int i = 0; 0==addr && i < variants; i++) { - String tmp = GLExtensionNames.getFuncNamePermutation(tmpBase, i); + 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; !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; } - public boolean isExtensionAvailable(String glExtensionName) { + @Override + public final boolean isExtensionAvailable(String glExtensionName) { if(null!=extensionAvailability) { return extensionAvailability.isExtensionAvailable(mapToRealGLExtensionName(glExtensionName)); } return false; } + @Override public final int getPlatformExtensionCount() { return null != extensionAvailability ? extensionAvailability.getPlatformExtensionCount() : 0; } - + + @Override public final String getPlatformExtensionsString() { if(null!=extensionAvailability) { return extensionAvailability.getPlatformExtensionsString(); @@ -1172,10 +2048,12 @@ public abstract class GLContextImpl extends GLContext { return null; } + @Override public final int getGLExtensionCount() { return null != extensionAvailability ? extensionAvailability.getGLExtensionCount() : 0; } - + + @Override public final String getGLExtensionsString() { if(null!=extensionAvailability) { return extensionAvailability.getGLExtensionsString(); @@ -1189,15 +2067,15 @@ public abstract class GLContextImpl extends GLContext { } return false; } - + protected static String getContextFQN(AbstractGraphicsDevice device, int major, int minor, int ctxProfileBits) { // remove non-key values - ctxProfileBits &= ~( GLContext.CTX_OPTION_DEBUG | GLContext.CTX_IMPL_ES2_COMPAT ) ; - + ctxProfileBits &= CTX_IMPL_CACHE_MASK; + return device.getUniqueID() + "-" + toHexString(composeBits(major, minor, ctxProfileBits)); } - protected String getContextFQN() { + protected final String getContextFQN() { return contextFQN; } @@ -1208,32 +2086,63 @@ public abstract class GLContextImpl extends GLContext { throw new GLException("Not supported on non-pbuffer contexts"); } - /** On some platforms the mismatch between OpenGL's coordinate - system (origin at bottom left) and the window system's - coordinate system (origin at top left) necessitates a vertical - flip of pixels read from offscreen contexts. */ - public abstract boolean offscreenImageNeedsVerticalFlip(); + @Override + public int getDefaultPixelDataType() { + evalPixelDataType(); + return pixelDataType; + } - /** Only called for offscreen contexts; needed by glReadPixels */ - public abstract int getOffscreenContextPixelDataType(); + @Override + public int getDefaultPixelDataFormat() { + evalPixelDataType(); + return pixelDataFormat; + } + private final void evalPixelDataType() { + if(!pixelDataEvaluated) { + synchronized(this) { + if(!pixelDataEvaluated) { + boolean ok = false; + /* if(isGL2GL3() && 3 == components) { + pixelDataInternalFormat=GL.GL_RGB; + pixelDataFormat=GL.GL_RGB; + pixelDataType = GL.GL_UNSIGNED_BYTE; + ok = true; + } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) { + final int[] glImplColorReadVals = new int[] { 0, 0 }; + gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0); + gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1); + // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; + pixelDataFormat = glImplColorReadVals[0]; + pixelDataType = glImplColorReadVals[1]; + ok = 0 != pixelDataFormat && 0 != pixelDataType; + } + if( !ok ) { + // RGBA read is safe for all GL profiles + // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; + pixelDataFormat=GL.GL_RGBA; + pixelDataType = GL.GL_UNSIGNED_BYTE; + } + // TODO: Consider: + // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; + pixelDataEvaluated = true; + } + } + } + } //---------------------------------------------------------------------- // Helpers for buffer object optimizations - - public void setBufferSizeTracker(GLBufferSizeTracker bufferSizeTracker) { - this.bufferSizeTracker = bufferSizeTracker; - } - public GLBufferSizeTracker getBufferSizeTracker() { - return bufferSizeTracker; + public final GLBufferObjectTracker getBufferObjectTracker() { + return bufferObjectTracker; } - public GLBufferStateTracker getBufferStateTracker() { + public final GLBufferStateTracker getBufferStateTracker() { return bufferStateTracker; } - public GLStateTracker getGLStateTracker() { + public final GLStateTracker getGLStateTracker() { return glStateTracker; } @@ -1242,38 +2151,123 @@ public abstract class GLContextImpl extends GLContext { // current on the OpenGL worker thread // - public boolean hasWaiters() { + /** + * Returns true if the given thread is owner, otherwise false. + * <p> + * Method exists merely for code validation of {@link #isCurrent()}. + * </p> + */ + public final boolean isOwner(Thread thread) { + return lock.isOwner(thread); + } + + /** + * Returns true if there are other threads waiting for this GLContext to {@link #makeCurrent()}, otherwise false. + * <p> + * Since method does not perform any synchronization, accurate result are returned if lock is hold - only. + * </p> + */ + public final boolean hasWaiters() { return lock.getQueueLength()>0; } - + + /** + * Returns the number of hold locks. See {@link RecursiveLock#getHoldCount()} for semantics. + * <p> + * Since method does not perform any synchronization, accurate result are returned if lock is hold - only. + * </p> + */ + public final int getLockCount() { + return lock.getHoldCount(); + } + + //--------------------------------------------------------------------------- + // Special FBO hook + // + + /** + * Tracks {@link GL#GL_FRAMEBUFFER}, {@link GL2GL3#GL_DRAW_FRAMEBUFFER} and {@link GL2GL3#GL_READ_FRAMEBUFFER} + * to be returned via {@link #getBoundFramebuffer(int)}. + * + * <p>Invoked by {@link GL#glBindFramebuffer(int, int)}. </p> + * + * <p>Assumes valid <code>framebufferName</code> range of [0..{@link Integer#MAX_VALUE}]</p> + * + * <p>Does not throw an exception if <code>target</code> is unknown or <code>framebufferName</code> invalid.</p> + */ + public final void setBoundFramebuffer(int target, int framebufferName) { + if(0 > framebufferName) { + return; // ignore invalid name + } + switch(target) { + case GL.GL_FRAMEBUFFER: + boundFBOTarget[0] = framebufferName; // draw + boundFBOTarget[1] = framebufferName; // read + break; + case GL2GL3.GL_DRAW_FRAMEBUFFER: + boundFBOTarget[0] = framebufferName; // draw + break; + case GL2GL3.GL_READ_FRAMEBUFFER: + boundFBOTarget[1] = framebufferName; // read + break; + default: // ignore untracked target + } + } + @Override + public final int getBoundFramebuffer(int target) { + switch(target) { + case GL.GL_FRAMEBUFFER: + case GL2GL3.GL_DRAW_FRAMEBUFFER: + return boundFBOTarget[0]; // draw + case GL2GL3.GL_READ_FRAMEBUFFER: + return boundFBOTarget[1]; // read + default: + throw new InternalError("Invalid FBO target name: "+toHexString(target)); + } + } + + @Override + public final int getDefaultDrawFramebuffer() { return drawable.getDefaultDrawFramebuffer(); } + @Override + public final int getDefaultReadFramebuffer() { return drawable.getDefaultReadFramebuffer(); } + @Override + public final int getDefaultReadBuffer() { return drawable.getDefaultReadBuffer(gl, drawableRead != drawable); } + //--------------------------------------------------------------------------- // GL_ARB_debug_output, GL_AMD_debug_output helpers // + @Override public final String getGLDebugMessageExtension() { return glDebugHandler.getExtension(); } + @Override public final boolean isGLDebugMessageEnabled() { return glDebugHandler.isEnabled(); } - + + @Override public final int getContextCreationFlags() { - return additionalCtxCreationFlags; + return additionalCtxCreationFlags; } + @Override public final void setContextCreationFlags(int flags) { if(!isCreated()) { additionalCtxCreationFlags = flags & GLContext.CTX_OPTION_DEBUG; } } - - public final boolean isGLDebugSynchronous() { return glDebugHandler.isSynchronous(); } - + + @Override + public final boolean isGLDebugSynchronous() { return glDebugHandler.isSynchronous(); } + + @Override public final void setGLDebugSynchronous(boolean synchronous) { glDebugHandler.setSynchronous(synchronous); } - + + @Override public final void enableGLDebugMessage(boolean enable) throws GLException { if(!isCreated()) { if(enable) { @@ -1286,40 +2280,48 @@ public abstract class GLContextImpl extends GLContext { glDebugHandler.enable(enable); } } - - public final void addGLDebugListener(GLDebugListener listener) { + + @Override + public final void addGLDebugListener(GLDebugListener listener) { glDebugHandler.addListener(listener); } - + + @Override public final void removeGLDebugListener(GLDebugListener listener) { glDebugHandler.removeListener(listener); - } - + } + + @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); - } + } } - + + @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); } } - + + @Override 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 */ + private static native void glGetIntegervInt(int pname, int[] params, int params_offset, long procAddress); } diff --git a/src/jogl/classes/jogamp/opengl/GLContextShareSet.java b/src/jogl/classes/jogamp/opengl/GLContextShareSet.java index b7acc0dff..c057c904c 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextShareSet.java +++ b/src/jogl/classes/jogamp/opengl/GLContextShareSet.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2011 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,18 +29,19 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -55,39 +56,39 @@ import javax.media.opengl.GLException; public class GLContextShareSet { private static final boolean DEBUG = GLContextImpl.DEBUG; - + // This class is implemented using a HashMap which maps from all shared contexts // to a share set, containing all shared contexts itself. - private static final Map<GLContext, ShareSet> shareMap = new HashMap<GLContext, ShareSet>(); + private static final Map<GLContext, ShareSet> shareMap = new IdentityHashMap<GLContext, ShareSet>(); private static final Object dummyValue = new Object(); private static class ShareSet { - private Map<GLContext, Object> allShares = new HashMap<GLContext, Object>(); - private Map<GLContext, Object> createdShares = new HashMap<GLContext, Object>(); - private Map<GLContext, Object> destroyedShares = new HashMap<GLContext, Object>(); + private final Map<GLContext, Object> allShares = new IdentityHashMap<GLContext, Object>(); + private final Map<GLContext, Object> createdShares = new IdentityHashMap<GLContext, Object>(); + private final Map<GLContext, Object> destroyedShares = new IdentityHashMap<GLContext, Object>(); - public void add(GLContext ctx) { + public void add(final GLContext ctx) { if (allShares.put(ctx, dummyValue) == null) { if (ctx.isCreated()) { createdShares.put(ctx, dummyValue); } else { destroyedShares.put(ctx, dummyValue); } - } + } } public Set<GLContext> getCreatedShares() { return createdShares.keySet(); } - + public Set<GLContext> getDestroyedShares() { return destroyedShares.keySet(); } - - public GLContext getCreatedShare(GLContext ignore) { - for (Iterator<GLContext> iter = createdShares.keySet().iterator(); iter.hasNext(); ) { - GLContext ctx = iter.next(); + + public GLContext getCreatedShare(final GLContext ignore) { + for (final Iterator<GLContext> iter = createdShares.keySet().iterator(); iter.hasNext(); ) { + final GLContext ctx = iter.next(); if (ctx != ignore) { return ctx; } @@ -95,21 +96,21 @@ public class GLContextShareSet { return null; } - public void contextCreated(GLContext ctx) { - Object res = destroyedShares.remove(ctx); + public void contextCreated(final GLContext ctx) { + final Object res = destroyedShares.remove(ctx); assert res != null : "State of ShareSet corrupted; thought context " + ctx + " should have been in destroyed set but wasn't"; - res = createdShares.put(ctx, dummyValue); - assert res == null : "State of ShareSet corrupted; thought context " + + final Object res2 = createdShares.put(ctx, dummyValue); + assert res2 == null : "State of ShareSet corrupted; thought context " + ctx + " shouldn't have been in created set but was"; } - public void contextDestroyed(GLContext ctx) { - Object res = createdShares.remove(ctx); + public void contextDestroyed(final GLContext ctx) { + final Object res = createdShares.remove(ctx); assert res != null : "State of ShareSet corrupted; thought context " + ctx + " should have been in created set but wasn't"; - res = destroyedShares.put(ctx, dummyValue); - assert res == null : "State of ShareSet corrupted; thought context " + + final Object res2 = destroyedShares.put(ctx, dummyValue); + assert res2 == null : "State of ShareSet corrupted; thought context " + ctx + " shouldn't have been in destroyed set but was"; } } @@ -117,7 +118,7 @@ public class GLContextShareSet { /** Indicate that contexts <code>share1</code> and <code>share2</code> will share textures and display lists. Both must be non-null. */ - public static synchronized void registerSharing(GLContext share1, GLContext share2) { + public static synchronized void registerSharing(final GLContext share1, final GLContext share2) { if (share1 == null || share2 == null) { throw new IllegalArgumentException("Both share1 and share2 must be non-null"); } @@ -133,12 +134,12 @@ public class GLContextShareSet { addEntry(share1, share); addEntry(share2, share); if (DEBUG) { - System.err.println("GLContextShareSet: registereSharing: 1: " + + System.err.println("GLContextShareSet: registereSharing: 1: " + toHexString(share1.getHandle()) + ", 2: " + toHexString(share2.getHandle())); - } + } } - public static synchronized void unregisterSharing(GLContext lastContext) { + public static synchronized void unregisterSharing(final GLContext lastContext) { if (lastContext == null) { throw new IllegalArgumentException("Last context is null"); } @@ -155,9 +156,9 @@ public class GLContextShareSet { throw new GLException("Last context's share set contains no destroyed context"); } if (DEBUG) { - System.err.println("GLContextShareSet: unregisterSharing: " + + System.err.println("GLContextShareSet: unregisterSharing: " + toHexString(lastContext.getHandle())+", entries: "+s.size()); - } + } for(Iterator<GLContext> iter = s.iterator() ; iter.hasNext() ; ) { GLContext ctx = iter.next(); if(null == removeEntry(ctx)) { @@ -165,61 +166,85 @@ public class GLContextShareSet { } } } - - private static synchronized Set<GLContext> getCreatedSharedImpl(GLContext context) { + + /** Returns true if the given GLContext is shared, otherwise false. */ + public static synchronized boolean isShared(final GLContext context) { if (context == null) { throw new IllegalArgumentException("context is null"); } final ShareSet share = entryFor(context); - if (share != null) { - return share.getCreatedShares(); + return share != null; + } + + /** Returns one created GLContext shared with the given <code>context</code>, otherwise return <code>null</code>. */ + public static synchronized GLContext getCreatedShare(final GLContext context) { + final ShareSet share = entryFor(context); + if (share == null) { + return null; } - return null; + return share.getCreatedShare(context); } - - public static synchronized boolean isShared(GLContext context) { + + private static synchronized Set<GLContext> getCreatedSharesImpl(final GLContext context) { if (context == null) { throw new IllegalArgumentException("context is null"); } final ShareSet share = entryFor(context); - return share != null; - } - - public static synchronized boolean hasCreatedSharedLeft(GLContext context) { - final Set<GLContext> s = getCreatedSharedImpl(context); - return null != s && s.size()>0 ; - } - - /** currently not used .. - public static synchronized Set<GLContext> getCreatedShared(GLContext context) { - final Set<GLContext> s = getCreatedSharedImpl(context); - if (s == null) { - throw new GLException("context is unknown: "+context); + if (share != null) { + return share.getCreatedShares(); } - return s; + return null; } - - public static synchronized Set<GLContext> getDestroyedShared(GLContext context) { + private static synchronized Set<GLContext> getDestroyedSharesImpl(final GLContext context) { if (context == null) { throw new IllegalArgumentException("context is null"); } - ShareSet share = entryFor(context); - if (share == null) { - throw new GLException("context is unknown: "+context); - } - return share.getDestroyedShares(); - } */ - - public static synchronized GLContext getShareContext(GLContext contextToCreate) { - ShareSet share = entryFor(contextToCreate); - if (share == null) { - return null; + final ShareSet share = entryFor(context); + if (share != null) { + return share.getDestroyedShares(); } - return share.getCreatedShare(contextToCreate); + return null; + } + + /** Returns true if the given GLContext has shared and created GLContext left including itself, otherwise false. */ + public static synchronized boolean hasCreatedSharedLeft(GLContext context) { + final Set<GLContext> s = getCreatedSharesImpl(context); + return null != s && s.size() > 0; + } + + /** Returns a new array-list of created GLContext shared with the given GLContext. */ + public static synchronized ArrayList<GLContext> getCreatedShares(final GLContext context) { + final ArrayList<GLContext> otherShares = new ArrayList<GLContext>(); + final Set<GLContext> createdShares = getCreatedSharesImpl(context); + if( null != createdShares ) { + for (final Iterator<GLContext> iter = createdShares.iterator(); iter.hasNext(); ) { + final GLContext ctx = iter.next(); + if (ctx != context) { + otherShares.add(ctx); + } + } + } + return otherShares; } - public static synchronized boolean contextCreated(GLContext context) { - ShareSet share = entryFor(context); + /** Returns a new array-list of destroyed GLContext shared with the given GLContext. */ + public static synchronized ArrayList<GLContext> getDestroyedShares(final GLContext context) { + final ArrayList<GLContext> otherShares = new ArrayList<GLContext>(); + final Set<GLContext> destroyedShares = getDestroyedSharesImpl(context); + if( null != destroyedShares ) { + for (final Iterator<GLContext> iter = destroyedShares.iterator(); iter.hasNext(); ) { + final GLContext ctx = iter.next(); + if (ctx != context) { + otherShares.add(ctx); + } + } + } + return otherShares; + } + + /** Mark the given GLContext as being created. */ + public static synchronized boolean contextCreated(final GLContext context) { + final ShareSet share = entryFor(context); if (share != null) { share.contextCreated(context); return true; @@ -227,8 +252,9 @@ public class GLContextShareSet { return false; } - public static synchronized boolean contextDestroyed(GLContext context) { - ShareSet share = entryFor(context); + /** Mark the given GLContext as being destroyed. */ + public static synchronized boolean contextDestroyed(final GLContext context) { + final ShareSet share = entryFor(context); if (share != null) { share.contextDestroyed(context); return true; @@ -236,48 +262,24 @@ public class GLContextShareSet { return false; } - /** In order to avoid glGet calls for buffer object checks related - to glVertexPointer, etc. calls as well as glMapBuffer calls, we - need to share the same GLBufferSizeTracker object between - contexts sharing textures and display lists. For now we keep - this mechanism orthogonal to the GLObjectTracker to hopefully - keep things easier to understand. (The GLObjectTracker is - currently only needed in a fairly esoteric case, when the - Java2D/JOGL bridge is active, but the GLBufferSizeTracker - mechanism is now always required.) */ - public static void synchronizeBufferObjectSharing(GLContext olderContextOrNull, GLContext newContext) { - GLContextImpl older = (GLContextImpl) olderContextOrNull; - GLContextImpl newer = (GLContextImpl) newContext; - GLBufferSizeTracker tracker = null; - if (older != null) { - tracker = older.getBufferSizeTracker(); - assert (tracker != null) - : "registerForBufferObjectSharing was not called properly for the older context, or has a bug in it"; - } - if (tracker == null) { - tracker = new GLBufferSizeTracker(); - } - newer.setBufferSizeTracker(tracker); - } - //---------------------------------------------------------------------- // Internals only below this point - - private static ShareSet entryFor(GLContext context) { - return (ShareSet) shareMap.get(context); + + private static ShareSet entryFor(final GLContext context) { + return shareMap.get(context); } - private static void addEntry(GLContext context, ShareSet share) { + private static void addEntry(final GLContext context, final ShareSet share) { if (shareMap.get(context) == null) { shareMap.put(context, share); } } - private static ShareSet removeEntry(GLContext context) { - return (ShareSet) shareMap.remove(context); + private static ShareSet removeEntry(final GLContext context) { + return shareMap.remove(context); } - - protected static String toHexString(long hex) { + + private static String toHexString(long hex) { return "0x" + Long.toHexString(hex); - } + } } diff --git a/src/jogl/classes/jogamp/opengl/GLDebugMessageHandler.java b/src/jogl/classes/jogamp/opengl/GLDebugMessageHandler.java index b950c2fdf..2c947693c 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; @@ -37,68 +39,62 @@ import javax.media.opengl.GLException; import com.jogamp.common.os.Platform; import com.jogamp.gluegen.runtime.ProcAddressTable; -import jogamp.opengl.gl4.GL4bcProcAddressTable; +import com.jogamp.opengl.GLExtensions; /** * The GLDebugMessageHandler, handling <i>GL_ARB_debug_output</i> or <i>GL_AMD_debug_output</i> * debug messages.<br> - * + * * <p>An instance must be bound to the current thread's GLContext to achieve thread safety.</p> - * - * <p>A native callback function is registered at {@link #enable(boolean) enable(true)}, - * which forwards received messages to the added {@link GLDebugListener} directly. + * + * <p>A native callback function is registered at {@link #enable(boolean) enable(true)}, + * which forwards received messages to the added {@link GLDebugListener} directly. * Hence the {@link GLDebugListener#messageSent(GLDebugMessage)} implementation shall * return as fast as possible.</p> - * + * * <p>In case no <i>GL_ARB_debug_output</i> is available, but <i>GL_AMD_debug_output</i>, * the messages are translated to <i>ARB</i> {@link GLDebugMessage}, using {@link GLDebugMessage#translateAMDEvent(javax.media.opengl.GLContext, long, int, int, int, String)}.</p> */ public class GLDebugMessageHandler { - /** Extension <i>GL_ARB_debug_output</i> implementing GLDebugMessage */ - public static final String GL_ARB_debug_output = "GL_ARB_debug_output".intern(); - - /** Extension <i>GL_AMD_debug_output</i> implementing GLDebugMessage */ - public static final String GL_AMD_debug_output = "GL_AMD_debug_output".intern(); - private static final boolean DEBUG = Debug.debug("GLDebugMessageHandler"); - + private static final int EXT_ARB = 1; - private static final int EXT_AMD = 2; - + private static final int EXT_AMD = 2; + static { if ( !initIDs0() ) { throw new NativeWindowException("Failed to initialize GLDebugMessageHandler jmethodIDs"); - } + } } - - private final GLContextImpl ctx; + + private final GLContextImpl ctx; private final ListenerSyncedImplStub<GLDebugListener> listenerImpl; - + // licefycle: init - EOL private String extName; private int extType; private long glDebugMessageCallbackProcAddress; - private boolean extAvailable; + private boolean extAvailable; private boolean synchronous; - + // licefycle: enable - disable/EOL private long handle; - + /** * @param ctx the associated GLContext * @param glDebugExtension chosen extension to use */ - public GLDebugMessageHandler(GLContextImpl ctx) { + public GLDebugMessageHandler(GLContextImpl ctx) { this.ctx = ctx; - this.listenerImpl = new ListenerSyncedImplStub<GLDebugListener>(); + this.listenerImpl = new ListenerSyncedImplStub<GLDebugListener>(); this.glDebugMessageCallbackProcAddress = 0; this.extName = null; this.extType = 0; - this.extAvailable = false; + this.extAvailable = false; this.handle = 0; this.synchronous = true; } - + public void init(boolean enable) { if(DEBUG) { System.err.println("GLDebugMessageHandler.init("+enable+")"); @@ -110,18 +106,31 @@ public class GLDebugMessageHandler { System.err.println("GLDebugMessageHandler.init("+enable+") .. n/a"); } } - + + private final long getAddressFor(final ProcAddressTable table, final String functionName) { + return AccessController.doPrivileged(new PrivilegedAction<Long>() { + @Override + 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()) { return; } - + if( !ctx.isGLDebugEnabled() ) { if(DEBUG) { System.err.println("GLDebugMessageHandler: GL DEBUG not set in ARB ctx options: "+ctx.getGLVersion()); } - return; + return; } if(Platform.OS_TYPE == Platform.OSType.WINDOWS && Platform.is32Bit()) { // Currently buggy, ie. throws an exception after leaving the native callback. @@ -131,101 +140,103 @@ public class GLDebugMessageHandler { } return; } - if( ctx.isExtensionAvailable(GL_ARB_debug_output) ) { - extName = GL_ARB_debug_output; + if( ctx.isExtensionAvailable(GLExtensions.ARB_debug_output) ) { + extName = GLExtensions.ARB_debug_output; extType = EXT_ARB; - } else if( ctx.isExtensionAvailable(GL_AMD_debug_output) ) { - extName = GL_AMD_debug_output; + } else if( ctx.isExtensionAvailable(GLExtensions.AMD_debug_output) ) { + extName = GLExtensions.AMD_debug_output; extType = EXT_AMD; } if(DEBUG) { System.err.println("GLDebugMessageHandler: Using extension: <"+extName+">"); } - + if(0 == extType) { if(DEBUG) { System.err.println("GLDebugMessageHandler: No extension available! "+ctx.getGLVersion()); + System.err.println("GL_EXTENSIONS "+ctx.getGLExtensionCount()); + System.err.println(ctx.getGLExtensionsString()); } return; } - + 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; + case EXT_ARB: + glDebugMessageCallbackProcAddress = getAddressFor(procAddressTable, "glDebugMessageCallbackARB"); break; - case EXT_AMD: - glDebugMessageCallbackProcAddress = desktopProcAddressTable._addressof_glDebugMessageCallbackAMD; + case EXT_AMD: + glDebugMessageCallbackProcAddress = getAddressFor(procAddressTable, "glDebugMessageCallbackAMD"); break; } } else { + glDebugMessageCallbackProcAddress = 0; if(DEBUG) { - System.err.println("Non desktop context not supported"); - } + System.err.println("Non desktop context not supported"); + } } extAvailable = 0 < extType && null != extName && 0 != glDebugMessageCallbackProcAddress; - + if(DEBUG) { System.err.println("GLDebugMessageHandler: extAvailable: "+extAvailable+", glDebugMessageCallback* : 0x"+Long.toHexString(glDebugMessageCallbackProcAddress)); } - + if(!extAvailable) { glDebugMessageCallbackProcAddress = 0; } - + handle = 0; } public final boolean isAvailable() { return extAvailable; } - + /** - * @return The extension implementing the GLDebugMessage feature, - * either {@link #GL_ARB_debug_output} or {@link #GL_AMD_debug_output}. - * If unavailable <i>null</i> is returned. + * @return The extension implementing the GLDebugMessage feature, + * either {@link #GL_ARB_debug_output} or {@link #GL_AMD_debug_output}. + * If unavailable <i>null</i> is returned. */ public final String getExtension() { return extName; } - + public final boolean isExtensionARB() { - return extName == GL_ARB_debug_output; + return extName == GLExtensions.ARB_debug_output; } - + public final boolean isExtensionAMD() { - return extName == GL_AMD_debug_output; + return extName == GLExtensions.AMD_debug_output; } - + /** - * @see javax.media.opengl.GLContext#isGLDebugSynchronous() + * @see javax.media.opengl.GLContext#isGLDebugSynchronous() */ public final boolean isSynchronous() { return synchronous; } - + /** - * @see javax.media.opengl.GLContext#setGLDebugSynchronous(boolean) + * @see javax.media.opengl.GLContext#setGLDebugSynchronous(boolean) */ public final void setSynchronous(boolean synchronous) { this.synchronous = synchronous; if( isEnabled() ) { setSynchronousImpl(); } - } + } 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); } } } - + /** - * @see javax.media.opengl.GLContext#enableGLDebugMessage(boolean) + * @see javax.media.opengl.GLContext#enableGLDebugMessage(boolean) */ public final void enable(boolean enable) throws GLException { ctx.validateCurrent(); @@ -233,7 +244,7 @@ public class GLDebugMessageHandler { return; } enableImpl(enable); - } + } final void enableImpl(boolean enable) throws GLException { if(enable) { if(0 == handle) { @@ -247,19 +258,19 @@ public class GLDebugMessageHandler { if(0 != handle) { unregister0(glDebugMessageCallbackProcAddress, handle); handle = 0; - } + } } if(DEBUG) { System.err.println("GLDebugMessageHandler: enable("+enable+") -> 0x" + Long.toHexString(handle)); } } - + public final boolean isEnabled() { return 0 != handle; } - public final int listenerSize() { - return listenerImpl.size(); + public final int listenerSize() { + return listenerImpl.size(); } - + public final void addListener(GLDebugListener listener) { listenerImpl.addListener(-1, listener); } @@ -267,11 +278,11 @@ public class GLDebugMessageHandler { public final void addListener(int index, GLDebugListener listener) { listenerImpl.addListener(index, listener); } - + public final void removeListener(GLDebugListener listener) { listenerImpl.removeListener(listener); } - + private final void sendMessage(GLDebugMessage msg) { synchronized(listenerImpl) { if(DEBUG) { @@ -283,25 +294,26 @@ public class GLDebugMessageHandler { } } } - + public static class StdErrGLDebugListener implements GLDebugListener { boolean threadDump; - + public StdErrGLDebugListener(boolean threadDump) { this.threadDump = threadDump; } + @Override public void messageSent(GLDebugMessage event) { System.err.println(event); if(threadDump) { Thread.dumpStack(); } - } + } } - + // // native -> java // - + protected final void glDebugMessageARB(int source, int type, int id, int severity, String msg) { final GLDebugMessage event = new GLDebugMessage(ctx, System.currentTimeMillis(), source, type, id, severity, msg); sendMessage(event); @@ -311,11 +323,11 @@ public class GLDebugMessageHandler { final GLDebugMessage event = GLDebugMessage.translateAMDEvent(ctx, System.currentTimeMillis(), id, category, severity, msg); sendMessage(event); } - + // // java -> native - // - + // + private static native boolean initIDs0(); private native long register0(long glDebugMessageCallbackProcAddress, int extType); private native void unregister0(long glDebugMessageCallbackProcAddress, long handle); diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java index 3c60eb699..c914b5e10 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -47,7 +47,9 @@ import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.OffscreenLayerSurface; import javax.media.nativewindow.ProxySurface; -import javax.media.nativewindow.SurfaceChangeable; +import javax.media.nativewindow.MutableSurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesImmutable; @@ -55,10 +57,16 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; +import javax.media.opengl.GLFBODrawable; +import javax.media.opengl.GLOffscreenAutoDrawable; import javax.media.opengl.GLPbuffer; import javax.media.opengl.GLProfile; import com.jogamp.nativewindow.MutableGraphicsConfiguration; +import com.jogamp.nativewindow.DelegatedUpstreamSurfaceHookWithSurfaceSize; +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; +import com.jogamp.opengl.GLAutoDrawableDelegate; +import com.jogamp.opengl.GLRendererQuirks; /** Extends GLDrawableFactory with a few methods for handling @@ -66,29 +74,70 @@ import com.jogamp.nativewindow.MutableGraphicsConfiguration; Independent Bitmaps on Windows, pixmaps on X11). Direct access to these GLDrawables is not supplied directly to end users, though they may be instantiated by the GLJPanel implementation. */ +@SuppressWarnings("deprecation") public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { - protected static final boolean DEBUG = GLDrawableImpl.DEBUG; + protected static final boolean DEBUG = GLDrawableFactory.DEBUG; // allow package access protected GLDrawableFactoryImpl() { super(); } /** - * Returns the shared context mapped to the <code>device</code> {@link AbstractGraphicsDevice#getConnection()}, + * Returns the shared resource mapped to the <code>device</code> {@link AbstractGraphicsDevice#getConnection()}, * either a pre-existing or newly created, or <code>null</code> if creation failed or not supported.<br> + * Creation of the shared resource is tried only once. + * + * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + */ + protected final SharedResourceRunner.Resource getOrCreateSharedResource(AbstractGraphicsDevice device) { + try { + device = validateDevice(device); + if( null != device) { + return getOrCreateSharedResourceImpl( device ); + } + } catch (GLException gle) { + if(DEBUG) { + System.err.println("Catched Exception on thread "+getThreadName()); + gle.printStackTrace(); + } + } + return null; + } + protected abstract SharedResourceRunner.Resource getOrCreateSharedResourceImpl(AbstractGraphicsDevice device); + + /** + * Returns the shared context mapped to the <code>device</code> {@link AbstractGraphicsDevice#getConnection()}, + * either a pre-existing or newly created, or <code>null</code> if creation failed or <b>not supported</b>.<br> * Creation of the shared context is tried only once. * * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. */ public final GLContext getOrCreateSharedContext(AbstractGraphicsDevice device) { - device = validateDevice(device); - if(null!=device) { - return getOrCreateSharedContextImpl(device); + final SharedResourceRunner.Resource sr = getOrCreateSharedResource( device ); + if(null!=sr) { + return sr.getContext(); } return null; } - protected abstract GLContext getOrCreateSharedContextImpl(AbstractGraphicsDevice device); - + + @Override + protected final boolean createSharedResourceImpl(AbstractGraphicsDevice device) { + final SharedResourceRunner.Resource sr = getOrCreateSharedResource( device ); + if(null!=sr) { + return sr.isValid(); + } + return false; + } + + @Override + public final GLRendererQuirks getRendererQuirks(AbstractGraphicsDevice device) { + final SharedResourceRunner.Resource sr = getOrCreateSharedResource( device ); + if(null!=sr) { + return sr.getRendererQuirks(); + } + return null; + } + /** * Returns the shared device mapped to the <code>device</code> {@link AbstractGraphicsDevice#getConnection()}, * either a preexisting or newly created, or <code>null</code> if creation failed or not supported.<br> @@ -97,25 +146,14 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. */ protected final AbstractGraphicsDevice getOrCreateSharedDevice(AbstractGraphicsDevice device) { - if(null==device) { - device = getDefaultDevice(); - if(null==device) { - throw new InternalError("no default device"); - } - if (GLProfile.DEBUG) { - System.err.println("Info: GLDrawableFactoryImpl.getOrCreateSharedContext: using default device : "+device); - } - } else if( !getIsDeviceCompatible(device) ) { - if (GLProfile.DEBUG) { - System.err.println("Info: GLDrawableFactoryImpl.getOrCreateSharedContext: device not compatible : "+device); - } - return null; + final SharedResourceRunner.Resource sr = getOrCreateSharedResource( device ); + if(null!=sr) { + return sr.getDevice(); } - return getOrCreateSharedDeviceImpl(device); + return null; } - protected abstract AbstractGraphicsDevice getOrCreateSharedDeviceImpl(AbstractGraphicsDevice device); - /** + /** * Returns the GLDynamicLookupHelper * @param profile if EGL/ES, profile <code>1</code> refers to ES1 and <code>2</code> to ES2, * otherwise the profile is ignored. @@ -125,32 +163,48 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { //--------------------------------------------------------------------------- // Dispatching GLDrawable construction in respect to the NativeSurface Capabilities // - public GLDrawable createGLDrawable(NativeSurface target) { + @Override + public final GLDrawable createGLDrawable(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); } final MutableGraphicsConfiguration config = (MutableGraphicsConfiguration) target.getGraphicsConfiguration(); - GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - AbstractGraphicsDevice adevice = config.getScreen().getDevice(); + final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final AbstractGraphicsDevice adevice = config.getScreen().getDevice(); + final boolean isFBOAvailable = GLContext.isFBOAvailable(adevice, chosenCaps.getGLProfile()); GLDrawable result = null; adevice.lock(); try { final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(target, true); if(null != ols) { - // layered surface -> Offscreen/PBuffer - final GLCapabilities chosenCapsMod = (GLCapabilities) chosenCaps.cloneMutable(); - chosenCapsMod.setOnscreen(false); - chosenCapsMod.setPBuffer(canCreateGLPbuffer(adevice)); + final GLCapabilitiesImmutable chosenCapsMod = GLGraphicsConfigurationUtil.fixOffscreenGLCapabilities(chosenCaps, this, adevice); + + // layered surface -> Offscreen/[FBO|PBuffer] + if( !chosenCapsMod.isFBO() && !chosenCapsMod.isPBuffer() ) { + throw new GLException("Neither FBO nor Pbuffer is available for "+chosenCapsMod+", "+target); + } config.setChosenCapabilities(chosenCapsMod); + ols.setChosenCapabilities(chosenCapsMod); if(DEBUG) { - System.err.println("GLDrawableFactoryImpl.createGLDrawable -> OnscreenDrawable -> Offscreen-Layer: "+target); + System.err.println("GLDrawableFactoryImpl.createGLDrawable -> OnscreenDrawable -> Offscreen-Layer"); + System.err.println("chosenCaps: "+chosenCaps); + System.err.println("chosenCapsMod: "+chosenCapsMod); + System.err.println("OffscreenLayerSurface: **** "+ols); + System.err.println("Target: **** "+target); + Thread.dumpStack(); } - if( ! ( target instanceof SurfaceChangeable ) ) { + if( ! ( target instanceof MutableSurface ) ) { throw new IllegalArgumentException("Passed NativeSurface must implement SurfaceChangeable for offscreen layered surface: "+target); } - result = createOffscreenDrawableImpl(target); + if( chosenCapsMod.isFBO() ) { + result = createFBODrawableImpl(target, chosenCapsMod, 0); + } else { + result = createOffscreenDrawableImpl(target); + } } else if(chosenCaps.isOnscreen()) { // onscreen + final GLCapabilitiesImmutable chosenCapsMod = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps); + config.setChosenCapabilities(chosenCapsMod); if(DEBUG) { System.err.println("GLDrawableFactoryImpl.createGLDrawable -> OnscreenDrawable: "+target); } @@ -158,12 +212,20 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { } else { // offscreen if(DEBUG) { - System.err.println("GLDrawableFactoryImpl.createGLDrawable -> OffScreenDrawable (PBuffer: "+chosenCaps.isPBuffer()+"): "+target); + System.err.println("GLDrawableFactoryImpl.createGLDrawable -> OffScreenDrawable, FBO chosen / avail, PBuffer: "+ + chosenCaps.isFBO()+" / "+isFBOAvailable+", "+chosenCaps.isPBuffer()+": "+target); + } + if( ! ( target instanceof MutableSurface ) ) { + throw new IllegalArgumentException("Passed NativeSurface must implement MutableSurface for offscreen: "+target); } - if( ! ( target instanceof SurfaceChangeable ) ) { - throw new IllegalArgumentException("Passed NativeSurface must implement SurfaceChangeable for offscreen: "+target); + if( chosenCaps.isFBO() && isFBOAvailable ) { + // need to hook-up a native dummy surface since source may not have & use minimum GLCapabilities for it w/ same profile + final ProxySurface dummySurface = createDummySurfaceImpl(adevice, false, new GLCapabilities(chosenCaps.getGLProfile()), (GLCapabilitiesImmutable)config.getRequestedCapabilities(), null, 64, 64); + dummySurface.setUpstreamSurfaceHook(new DelegatedUpstreamSurfaceHookWithSurfaceSize(dummySurface.getUpstreamSurfaceHook(), target)); + result = createFBODrawableImpl(dummySurface, chosenCaps, 0); + } else { + result = createOffscreenDrawableImpl(target); } - result = createOffscreenDrawableImpl(target); } } finally { adevice.unlock(); @@ -176,128 +238,256 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { //--------------------------------------------------------------------------- // - // Onscreen GLDrawable construction + // Onscreen GLDrawable construction // protected abstract GLDrawableImpl createOnscreenDrawableImpl(NativeSurface target); //--------------------------------------------------------------------------- // - // PBuffer GLDrawable construction + // PBuffer Offscreen GLAutoDrawable construction // - public abstract boolean canCreateGLPbuffer(AbstractGraphicsDevice device); + @Override + public abstract boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp); - public GLPbuffer createGLPbuffer(AbstractGraphicsDevice deviceReq, + @Override + public final GLPbuffer createGLPbuffer(AbstractGraphicsDevice deviceReq, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, int width, int height, GLContext shareWith) { - if(height<=0 || height<=0) { - throw new GLException("Width and height of pbuffer must be positive (were (" + - width + ", " + height + "))"); + if(width<=0 || height<=0) { + throw new GLException("initial size must be positive (were (" + width + " x " + height + "))"); } - AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); if(null == device) { throw new GLException("No shared device for requested: "+deviceReq); } - - if (!canCreateGLPbuffer(device)) { - throw new GLException("Pbuffer support not available with device: "+device); - } - - GLCapabilitiesImmutable capsChosen = GLGraphicsConfigurationUtil.fixGLPBufferGLCapabilities(capsRequested); - GLDrawableImpl drawable = null; - device.lock(); - try { - drawable = (GLDrawableImpl) createGLDrawable( createOffscreenSurfaceImpl(device, capsChosen, capsRequested, chooser, width, height) ); - if(null != drawable) { - drawable.setRealized(true); - } - } finally { - device.unlock(); + if ( !canCreateGLPbuffer(device, capsRequested.getGLProfile()) ) { + throw new GLException("Pbuffer not available with device: "+device); } - if(null==drawable) { - throw new GLException("Could not create Pbuffer drawable for: "+device+", "+capsChosen+", "+width+"x"+height); + final GLCapabilitiesImmutable capsChosen = GLGraphicsConfigurationUtil.fixGLPBufferGLCapabilities(capsRequested); + final GLDrawableImpl drawable = createOffscreenDrawableImpl( createMutableSurfaceImpl(device, true, capsChosen, capsRequested, chooser, + new UpstreamSurfaceHookMutableSize(width, height) ) ); + if(null != drawable) { + drawable.setRealized(true); } - return new GLPbufferImpl( drawable, shareWith); - } + return new GLPbufferImpl( drawable, (GLContextImpl) drawable.createContext(shareWith) ); + } //--------------------------------------------------------------------------- // - // Offscreen GLDrawable construction + // Offscreen GLDrawable construction // - protected abstract GLDrawableImpl createOffscreenDrawableImpl(NativeSurface target) ; + @Override + public final boolean canCreateFBO(AbstractGraphicsDevice deviceReq, GLProfile glp) { + AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); + if(null == device) { + throw new GLException("No shared device for requested: "+deviceReq); + } + return GLContext.isFBOAvailable(device, glp); + } + + @Override + public final GLOffscreenAutoDrawable createOffscreenAutoDrawable(AbstractGraphicsDevice deviceReq, + GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, + int width, int height, + GLContext shareWith) { + final GLDrawable drawable = createOffscreenDrawable( deviceReq, capsRequested, chooser, width, height ); + drawable.setRealized(true); + final GLContext context = drawable.createContext(shareWith); + if(drawable instanceof GLFBODrawableImpl) { + return new GLOffscreenAutoDrawableImpl.FBOImpl( (GLFBODrawableImpl)drawable, context, null, null ); + } + return new GLOffscreenAutoDrawableImpl( drawable, context, null, null); + } + + @Override + public final GLOffscreenAutoDrawable createOffscreenAutoDrawable(AbstractGraphicsDevice deviceReq, + GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, + int width, int height) { + final GLDrawable drawable = createOffscreenDrawable( deviceReq, capsRequested, chooser, width, height ); + drawable.setRealized(true); + if(drawable instanceof GLFBODrawableImpl) { + return new GLOffscreenAutoDrawableImpl.FBOImpl( (GLFBODrawableImpl)drawable, null, null, null ); + } + return new GLOffscreenAutoDrawableImpl( drawable, null, null, null); + } - public GLDrawable createOffscreenDrawable(AbstractGraphicsDevice deviceReq, + @Override + public final GLAutoDrawable createDummyAutoDrawable(AbstractGraphicsDevice deviceReq, boolean createNewDevice, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { + final GLDrawable drawable = createDummyDrawable(deviceReq, createNewDevice, capsRequested, chooser); + drawable.setRealized(true); + final GLAutoDrawable sharedDrawable = new GLAutoDrawableDelegate(drawable, null, null, true /*ownDevice*/, null) { }; + return sharedDrawable; + } + + @Override + public final GLDrawable createOffscreenDrawable(AbstractGraphicsDevice deviceReq, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, - int width, - int height) { + int width, int height) { if(width<=0 || height<=0) { - throw new GLException("Width and height of pbuffer must be positive (were (" + - width + ", " + height + "))"); + throw new GLException("initial size must be positive (were (" + width + " x " + height + "))"); } - AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); + final AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); if(null == device) { throw new GLException("No shared device for requested: "+deviceReq); } - GLCapabilitiesImmutable capsChosen = GLGraphicsConfigurationUtil.fixOffScreenGLCapabilities(capsRequested, canCreateGLPbuffer(deviceReq)); - device.lock(); - try { - return createGLDrawable( createOffscreenSurfaceImpl(device, capsChosen, capsRequested, chooser, width, height) ); - } finally { - device.unlock(); + final GLCapabilitiesImmutable capsChosen = GLGraphicsConfigurationUtil.fixOffscreenGLCapabilities(capsRequested, this, device); + + if( capsChosen.isFBO() ) { + // Use minimum GLCapabilities for the dummy surface w/ same profile + final ProxySurface dummySurface = createDummySurfaceImpl(device, true, new GLCapabilities(capsChosen.getGLProfile()), capsRequested, null, width, height); + final GLDrawableImpl dummyDrawable = createOnscreenDrawableImpl(dummySurface); + return new GLFBODrawableImpl.ResizeableImpl(this, dummyDrawable, dummySurface, capsChosen, 0); } + return createOffscreenDrawableImpl( createMutableSurfaceImpl(device, true, capsChosen, capsRequested, chooser, + new UpstreamSurfaceHookMutableSize(width, height) ) ); } - public NativeSurface createOffscreenSurface(AbstractGraphicsDevice deviceReq, - GLCapabilitiesImmutable capsRequested, - GLCapabilitiesChooser chooser, - int width, int height) { - AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); + @Override + public final GLDrawable createDummyDrawable(AbstractGraphicsDevice deviceReq, boolean createNewDevice, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { + final AbstractGraphicsDevice device = createNewDevice ? getOrCreateSharedDevice(deviceReq) : deviceReq; if(null == device) { - throw new GLException("No shared device for requested: "+deviceReq); + throw new GLException("No shared device for requested: "+deviceReq+", createNewDevice "+createNewDevice); + } + if( !createNewDevice ) { + device.lock(); } - GLCapabilitiesImmutable capsChosen = GLGraphicsConfigurationUtil.fixOffScreenGLCapabilities(capsRequested, canCreateGLPbuffer(deviceReq)); - - device.lock(); try { - return createOffscreenSurfaceImpl(device, capsChosen, capsRequested, chooser, width, height); + final ProxySurface dummySurface = createDummySurfaceImpl(device, createNewDevice, capsRequested, capsRequested, chooser, 64, 64); + return createOnscreenDrawableImpl(dummySurface); } finally { - device.unlock(); + if( !createNewDevice ) { + device.unlock(); + } } } + /** Creates a platform independent unrealized FBO offscreen GLDrawable */ + protected final GLFBODrawable createFBODrawableImpl(NativeSurface dummySurface, GLCapabilitiesImmutable fboCaps, int textureUnit) { + final GLDrawableImpl dummyDrawable = createOnscreenDrawableImpl(dummySurface); + return new GLFBODrawableImpl(this, dummyDrawable, dummySurface, fboCaps, textureUnit); + } + + /** Creates a platform dependent unrealized offscreen pbuffer/pixmap GLDrawable instance */ + protected abstract GLDrawableImpl createOffscreenDrawableImpl(NativeSurface target) ; + /** - * creates an offscreen NativeSurface, which must implement SurfaceChangeable as well, - * so the windowing system related implementation is able to set the surface handle. + * Creates a mutable {@link ProxySurface} w/o defined surface handle. + * <p> + * It's {@link AbstractGraphicsConfiguration} is properly set according to the given {@link GLCapabilitiesImmutable}. + * </p> + * <p> + * Lifecycle (destruction) of the TBD surface handle shall be handled by the caller. + * </p> + * @param device a valid platform dependent target device. + * @param createNewDevice if <code>true</code> a new independent device instance is created using <code>device</code> details, + * otherwise <code>device</code> instance is used as-is. + * @param capsChosen + * @param capsRequested + * @param chooser the custom chooser, may be null for default + * @param upstreamHook surface size information and optional control of the surface's lifecycle + * @return the created {@link MutableSurface} instance w/o defined surface handle */ - protected abstract NativeSurface createOffscreenSurfaceImpl(AbstractGraphicsDevice device, - GLCapabilitiesImmutable capabilities, GLCapabilitiesImmutable capsRequested, - GLCapabilitiesChooser chooser, - int width, int height); + protected abstract ProxySurface createMutableSurfaceImpl(AbstractGraphicsDevice device, boolean createNewDevice, + GLCapabilitiesImmutable capsChosen, + GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstreamHook); - public ProxySurface createProxySurface(AbstractGraphicsDevice device, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { + /** + * A dummy surface is not visible on screen and will not be used to render directly to, + * it maybe on- or offscreen. + * <p> + * It is used to allow the creation of a {@link GLDrawable} and {@link GLContext} to query information. + * It also allows creation of framebuffer objects which are used for rendering or using a shared GLContext w/o actually rendering to a usable framebuffer. + * </p> + * <p> + * Creates a new independent device instance using <code>deviceReq</code> details. + * </p> + * @param deviceReq which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. + * @param requestedCaps + * @param chooser the custom chooser, may be null for default + * @param width the initial width as returned by {@link NativeSurface#getWidth()}, not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getHeight()}, not the actual dummy surface height, + * The latter is platform specific and small + * + * @return the created {@link ProxySurface} instance w/o defined surface handle but platform specific {@link UpstreamSurfaceHook}. + */ + public final ProxySurface createDummySurface(AbstractGraphicsDevice deviceReq, GLCapabilitiesImmutable requestedCaps, GLCapabilitiesChooser chooser, + int width, int height) { + final AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); if(null == device) { - throw new GLException("No shared device for requested: "+device); + throw new GLException("No shared device for requested: "+deviceReq); + } + return createDummySurfaceImpl(device, true, requestedCaps, requestedCaps, chooser, width, height); + } + + /** + * A dummy surface is not visible on screen and will not be used to render directly to, + * it maybe on- or offscreen. + * <p> + * It is used to allow the creation of a {@link GLDrawable} and {@link GLContext} to query information. + * It also allows creation of framebuffer objects which are used for rendering or using a shared GLContext w/o actually rendering to a usable framebuffer. + * </p> + * @param device a valid platform dependent target device. + * @param createNewDevice if <code>true</code> a new device instance is created using <code>device</code> details, + * otherwise <code>device</code> instance is used as-is. + * @param chosenCaps + * @param requestedCaps + * @param chooser the custom chooser, may be null for default + * @param width the initial width as returned by {@link NativeSurface#getWidth()}, not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getHeight()}, not the actual dummy surface height, + * The latter is platform specific and small + * @return the created {@link ProxySurface} instance w/o defined surface handle but platform specific {@link UpstreamSurfaceHook}. + */ + public abstract ProxySurface createDummySurfaceImpl(AbstractGraphicsDevice device, boolean createNewDevice, + GLCapabilitiesImmutable chosenCaps, GLCapabilitiesImmutable requestedCaps, GLCapabilitiesChooser chooser, int width, int height); + + //--------------------------------------------------------------------------- + // + // ProxySurface (Wrapped pre-existing native surface) construction + // + + @Override + public ProxySurface createProxySurface(AbstractGraphicsDevice deviceReq, int screenIdx, long windowHandle, + GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream) { + final AbstractGraphicsDevice device = getOrCreateSharedDevice(deviceReq); + if(null == device) { + throw new GLException("No shared device for requested: "+deviceReq); + } + if(0 == windowHandle) { + throw new IllegalArgumentException("Null windowHandle"); } device.lock(); try { - return createProxySurfaceImpl(device, windowHandle, capsRequested, chooser); + return createProxySurfaceImpl(device, screenIdx, windowHandle, capsRequested, chooser, upstream); } finally { device.unlock(); } - } - - protected abstract ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice device, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser); + } + + /** + * Creates a {@link ProxySurface} with a set surface handle. + * <p> + * Implementation is also required to allocate it's own {@link AbstractGraphicsDevice} instance. + * </p> + * @param upstream TODO + */ + protected abstract ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice deviceReq, int screenIdx, long windowHandle, + GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream); //--------------------------------------------------------------------------- // @@ -305,25 +495,17 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { // protected abstract GLContext createExternalGLContextImpl(); - + + @Override public GLContext createExternalGLContext() { - NativeWindowFactory.getDefaultToolkitLock().lock(); - try { - return createExternalGLContextImpl(); - } finally { - NativeWindowFactory.getDefaultToolkitLock().unlock(); - } + return createExternalGLContextImpl(); } protected abstract GLDrawable createExternalGLDrawableImpl(); + @Override public GLDrawable createExternalGLDrawable() { - NativeWindowFactory.getDefaultToolkitLock().lock(); - try { - return createExternalGLDrawableImpl(); - } finally { - NativeWindowFactory.getDefaultToolkitLock().unlock(); - } + return createExternalGLDrawableImpl(); } @@ -342,17 +524,6 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { return (GLDrawableFactoryImpl) getFactory(glp); } - //--------------------------------------------------------------------------- - // Support for Java2D/JOGL bridge on Mac OS X; the external - // GLDrawable mechanism in the public API is sufficient to - // implement this functionality on all other platforms - // - - public abstract boolean canCreateContextOnJava2DSurface(AbstractGraphicsDevice device); - - public abstract GLContext createContextOnJava2DSurface(Object graphics, GLContext shareWith) - throws GLException; - //---------------------------------------------------------------------- // Gamma adjustment support // Thanks to the LWJGL team for illustrating how to make these @@ -399,7 +570,7 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { * normal ahead of time, use resetDisplayGamma(). Throws * IllegalArgumentException if any of the parameters were * out-of-bounds. - * + * * @param gamma The gamma value, typically > 1.0 (default value is * 1.0) * @param brightness The brightness value between -1.0 and 1.0, @@ -436,16 +607,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(); } //------------------------------------------------------ @@ -477,34 +648,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() { - 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 c992b3cb2..0e135d5e0 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,34 +41,52 @@ package jogamp.opengl; import java.util.ArrayList; +import java.util.List; import java.util.HashSet; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.opengl.GL; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; +import javax.media.opengl.GLFBODrawable; import javax.media.opengl.GLRunnable; - -import com.jogamp.opengl.util.Animator; +import javax.media.opengl.GLSharedContextSetter; /** Encapsulates the implementation of most of the GLAutoDrawable's - methods to be able to share it between GLCanvas and GLJPanel. */ - + 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 Object listenersLock = new Object(); - private ArrayList<GLEventListener> listeners; - private HashSet<GLEventListener> listenersToBeInit; + private final Object listenersLock = new Object(); + private final ArrayList<GLEventListener> listeners = new ArrayList<GLEventListener>(); + private final HashSet<GLEventListener> listenersToBeInit = new HashSet<GLEventListener>(); + private final Object glRunnablesLock = new Object(); + private volatile ArrayList<GLRunnableTask> glRunnables = new ArrayList<GLRunnableTask>(); private boolean autoSwapBufferMode; - private Thread skipContextReleaseThread; - private Object glRunnablesLock = new Object(); - private ArrayList<GLRunnable> glRunnables; + private volatile Thread exclusiveContextThread; + /** -1 release, 0 nop, 1 claim */ + private volatile int exclusiveContextSwitch; private GLAnimatorControl animatorCtrl; + private static Runnable nop = new Runnable() { @Override public void run() {} }; + + private GLContext sharedContext; + private GLAutoDrawable sharedAutoDrawable; + public GLDrawableHelper() { reset(); @@ -76,15 +94,76 @@ public class GLDrawableHelper { public final void reset() { synchronized(listenersLock) { - listeners = new ArrayList<GLEventListener>(); - listenersToBeInit = new HashSet<GLEventListener>(); + listeners.clear(); + listenersToBeInit.clear(); } autoSwapBufferMode = true; - skipContextReleaseThread = null; + exclusiveContextThread = null; + exclusiveContextSwitch = 0; synchronized(glRunnablesLock) { - glRunnables = new ArrayList<GLRunnable>(); + glRunnables.clear(); } animatorCtrl = null; + sharedContext = null; + sharedAutoDrawable = null; + } + + public final void setSharedContext(GLContext thisContext, GLContext sharedContext) throws IllegalStateException { + if( null == sharedContext ) { + throw new IllegalStateException("Null shared GLContext"); + } + if( thisContext == sharedContext ) { + throw new IllegalStateException("Shared GLContext same as local"); + } + if( null != this.sharedContext ) { + throw new IllegalStateException("Shared GLContext already set"); + } + if( null != this.sharedAutoDrawable ) { + throw new IllegalStateException("Shared GLAutoDrawable already set"); + } + this.sharedContext = sharedContext; + } + + public final void setSharedAutoDrawable(GLAutoDrawable thisAutoDrawable, GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + if( null == sharedAutoDrawable ) { + throw new IllegalStateException("Null shared GLAutoDrawable"); + } + if( thisAutoDrawable == sharedAutoDrawable ) { + throw new IllegalStateException("Shared GLAutoDrawable same as this"); + } + if( null != this.sharedContext ) { + throw new IllegalStateException("Shared GLContext already set"); + } + if( null != this.sharedAutoDrawable ) { + throw new IllegalStateException("Shared GLAutoDrawable already set"); + } + this.sharedAutoDrawable = sharedAutoDrawable; + } + + /** + * @param shared returns the shared GLContext, based on set shared GLAutoDrawable + * or GLContext. Maybe null if none is set. + * @return true if initialization is pending due to a set shared GLAutoDrawable or GLContext + * which is not ready yet. Otherwise false. + */ + public boolean isSharedGLContextPending(GLContext[] shared) { + final GLContext shareWith; + final boolean pending; + if ( null != sharedAutoDrawable ) { + final boolean allGLELInitialized; + if( sharedAutoDrawable instanceof GLSharedContextSetter ) { + allGLELInitialized = ((GLSharedContextSetter)sharedAutoDrawable).areAllGLEventListenerInitialized(); + } else { + allGLELInitialized = true; // we have to assume 'yes' + } + shareWith = sharedAutoDrawable.getContext(); + pending = null == shareWith || !shareWith.isCreated() || !allGLELInitialized; + } else { + shareWith = sharedContext; + pending = null != shareWith && !shareWith.isCreated(); + } + shared[0] = shareWith; + return pending; } @Override @@ -105,6 +184,207 @@ public class GLDrawableHelper { return sb.toString(); } + /** Limit release calls of {@link #forceNativeRelease(GLContext)} to {@value}. */ + private static final int MAX_RELEASE_ITER = 512; + + /** + * Since GLContext's {@link GLContext#makeCurrent()} and {@link GLContext#release()} + * is recursive, a call to {@link GLContext#release()} may not natively release the context. + * <p> + * This methods continues calling {@link GLContext#release()} until the context has been natively released. + * </p> + * @param ctx + */ + public static final void forceNativeRelease(GLContext ctx) { + int releaseCount = 0; + do { + ctx.release(); + releaseCount++; + if (DEBUG) { + System.err.println("GLDrawableHelper.forceNativeRelease() #"+releaseCount+" -- currentThread "+Thread.currentThread()+" -> "+GLContext.getCurrent()); + } + } while( MAX_RELEASE_ITER > releaseCount && ctx.isCurrent() ); + + if( ctx.isCurrent() ) { + throw new GLException("Context still current after "+MAX_RELEASE_ITER+" releases: "+ctx); + } + } + + /** + * Switch {@link GLContext} / {@link GLDrawable} association. + * <p> + * The <code>oldCtx</code> will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, + * otherwise dis-associate <code>oldCtx</code> from <code>drawable</code> + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) oldCtx.setGLDrawable(null, true);}. + * </p> + * <p> + * Re-associate <code>newCtx</code> with <code>drawable</code> + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. + * </p> + * <p> + * If the old or new context was current on this thread, it is being released before switching the drawable. + * </p> + * <p> + * No locking is being performed on the drawable, caller is required to take care of it. + * </p> + * + * @param drawable the drawable which context is changed + * @param oldCtx the old context, maybe <code>null</code>. + * @param destroyOldCtx if <code>true</code>, destroy the <code>oldCtx</code> + * @param newCtx the new context, maybe <code>null</code> for dis-association. + * @param newCtxCreationFlags additional creation flags if newCtx is not null and not been created yet, see {@link GLContext#setContextCreationFlags(int)} + * + * @see GLAutoDrawable#setContext(GLContext, boolean) + */ + public static final void switchContext(GLDrawable drawable, GLContext oldCtx, boolean destroyOldCtx, GLContext newCtx, int newCtxCreationFlags) { + if( null != oldCtx ) { + if( destroyOldCtx ) { + oldCtx.destroy(); + } else { + oldCtx.setGLDrawable(null, true); // dis-associate old pair + } + } + + if(null!=newCtx) { + newCtx.setContextCreationFlags(newCtxCreationFlags); + newCtx.setGLDrawable(drawable, true); // re-associate new pair + } + } + + /** + * If the drawable is not realized, OP is a NOP. + * <ul> + * <li>release context if current</li> + * <li>destroy old drawable</li> + * <li>create new drawable</li> + * <li>attach new drawable to context</li> + * <li>make context current, if it was current</li> + * </ul> + * <p> + * Locking is performed via {@link GLContext#makeCurrent()} on the passed <code>context</code>. + * </p> + * + * @param drawable + * @param context maybe null + * @return the new drawable + */ + public static final GLDrawableImpl recreateGLDrawable(GLDrawableImpl drawable, GLContext context) { + if( ! drawable.isRealized() ) { + return drawable; + } + final GLContext currentContext = GLContext.getCurrent(); + final GLDrawableFactory factory = drawable.getFactory(); + final NativeSurface surface = drawable.getNativeSurface(); + final ProxySurface proxySurface = (surface instanceof ProxySurface) ? (ProxySurface)surface : null; + + if( null != context ) { + // Ensure to sync GL command stream + if( currentContext != context ) { + context.makeCurrent(); + } + context.getGL().glFinish(); + context.setGLDrawable(null, true); // dis-associate + } + + if(null != proxySurface) { + proxySurface.enableUpstreamSurfaceHookLifecycle(false); + } + try { + drawable.setRealized(false); + drawable = (GLDrawableImpl) factory.createGLDrawable(surface); // [2] + drawable.setRealized(true); + } finally { + if(null != proxySurface) { + proxySurface.enableUpstreamSurfaceHookLifecycle(true); + } + } + + if(null != context) { + context.setGLDrawable(drawable, true); // re-association + } + + if( null != currentContext ) { + currentContext.makeCurrent(); + } + return drawable; + } + + /** + * Performs resize operation on the given drawable, assuming it is offscreen. + * <p> + * The {@link GLDrawableImpl}'s {@link NativeSurface} is being locked during operation. + * In case the holder is an auto drawable or similar, it's lock shall be claimed by the caller. + * </p> + * <p> + * May recreate the drawable via {@link #recreateGLDrawable(GLDrawableImpl, GLContext)} + * in case of a a pbuffer- or pixmap-drawable. + * </p> + * <p> + * FBO drawables are resized w/o drawable destruction. + * </p> + * <p> + * Offscreen resize operation is validated w/ drawable size in the end. + * An exception is thrown if not successful. + * </p> + * + * @param drawable + * @param context + * @param newWidth the new width, it's minimum is capped to 1 + * @param newHeight the new height, it's minimum is capped to 1 + * @return the new drawable in case of an pbuffer/pixmap drawable, otherwise the passed drawable is being returned. + * @throws NativeWindowException is drawable is not offscreen or it's surface lock couldn't be claimed + * @throws GLException may be thrown a resize operation + */ + public static final GLDrawableImpl resizeOffscreenDrawable(GLDrawableImpl drawable, GLContext context, int newWidth, int newHeight) + throws NativeWindowException, GLException + { + final NativeSurface ns = drawable.getNativeSurface(); + final int lockRes = ns.lockSurface(); + if ( NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes ) { + throw new NativeWindowException("Could not lock surface of drawable: "+drawable); + } + boolean validateSize = true; + try { + if( ! drawable.isRealized() ) { + return drawable; + } + if( drawable.getChosenGLCapabilities().isOnscreen() ) { + throw new NativeWindowException("Drawable is not offscreen: "+drawable); + } + if( DEBUG && ( 0>=newWidth || 0>=newHeight) ) { + System.err.println("WARNING: Odd size detected: "+newWidth+"x"+newHeight+", using safe size 1x1. Drawable "+drawable); + Thread.dumpStack(); + } + if( 0 >= newWidth ) { newWidth = 1; validateSize=false; } + if( 0 >= newHeight ) { newHeight = 1; validateSize=false; } + // propagate new size + if( ns instanceof ProxySurface ) { + final ProxySurface ps = (ProxySurface) ns; + final UpstreamSurfaceHook ush = ps.getUpstreamSurfaceHook(); + if(ush instanceof UpstreamSurfaceHook.MutableSize) { + ((UpstreamSurfaceHook.MutableSize)ush).setSize(newWidth, newHeight); + } else if(DEBUG) { // we have to assume UpstreamSurfaceHook contains the new size already, hence size check @ bottom + System.err.println("GLDrawableHelper.resizeOffscreenDrawable: Drawable's offscreen ProxySurface n.a. UpstreamSurfaceHook.MutableSize, but "+ush.getClass().getName()+": "+ush); + } + } else if(DEBUG) { // we have to assume surface contains the new size already, hence size check @ bottom + System.err.println("GLDrawableHelper.resizeOffscreenDrawable: Drawable's offscreen surface n.a. ProxySurface, but "+ns.getClass().getName()+": "+ns); + } + if( drawable instanceof GLFBODrawable ) { + if( null != context && context.isCreated() ) { + ((GLFBODrawable) drawable).resetSize(context.getGL()); + } + } else { + drawable = GLDrawableHelper.recreateGLDrawable(drawable, context); + } + } finally { + ns.unlockSurface(); + } + if( validateSize && ( drawable.getWidth() != newWidth || drawable.getHeight() != newHeight ) ) { + throw new InternalError("Incomplete resize operation: expected "+newWidth+"x"+newHeight+", has: "+drawable); + } + return drawable; + } + public final void addGLEventListener(GLEventListener listener) { addGLEventListener(-1, listener); } @@ -117,88 +397,296 @@ public class GLDrawableHelper { // GLEventListener may be added after context is created, // hence we earmark initialization for the next display call. listenersToBeInit.add(listener); + listeners.add(index, listener); } } - - public final void removeGLEventListener(GLEventListener listener) { + + /** + * Note that no {@link GLEventListener#dispose(GLAutoDrawable)} call is being issued + * due to the lack of a current context. + * Consider calling {@link #disposeGLEventListener(GLAutoDrawable, GLDrawable, GLContext, GLEventListener)}. + * @return the removed listener, or null if listener was not added + */ + public final GLEventListener removeGLEventListener(GLEventListener listener) { synchronized(listenersLock) { - listeners.remove(listener); listenersToBeInit.remove(listener); + return listeners.remove(listener) ? listener : null; + } + } + + public final GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException { + synchronized(listenersLock) { + if(0>index) { + index = listeners.size()-1; + } + final GLEventListener listener = listeners.remove(index); + listenersToBeInit.remove(listener); + return listener; + } + } + + public final int getGLEventListenerCount() { + synchronized(listenersLock) { + return listeners.size(); + } + } + + public final GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException { + synchronized(listenersLock) { + if(0>index) { + index = listeners.size()-1; + } + return listeners.get(index); + } + } + + public final boolean areAllGLEventListenerInitialized() { + synchronized(listenersLock) { + return 0 == listenersToBeInit.size(); + } + } + + public final boolean getGLEventListenerInitState(GLEventListener listener) { + synchronized(listenersLock) { + return !listenersToBeInit.contains(listener); + } + } + + public final void setGLEventListenerInitState(GLEventListener listener, boolean initialized) { + synchronized(listenersLock) { + if(initialized) { + listenersToBeInit.remove(listener); + } else { + listenersToBeInit.add(listener); + } } } /** - * Issues {@link javax.media.opengl.GLEventListener#dispose(javax.media.opengl.GLAutoDrawable)} - * to all listeners. + * Disposes the given {@link GLEventListener} via {@link GLEventListener#dispose(GLAutoDrawable)} + * if it has been initialized and added to this queue. * <p> - * Please consider using {@link #disposeGL(GLAutoDrawable, GLDrawable, GLContext, Runnable)} - * for correctness! + * If <code>remove</code> is <code>true</code>, the {@link GLEventListener} is removed from this drawable queue before disposal, + * otherwise marked uninitialized. * </p> - * @param drawable + * <p> + * Please consider using {@link #disposeGLEventListener(GLAutoDrawable, GLDrawable, GLContext, GLEventListener)} + * for correctness, i.e. encapsulating all calls w/ makeCurrent etc. + * </p> + * @param autoDrawable + * @param remove if true, the listener gets removed + * @return the disposed and/or removed listener, otherwise null if neither action is performed + */ + public final GLEventListener disposeGLEventListener(GLAutoDrawable autoDrawable, GLEventListener listener, boolean remove) { + synchronized(listenersLock) { + if( remove ) { + if( listeners.remove(listener) ) { + if( !listenersToBeInit.remove(listener) ) { + listener.dispose(autoDrawable); + } + return listener; + } + } else { + if( listeners.contains(listener) && !listenersToBeInit.contains(listener) ) { + listener.dispose(autoDrawable); + listenersToBeInit.add(listener); + return listener; + } + } + } + return null; + } + + /** + * Disposes all added initialized {@link GLEventListener}s via {@link GLEventListener#dispose(GLAutoDrawable)}. + * <p> + * If <code>remove</code> is <code>true</code>, the {@link GLEventListener}s are removed from this drawable queue before disposal, + * otherwise maked uninitialized. + * </p> + * <p> + * Please consider using {@link #disposeAllGLEventListener(GLAutoDrawable, GLContext, boolean)} + * or {@link #disposeGL(GLAutoDrawable, GLContext, boolean)} + * for correctness, i.e. encapsulating all calls w/ makeCurrent etc. + * </p> + * @param autoDrawable + * @return the disposal count */ - public final void dispose(GLAutoDrawable drawable) { + public final int disposeAllGLEventListener(GLAutoDrawable autoDrawable, boolean remove) { + int disposeCount = 0; synchronized(listenersLock) { - for (int i=0; i < listeners.size(); i++) { - listeners.get(i).dispose(drawable); + if( remove ) { + for (int count = listeners.size(); 0 < count && 0 < listeners.size(); count--) { + final GLEventListener listener = listeners.remove(0); + if( !listenersToBeInit.remove(listener) ) { + listener.dispose(autoDrawable); + disposeCount++; + } + } + } else { + for (int i = 0; i < listeners.size(); i++) { + final GLEventListener listener = listeners.get(i); + if( !listenersToBeInit.contains(listener) ) { + listener.dispose(autoDrawable); + listenersToBeInit.add(listener); + disposeCount++; + } + } } } + return disposeCount; } - - private boolean init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) { - if(listenersToBeInit.remove(l)) { - l.init(drawable); - if(sendReshape) { - reshape(l, drawable, 0, 0, drawable.getWidth(), drawable.getHeight(), true /* setViewport */, false); + + /** + * Principal helper method which runs {@link #disposeGLEventListener(GLAutoDrawable, GLEventListener, boolean)} + * with the context made current. + * <p> + * If an {@link GLAnimatorControl} is being attached and the current thread is different + * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * + * @param autoDrawable + * @param context + * @param listener + * @param initAction + */ + public final GLEventListener disposeGLEventListener(final GLAutoDrawable autoDrawable, + final GLDrawable drawable, + final GLContext context, + final GLEventListener listener, + final boolean remove) { + synchronized(listenersLock) { + // fast path for uninitialized listener + if( listenersToBeInit.contains(listener) ) { + if( remove ) { + listenersToBeInit.remove(listener); + return listeners.remove(listener) ? listener : null; + } + return null; } - return true; } - return false; + final boolean isPaused = isAnimatorAnimatingOnOtherThread() && animatorCtrl.pause(); + final GLEventListener[] res = new GLEventListener[] { null }; + final Runnable action = new Runnable() { + @Override + public void run() { + res[0] = disposeGLEventListener(autoDrawable, listener, remove); + } + }; + invokeGL(drawable, context, action, nop); + + if(isPaused) { + animatorCtrl.resume(); + } + return res[0]; } - public final void init(GLAutoDrawable drawable) { - synchronized(listenersLock) { - for (int i=0; i < listeners.size(); i++) { - final GLEventListener listener = listeners.get(i) ; + /** + * Principal helper method which runs {@link #disposeAllGLEventListener(GLAutoDrawable, boolean)} + * with the context made current. + * <p> + * If an {@link GLAnimatorControl} is being attached and the current thread is different + * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * + * @param autoDrawable + * @param context + * @param remove + */ + public final void disposeAllGLEventListener(final GLAutoDrawable autoDrawable, + final GLDrawable drawable, + final GLContext context, + final boolean remove) { - // If make current ctx, 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 the must always be initialized unconditional. - listenersToBeInit.add(listener); + final boolean isPaused = isAnimatorAnimatingOnOtherThread() && animatorCtrl.pause(); - if ( ! init( listener, drawable, false ) ) { - throw new GLException("GLEventListener "+listener+" already initialized: "+drawable); + final Runnable action = new Runnable() { + @Override + public void run() { + disposeAllGLEventListener(autoDrawable, remove); } + }; + invokeGL(drawable, context, action, nop); + + if(isPaused) { + animatorCtrl.resume(); + } + } + + 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(), setViewport, false /* checkInit */); + } + } + + /** + * The default init action to be called once after ctx is being created @ 1st makeCurrent(). + * @param sendReshape set to true if the subsequent display call won't reshape, otherwise false to avoid double reshape. + **/ + public final void init(GLAutoDrawable drawable, boolean sendReshape) { + synchronized(listenersLock) { + final ArrayList<GLEventListener> _listeners = listeners; + 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)) { - displayImpl(drawable); + if( glRunnables.size()>0 && !execGLRunnables(drawable) ) { // glRunnables volatile OK; execGL.. only executed if size > 0 + displayImpl(drawable); } } - private void displayImpl(GLAutoDrawable drawable) { + private final void displayImpl(GLAutoDrawable drawable) { synchronized(listenersLock) { - for (int i=0; i < listeners.size(); i++) { - final GLEventListener listener = listeners.get(i) ; - // GLEventListener may need to be init, + final ArrayList<GLEventListener> _listeners = listeners; + 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 - init( listener, drawable, true ) ; + if( listenersToBeInit.remove(listener) ) { + init( listener, drawable, true /* sendReshape */, listenersToBeInit.size() + 1 == listenerCount /* setViewport if 1st init */ ); + } listener.display(drawable); } } } - private void reshape(GLEventListener listener, GLAutoDrawable drawable, - int x, int y, int width, int height, boolean setViewport, boolean checkInit) { + private final void reshape(GLEventListener listener, GLAutoDrawable drawable, + int x, int y, int width, int height, boolean setViewport, boolean checkInit) { if(checkInit) { - // GLEventListener may need to be init, - // in case this one is added after the realization of the GLAutoDrawable - init( listener, drawable, false ) ; + // GLEventListener may need to be init, + // in case this one is added after the realization of the GLAutoDrawable + synchronized(listenersLock) { + if( listenersToBeInit.remove(listener) ) { + listener.init(drawable); + } + } } if(setViewport) { + final GL gl = drawable.getGL(); + final int glerr0 = gl.glGetError(); + if( GL.GL_NO_ERROR != glerr0 ) { + System.err.println("Info: GLDrawableHelper.reshape: pre-exisiting GL error 0x"+Integer.toHexString(glerr0)); + if(DEBUG) { + Thread.dumpStack(); + } + } drawable.getGL().glViewport(x, y, width, height); } listener.reshape(drawable, x, y, width, height); @@ -207,30 +695,51 @@ 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(listeners.get(i), drawable, x, y, width, height, 0==i /* setViewport */, true /* checkInit */); } } } - private boolean execGLRunnables(GLAutoDrawable drawable) { + private final boolean execGLRunnables(GLAutoDrawable drawable) { // glRunnables.size()>0 boolean res = true; - if(glRunnables.size()>0) { + // 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; + } + } + return res; + } + + public final void flushGLRunnables() { + if(glRunnables.size()>0) { // volatile OK // swap one-shot list asap - ArrayList<GLRunnable> _glRunnables = null; + final ArrayList<GLRunnableTask> _glRunnables; synchronized(glRunnablesLock) { if(glRunnables.size()>0) { _glRunnables = glRunnables; - glRunnables = new ArrayList<GLRunnable>(); + 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; + _glRunnables.get(i).flush(); } } } - return res; } public final void setAnimator(GLAnimatorControl animator) throws GLException { @@ -248,15 +757,15 @@ public class GLDrawableHelper { } } - public final boolean isExternalAnimatorRunning() { + public final boolean isAnimatorStartedOnOtherThread() { return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; } - public final boolean isAnimatorRunning() { + public final boolean isAnimatorStarted() { return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; } - public final boolean isExternalAnimatorAnimating() { + public final boolean isAnimatorAnimatingOnOtherThread() { return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; } @@ -264,17 +773,35 @@ public class GLDrawableHelper { return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; } - public final void invoke(GLAutoDrawable drawable, boolean wait, GLRunnable glRunnable) { - if( null == drawable || null == glRunnable ) { - return; + /** + * <p> + * If <code>wait</code> is <code>true</code> the call blocks until the <code>glRunnable</code> + * has been executed.<p> + * <p> + * If <code>wait</code> is <code>true</code> <b>and</b> + * {@link GLDrawable#isRealized()} returns <code>false</code> <i>or</i> {@link GLAutoDrawable#getContext()} returns <code>null</code>, + * the call is ignored and returns <code>false</code>.<br> + * This helps avoiding deadlocking the caller. + * </p> + * + * @param drawable the {@link GLAutoDrawable} to be used + * @param wait if <code>true</code> block until execution of <code>glRunnable</code> is finished, otherwise return immediatly w/o waiting + * @param glRunnable the {@link GLRunnable} to execute within {@link #display()} + * @return <code>true</code> if the {@link GLRunnable} has been processed or queued, otherwise <code>false</code>. + */ + public final boolean invoke(GLAutoDrawable drawable, boolean wait, GLRunnable glRunnable) { + if( null == glRunnable || null == drawable || + wait && ( !drawable.isRealized() || null==drawable.getContext() ) ) { + return false; } - Throwable throwable = null; + GLRunnableTask rTask = null; Object rTaskLock = new Object(); + Throwable throwable = null; synchronized(rTaskLock) { - boolean deferred; + final boolean deferred; synchronized(glRunnablesLock) { - deferred = isExternalAnimatorAnimating(); + deferred = isAnimatorAnimatingOnOtherThread(); if(!deferred) { wait = false; // don't wait if exec immediatly } @@ -299,6 +826,60 @@ public class GLDrawableHelper { } } } + return true; + } + + public final boolean invoke(GLAutoDrawable drawable, boolean wait, List<GLRunnable> newGLRunnables) { + if( null == newGLRunnables || newGLRunnables.size() == 0 || null == drawable || + wait && ( !drawable.isRealized() || null==drawable.getContext() ) ) { + return false; + } + + final int count = newGLRunnables.size(); + GLRunnableTask rTask = null; + Object rTaskLock = new Object(); + Throwable throwable = null; + synchronized(rTaskLock) { + final boolean deferred; + synchronized(glRunnablesLock) { + deferred = isAnimatorAnimatingOnOtherThread() || !drawable.isRealized(); + if(!deferred) { + wait = false; // don't wait if exec immediately + } + for(int i=0; i<count-1; i++) { + glRunnables.add( new GLRunnableTask(newGLRunnables.get(i), null, false) ); + } + rTask = new GLRunnableTask(newGLRunnables.get(count-1), + wait ? rTaskLock : null, + wait /* catch Exceptions if waiting for result */); + glRunnables.add(rTask); + } + if( !deferred ) { + drawable.display(); + } else if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } + } + return true; + } + + public final void enqueue(GLRunnable glRunnable) { + if( null == glRunnable) { + return; + } + synchronized(glRunnablesLock) { + glRunnables.add( new GLRunnableTask(glRunnable, null, false) ); + } } public final void setAutoSwapBufferMode(boolean enable) { @@ -309,25 +890,74 @@ public class GLDrawableHelper { return autoSwapBufferMode; } + private final String getExclusiveContextSwitchString() { + return 0 == exclusiveContextSwitch ? "nop" : ( 0 > exclusiveContextSwitch ? "released" : "claimed" ) ; + } + /** - * @param t the thread for which context release shall be skipped, usually the animation thread, - * ie. {@link Animator#getThread()}. - * @deprecated this is an experimental feature, - * intended for measuring performance in regards to GL context switch - * and only being used if {@link #PERF_STATS} is enabled - * by defining property <code>jogl.debug.GLDrawable.PerfStats</code>. + * Dedicates this instance's {@link GLContext} to the given thread.<br/> + * The thread will exclusively claim the {@link GLContext} via {@link #display()} and not release it + * until {@link #destroy()} or <code>setExclusiveContextThread(null)</code> has been called. + * <p> + * Default non-exclusive behavior is <i>requested</i> via <code>setExclusiveContextThread(null)</code>, + * which will cause the next call of {@link #display()} on the exclusive thread to + * release the {@link GLContext}. Only after it's async release, {@link #getExclusiveContextThread()} + * will return <code>null</code>. + * </p> + * <p> + * To release a previous made exclusive thread, a user issues <code>setExclusiveContextThread(null)</code> + * and may poll {@link #getExclusiveContextThread()} until it returns <code>null</code>, + * <i>while</i> the exclusive thread is still running. + * </p> + * <p> + * Note: Setting a new exclusive thread without properly releasing a previous one + * will throw an GLException. + * </p> + * <p> + * One scenario could be to dedicate the context to the {@link com.jogamp.opengl.util.AnimatorBase#getThread() animator thread} + * and spare redundant context switches. + * </p> + * @param t the exclusive thread to claim the context, or <code>null</code> for default operation. + * @return previous exclusive context thread + * @throws GLException If an exclusive thread is still active but a new one is attempted to be set */ - public final void setSkipContextReleaseThread(Thread t) { - skipContextReleaseThread = t; + public final Thread setExclusiveContextThread(Thread t, GLContext context) throws GLException { + if (DEBUG) { + System.err.println("GLDrawableHelper.setExclusiveContextThread(): START switch "+getExclusiveContextSwitchString()+", thread "+exclusiveContextThread+" -> "+t+" -- currentThread "+Thread.currentThread()); + } + final Thread oldExclusiveContextThread = exclusiveContextThread; + if( exclusiveContextThread == t ) { + exclusiveContextSwitch = 0; // keep + } else if( null == t ) { + exclusiveContextSwitch = -1; // release + } else { + exclusiveContextSwitch = 1; // claim + if( null != exclusiveContextThread ) { + throw new GLException("Release current exclusive Context Thread "+exclusiveContextThread+" first"); + } + if( null != context && context.isCurrent() ) { + try { + forceNativeRelease(context); + } catch (Throwable ex) { + ex.printStackTrace(); + throw new GLException(ex); + } + } + exclusiveContextThread = t; + } + if (DEBUG) { + System.err.println("GLDrawableHelper.setExclusiveContextThread(): END switch "+getExclusiveContextSwitchString()+", thread "+exclusiveContextThread+" -- currentThread "+Thread.currentThread()); + } + return oldExclusiveContextThread; } /** - * @deprecated see {@link #setSkipContextReleaseThread(Thread)} + * @see #setExclusiveContextThread(Thread, GLContext) */ - public final Thread getSkipContextReleaseThread() { - return skipContextReleaseThread; + public final Thread getExclusiveContextThread() { + return exclusiveContextThread; } - + private static final ThreadLocal<Runnable> perThreadInitAction = new ThreadLocal<Runnable>(); /** Principal helper method which runs a Runnable with the context @@ -346,104 +976,76 @@ public class GLDrawableHelper { * @param runnable * @param initAction */ - public final void invokeGL(GLDrawable drawable, - GLContext context, - Runnable runnable, - Runnable initAction) { + public final void invokeGL(final GLDrawable drawable, + final GLContext context, + final Runnable runnable, + final Runnable initAction) { if(null==context) { if (DEBUG) { - Exception e = new GLException(Thread.currentThread().getName()+" Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext"); + Exception e = new GLException(getThreadName()+" Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext"); e.printStackTrace(); } return; } if(PERF_STATS) { - invokeGLImplStats(drawable, context, runnable, initAction, null); + invokeGLImplStats(drawable, context, runnable, initAction); } else { - invokeGLImpl(drawable, context, runnable, initAction, null); + invokeGLImpl(drawable, context, runnable, initAction); } } - /** - * Principal helper method which runs {@link #dispose(GLAutoDrawable)} with the context - * made current and destroys the context afterwards while holding the lock. - * + /** + * Principal helper method which runs + * {@link #disposeAllGLEventListener(GLAutoDrawable, boolean) disposeAllGLEventListener(autoDrawable, false)} + * with the context made current. + * <p> + * If <code>destroyContext</code> is <code>true</code> the context is destroyed in the end while holding the lock. + * </p> + * <p> + * If <code>destroyContext</code> is <code>false</code> the context is natively released, i.e. released as often as locked before. + * </p> * @param autoDrawable - * @param drawable * @param context - * @param postAction + * @param destroyContext destroy context in the end while holding the lock */ - public final void disposeGL(GLAutoDrawable autoDrawable, - GLDrawable drawable, - GLContext context, - Runnable postAction) { - if(PERF_STATS) { - invokeGLImplStats(drawable, context, null, null, autoDrawable); - } else { - invokeGLImpl(drawable, context, null, null, autoDrawable); - } - if(null != postAction) { - postAction.run(); - } - } - - private final void invokeGLImpl(GLDrawable drawable, - GLContext context, - Runnable runnable, - Runnable initAction, - GLAutoDrawable disposeAutoDrawable) { - final Thread currentThread = Thread.currentThread(); - - final boolean isDisposeAction = null==initAction ; - + public final void disposeGL(final GLAutoDrawable autoDrawable, + final GLContext context, boolean destroyContext) { // Support for recursive makeCurrent() calls as well as calling // other drawables' display() methods from within another one's GLContext lastContext = GLContext.getCurrent(); Runnable lastInitAction = null; if (lastContext != null) { if (lastContext == context) { - lastContext = null; // utilize recursive locking + lastContext = null; } else { + // utilize recursive locking lastInitAction = perThreadInitAction.get(); lastContext.release(); } } - int res = GLContext.CONTEXT_NOT_CURRENT; - + + int res; try { res = context.makeCurrent(); - if (res != GLContext.CONTEXT_NOT_CURRENT) { - if(!isDisposeAction) { - perThreadInitAction.set(initAction); - if (res == GLContext.CONTEXT_CURRENT_NEW) { - if (DEBUG) { - System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); - } - initAction.run(); - } - runnable.run(); - if (autoSwapBufferMode) { - drawable.swapBuffers(); - } - } else { - if(res == GLContext.CONTEXT_CURRENT_NEW) { - throw new GLException(currentThread.getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); - } - if(listeners.size()>0) { - dispose(disposeAutoDrawable); - } + if (GLContext.CONTEXT_NOT_CURRENT != res) { + if(GLContext.CONTEXT_CURRENT_NEW == res) { + throw new GLException(getThreadName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); + } + if( listeners.size() > 0 && null != autoDrawable ) { + disposeAllGLEventListener(autoDrawable, false); } } } finally { try { - if(isDisposeAction) { + if(destroyContext) { context.destroy(); - } else if( res != GLContext.CONTEXT_NOT_CURRENT ) { - context.release(); + } else { + forceNativeRelease(context); } + flushGLRunnables(); } catch (Exception e) { - System.err.println("Catched: "+e.getMessage()); + System.err.println("Catched Exception on thread "+getThreadName()); e.printStackTrace(); } if (lastContext != null) { @@ -454,99 +1056,206 @@ public class GLDrawableHelper { } } } - - private final void invokeGLImplStats(GLDrawable drawable, - GLContext context, - Runnable runnable, - Runnable initAction, - GLAutoDrawable disposeAutoDrawable) { - final Thread currentThread = Thread.currentThread(); - - final boolean isDisposeAction = null==initAction ; - - // Support for recursive makeCurrent() calls as well as calling - // other drawables' display() methods from within another one's - int res = GLContext.CONTEXT_NOT_CURRENT; - GLContext lastContext = GLContext.getCurrent(); - Runnable lastInitAction = null; - if (lastContext != null) { - if (lastContext == context) { - if( currentThread == skipContextReleaseThread ) { - res = GLContext.CONTEXT_CURRENT; - } // else: utilize recursive locking - lastContext = null; - } else { - lastInitAction = perThreadInitAction.get(); - lastContext.release(); - } - } - - long t0 = System.currentTimeMillis(); - long tdA = 0; // makeCurrent - long tdR = 0; // render time - long tdS = 0; // swapBuffers - long tdX = 0; // release - boolean ctxClaimed = false; - boolean ctxReleased = false; - boolean ctxDestroyed = false; - try { - if (res == GLContext.CONTEXT_NOT_CURRENT) { - res = context.makeCurrent(); - ctxClaimed = true; - } - if (res != GLContext.CONTEXT_NOT_CURRENT) { - if(!isDisposeAction) { - perThreadInitAction.set(initAction); - if (res == GLContext.CONTEXT_CURRENT_NEW) { - if (DEBUG) { - System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); - } - initAction.run(); - } - tdR = System.currentTimeMillis(); - tdA = tdR - t0; // makeCurrent - runnable.run(); - tdS = System.currentTimeMillis(); - tdR = tdS - tdR; // render time - if (autoSwapBufferMode) { - drawable.swapBuffers(); - tdX = System.currentTimeMillis(); - tdS = tdX - tdS; // swapBuffers - } - } else { - if(res == GLContext.CONTEXT_CURRENT_NEW) { - throw new GLException(currentThread.getName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); - } - if(listeners.size()>0) { - dispose(disposeAutoDrawable); - } - } + + private final void invokeGLImpl(final GLDrawable drawable, + final GLContext context, + final Runnable runnable, + final Runnable initAction) { + final Thread currentThread = Thread.currentThread(); + + // Exclusive Cases: + // 1: lock - unlock : default + // 2: lock - - : exclusive, not locked yet + // 3: - - - : exclusive, already locked + // 4: - - unlock : ex-exclusive, already locked + final boolean _isExclusiveThread, _releaseExclusiveThread; + if( null != exclusiveContextThread) { + if( currentThread == exclusiveContextThread ) { + _releaseExclusiveThread = 0 > exclusiveContextSwitch; + _isExclusiveThread = !_releaseExclusiveThread; + exclusiveContextSwitch = 0; + } else { + // Exclusive thread usage, but on other thread + return; + } + } else { + _releaseExclusiveThread = false; + _isExclusiveThread = false; } - } finally { + + // Support for recursive makeCurrent() calls as well as calling + // other drawables' display() methods from within another one's + int res = GLContext.CONTEXT_NOT_CURRENT; + GLContext lastContext = GLContext.getCurrent(); + Runnable lastInitAction = null; + if (lastContext != null) { + if (lastContext == context) { + res = GLContext.CONTEXT_CURRENT; + lastContext = null; + } else { + // utilize recursive locking + lastInitAction = perThreadInitAction.get(); + lastContext.release(); + } + } + try { - if(isDisposeAction) { - context.destroy(); - ctxDestroyed = true; - } else if( res != GLContext.CONTEXT_NOT_CURRENT && - (null == skipContextReleaseThread || currentThread != skipContextReleaseThread) ) { - context.release(); - ctxReleased = true; + final boolean releaseContext; + if( GLContext.CONTEXT_NOT_CURRENT == res ) { + res = context.makeCurrent(); + releaseContext = !_isExclusiveThread; + } else { + releaseContext = _releaseExclusiveThread; + } + if (GLContext.CONTEXT_NOT_CURRENT != res) { + try { + perThreadInitAction.set(initAction); + if (GLContext.CONTEXT_CURRENT_NEW == res) { + if (DEBUG) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + } + initAction.run(); + } + runnable.run(); + if ( autoSwapBufferMode ) { + drawable.swapBuffers(); + } + } finally { + if( _releaseExclusiveThread ) { + exclusiveContextThread = null; + if (DEBUG) { + System.err.println("GLDrawableHelper.invokeGL() - Release ExclusiveContextThread -- currentThread "+Thread.currentThread()); + } + } + if( releaseContext ) { + try { + context.release(); + } catch (Exception e) { + System.err.println("Catched Exception on thread "+getThreadName()); + e.printStackTrace(); + } + } + } + } + } finally { + if (lastContext != null) { + final int res2 = lastContext.makeCurrent(); + if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { + lastInitAction.run(); + } } - } catch (Exception e) { - System.err.println("Catched: "+e.getMessage()); - e.printStackTrace(); + } + } + + private final void invokeGLImplStats(final GLDrawable drawable, + final GLContext context, + final Runnable runnable, + final Runnable initAction) { + final Thread currentThread = Thread.currentThread(); + + // Exclusive Cases: + // 1: lock - unlock : default + // 2: lock - - : exclusive, not locked yet + // 3: - - - : exclusive, already locked + // 4: - - unlock : ex-exclusive, already locked + final boolean _isExclusiveThread, _releaseExclusiveThread; + if( null != exclusiveContextThread) { + if( currentThread == exclusiveContextThread ) { + _releaseExclusiveThread = 0 > exclusiveContextSwitch; + _isExclusiveThread = !_releaseExclusiveThread; + } else { + // Exclusive thread usage, but on other thread + return; + } + } else { + _releaseExclusiveThread = false; + _isExclusiveThread = false; } - tdX = System.currentTimeMillis() - tdX; // release / destroy + // Support for recursive makeCurrent() calls as well as calling + // other drawables' display() methods from within another one's + int res = GLContext.CONTEXT_NOT_CURRENT; + GLContext lastContext = GLContext.getCurrent(); + Runnable lastInitAction = null; if (lastContext != null) { - final int res2 = lastContext.makeCurrent(); - if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { - lastInitAction.run(); - } + if (lastContext == context) { + res = GLContext.CONTEXT_CURRENT; + lastContext = null; + } else { + // utilize recursive locking + lastInitAction = perThreadInitAction.get(); + lastContext.release(); + } } - } - long td = System.currentTimeMillis() - t0; - System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed); + + long t0 = System.currentTimeMillis(); + long tdA = 0; // makeCurrent + long tdR = 0; // render time + long tdS = 0; // swapBuffers + long tdX = 0; // release + boolean ctxClaimed = false; + boolean ctxReleased = false; + boolean ctxDestroyed = false; + try { + final boolean releaseContext; + if( GLContext.CONTEXT_NOT_CURRENT == res ) { + res = context.makeCurrent(); + releaseContext = !_isExclusiveThread; + ctxClaimed = true; + } else { + releaseContext = _releaseExclusiveThread; + } + if (GLContext.CONTEXT_NOT_CURRENT != res) { + try { + perThreadInitAction.set(initAction); + if (GLContext.CONTEXT_CURRENT_NEW == res) { + if (DEBUG) { + System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); + } + initAction.run(); + } + tdR = System.currentTimeMillis(); + tdA = tdR - t0; // makeCurrent + runnable.run(); + tdS = System.currentTimeMillis(); + tdR = tdS - tdR; // render time + if ( autoSwapBufferMode ) { + drawable.swapBuffers(); + tdX = System.currentTimeMillis(); + tdS = tdX - tdS; // swapBuffers + } + } finally { + if( _releaseExclusiveThread ) { + exclusiveContextSwitch = 0; + exclusiveContextThread = null; + if (DEBUG) { + System.err.println("GLDrawableHelper.invokeGL() - Release ExclusiveContextThread -- currentThread "+Thread.currentThread()); + } + } + if( releaseContext ) { + try { + context.release(); + ctxReleased = true; + } catch (Exception e) { + System.err.println("Catched Exception on thread "+getThreadName()); + e.printStackTrace(); + } + } + } + } + } finally { + tdX = System.currentTimeMillis() - tdX; // release / destroy + if (lastContext != null) { + final int res2 = lastContext.makeCurrent(); + if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { + lastInitAction.run(); + } + } + } + long td = System.currentTimeMillis() - t0; + System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed); } - + + protected static String getThreadName() { return Thread.currentThread().getName(); } + } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java index 36f17e5a1..d11274560 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,6 +42,8 @@ package jogamp.opengl; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.opengl.GL; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; @@ -50,173 +52,301 @@ import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; public abstract class GLDrawableImpl implements GLDrawable { - protected static final boolean DEBUG = Debug.debug("GLDrawable"); + protected static final boolean DEBUG = GLDrawableFactoryImpl.DEBUG; - protected GLDrawableImpl(GLDrawableFactory factory, - NativeSurface comp, - boolean realized) { + protected GLDrawableImpl(GLDrawableFactory factory, NativeSurface comp, boolean realized) { + this(factory, comp, (GLCapabilitiesImmutable) comp.getGraphicsConfiguration().getRequestedCapabilities(), realized); + } + + protected GLDrawableImpl(GLDrawableFactory factory, NativeSurface comp, GLCapabilitiesImmutable requestedCapabilities, boolean realized) { this.factory = factory; this.surface = comp; this.realized = realized; - this.requestedCapabilities = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getRequestedCapabilities(); + this.requestedCapabilities = requestedCapabilities; } - /** + /** * Returns the DynamicLookupHelper */ public abstract GLDynamicLookupHelper getGLDynamicLookupHelper(); - public GLDrawableFactoryImpl getFactoryImpl() { + public final GLDrawableFactoryImpl getFactoryImpl() { return (GLDrawableFactoryImpl) getFactory(); } - /** For offscreen GLDrawables (pbuffers and "pixmap" drawables), - indicates that native resources should be reclaimed. */ - public void destroy() { - surface.getGraphicsConfiguration().getScreen().getDevice().lock(); - try { - destroyImpl(); - } finally { - surface.getGraphicsConfiguration().getScreen().getDevice().unlock(); - } - } - protected void destroyImpl() { - throw new GLException("Should not call this (should only be called for offscreen GLDrawables)"); - } - + @Override public final void swapBuffers() throws GLException { - GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable)surface.getGraphicsConfiguration().getChosenCapabilities(); - if ( caps.getDoubleBuffered() ) { - if(!surface.surfaceSwap()) { - int lockRes = lockSurface(); // it's recursive, so it's ok within [makeCurrent .. release] - if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { - return; - } - try { - if (NativeSurface.LOCK_SURFACE_CHANGED == lockRes) { - updateHandle(); + if( !realized ) { // volatile OK (locked below) + return; // destroyed already + } + final int lockRes = lockSurface(); // it's recursive, so it's ok within [makeCurrent .. release] + if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) { + return; + } + try { + if( realized ) { // volatile OK + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable)surface.getGraphicsConfiguration().getChosenCapabilities(); + if ( caps.getDoubleBuffered() ) { + if(!surface.surfaceSwap()) { + swapBuffersImpl(true); + } + } else { + final GLContext ctx = GLContext.getCurrent(); + if(null!=ctx && ctx.getGLDrawable()==this) { + ctx.getGL().glFlush(); } - swapBuffersImpl(); - } finally { - unlockSurface(); + swapBuffersImpl(false); } } - } else { - GLContext ctx = GLContext.getCurrent(); - if(null!=ctx && ctx.getGLDrawable()==this) { - ctx.getGL().glFinish(); - } + } finally { + unlockSurface(); } surface.surfaceUpdated(this, surface, System.currentTimeMillis()); } - protected abstract void swapBuffersImpl(); - public static String toHexString(long hex) { + /** + * Platform and implementation depending surface swap. + * <p>The surface is locked.</p> + * <p> + * If <code>doubleBuffered</code> is <code>true</code>, + * an actual platform dependent surface swap shall be executed. + * </p> + * <p> + * If <code>doubleBuffered</code> is <code>false</code>, + * {@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); + + public final static String toHexString(long hex) { return "0x" + Long.toHexString(hex); } - public GLProfile getGLProfile() { + @Override + public final GLProfile getGLProfile() { return requestedCapabilities.getGLProfile(); } + @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { return (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); } - public GLCapabilitiesImmutable getRequestedGLCapabilities() { + public final GLCapabilitiesImmutable getRequestedGLCapabilities() { return requestedCapabilities; } + @Override public NativeSurface getNativeSurface() { return surface; } - /** called with locked surface @ setRealized(false) */ + /** + * called with locked surface @ setRealized(false) or @ lockSurface(..) when surface changed + * <p> + * Must be paired w/ {@link #createHandle()}. + * </p> + */ protected void destroyHandle() {} - - /** called with locked surface @ setRealized(true) or @ lockSurface(..) when surface changed */ - protected void updateHandle() {} + /** + * called with locked surface @ setRealized(true) or @ lockSurface(..) when surface changed + * <p> + * Must be paired w/ {@link #destroyHandle()}. + * </p> + */ + protected void createHandle() {} + + @Override public long getHandle() { return surface.getSurfaceHandle(); } - public GLDrawableFactory getFactory() { + @Override + public final GLDrawableFactory getFactory() { return factory; } - public final synchronized void setRealized(boolean realizedArg) { - if ( realized != realizedArg ) { + @Override + public final void setRealized(boolean realizedArg) { + if ( realized != realizedArg ) { // volatile: OK (locked below) + final boolean isProxySurface = surface instanceof ProxySurface; if(DEBUG) { - System.err.println(getThreadName() + ": setRealized: "+getClass().getSimpleName()+" "+realized+" -> "+realizedArg); + System.err.println(getThreadName() + ": setRealized: drawable "+getClass().getSimpleName()+", surface "+surface.getClass().getSimpleName()+", isProxySurface "+isProxySurface+": "+realized+" -> "+realizedArg); + Thread.dumpStack(); } - realized = realizedArg; AbstractGraphicsDevice aDevice = surface.getGraphicsConfiguration().getScreen().getDevice(); if(realizedArg) { - if(NativeSurface.LOCK_SURFACE_NOT_READY >= lockSurface()) { - throw new GLException("GLDrawableImpl.setRealized(true): already realized, but surface not ready (lockSurface)"); + if(isProxySurface) { + ((ProxySurface)surface).createNotify(); + } + if(NativeSurface.LOCK_SURFACE_NOT_READY >= surface.lockSurface()) { + throw new GLException("GLDrawableImpl.setRealized(true): Surface not ready (lockSurface)"); } } else { aDevice.lock(); } - try { - setRealizedImpl(); - if(realizedArg) { - updateHandle(); - } else { - destroyHandle(); + try { + if ( realized != realizedArg ) { // volatile: OK + realized = realizedArg; + if(realizedArg) { + setRealizedImpl(); + createHandle(); + } else { + destroyHandle(); + setRealizedImpl(); + } } } finally { if(realizedArg) { - unlockSurface(); + surface.unlockSurface(); } else { aDevice.unlock(); + if(isProxySurface) { + ((ProxySurface)surface).destroyNotify(); + } } } } else if(DEBUG) { System.err.println(getThreadName() + ": setRealized: "+getClass().getName()+" "+this.realized+" == "+realizedArg); } } + + /** + * Platform specific realization of drawable + */ protected abstract void setRealizedImpl(); - - public synchronized boolean isRealized() { + + /** + * Callback for special implementations, allowing + * <ul> + * <li>to associate bound context to this drawable (bound == true) + * or to remove such association (bound == false).</li> + * <li>to trigger GLContext/GLDrawable related lifecycle: <code>construct</code>, <code>destroy</code>.</li> + * </ul> + * <p> + * If <code>bound</code> is <code>true</code>, the context is current and being newly associated w/ this drawable. + * </p> + * <p> + * If <code>bound</code> is <code>false</code>, the context is still current and will be unbound (released and destroyed, or simply disassociated). + * </p> + * <p> + * Being called by {@link GLContextImpl#associateDrawable(boolean)}. + * </p> + * @param ctx the just bounded or unbounded context + * @param bound if <code>true</code> create an association, otherwise remove it + */ + protected void associateContext(GLContext ctx, boolean bound) { } + + /** + * Callback for special implementations, allowing GLContext to trigger GL related lifecycle: <code>makeCurrent</code>, <code>release</code>. + * <p> + * If <code>current</code> is <code>true</code>, the context has just been made current. + * </p> + * <p> + * If <code>current</code> is <code>false</code>, the context is still current and will be release after this method returns. + * </p> + * <p> + * Being called by {@link GLContextImpl#contextMadeCurrent(boolean)}. + * </p> + * @see #associateContext(GLContext, boolean) + */ + protected void contextMadeCurrent(GLContext glc, boolean current) { } + + /** Callback for special implementations, allowing GLContext to fetch a custom default render framebuffer. Defaults to zero.*/ + protected int getDefaultDrawFramebuffer() { return 0; } + /** Callback for special implementations, allowing GLContext to fetch a custom default read framebuffer. Defaults to zero. */ + protected int getDefaultReadFramebuffer() { return 0; } + /** Callback for special implementations, allowing GLContext to fetch a custom default read buffer of current framebuffer. */ + protected int getDefaultReadBuffer(GL gl, boolean hasDedicatedDrawableRead) { + if( gl.isGLES() || hasDedicatedDrawableRead || getChosenGLCapabilities().getDoubleBuffered() ) { + // Note-1: Neither ES1 nor ES2 supports selecting the read buffer via glReadBuffer + // Note-2: ES3 only supports GL_BACK, GL_NONE or GL_COLOR_ATTACHMENT0+i + return GL.GL_BACK; + } + return GL.GL_FRONT ; + } + + @Override + public final boolean isRealized() { return realized; } + @Override public int getWidth() { return surface.getWidth(); } - + + @Override public int getHeight() { return surface.getHeight(); } - public int lockSurface() throws GLException { - return surface.lockSurface(); + @Override + public boolean isGLOriented() { + return true; } - public void unlockSurface() { - surface.unlockSurface(); + /** + * {@link NativeSurface#lockSurface() Locks} the underlying windowing toolkit's {@link NativeSurface surface}. + * <p> + * <i>If</i> drawable is {@link #setRealized(boolean) realized}, + * the {@link #getHandle() drawable handle} is valid after successfully {@link NativeSurface#lockSurface() locking} + * it's {@link NativeSurface surface} until being {@link #unlockSurface() unlocked}. + * </p> + * <p> + * In case the {@link NativeSurface surface} has changed as indicated by it's + * {@link NativeSurface#lockSurface() lock} result {@link NativeSurface#LOCK_SURFACE_CHANGED}, + * the implementation is required to update this information as needed within it's implementation. + * </p> + * + * @see NativeSurface#lockSurface() + * @see #getHandle() + */ + public final int lockSurface() throws GLException { + final int lockRes = surface.lockSurface(); + if ( NativeSurface.LOCK_SURFACE_CHANGED == lockRes && realized ) { + // Update the drawable handle, in case the surface handle has changed. + final long _handle1 = getHandle(); + destroyHandle(); + createHandle(); + final long _handle2 = getHandle(); + if(DEBUG) { + if( _handle1 != _handle2) { + System.err.println(getThreadName() + ": Drawable handle changed: "+toHexString(_handle1)+" -> "+toHexString(_handle2)); + } + } + } + return lockRes; + } - public boolean isSurfaceLocked() { - return surface.isSurfaceLocked(); + /** + * {@link NativeSurface#unlockSurface() Unlocks} the underlying windowing toolkit {@link NativeSurface surface}, + * which may render the {@link #getHandle() drawable handle} invalid. + * + * @see NativeSurface#unlockSurface() + * @see #getHandle() + */ + public final void unlockSurface() { + surface.unlockSurface(); } + @Override public String toString() { return getClass().getSimpleName()+"[Realized "+isRealized()+ ",\n\tFactory "+getFactory()+ - ",\n\thandle "+toHexString(getHandle())+ - ",\n\tWindow "+getNativeSurface()+"]"; + ",\n\tHandle "+toHexString(getHandle())+ + ",\n\tSurface "+getNativeSurface()+"]"; } - protected static String getThreadName() { - return Thread.currentThread().getName(); - } - - protected GLDrawableFactory factory; - protected NativeSurface surface; - protected GLCapabilitiesImmutable requestedCapabilities; + protected static String getThreadName() { return Thread.currentThread().getName(); } + + protected final GLDrawableFactory factory; + protected final NativeSurface surface; + protected final GLCapabilitiesImmutable requestedCapabilities; // Indicates whether the surface (if an onscreen context) has been // realized. Plausibly, before the surface is realized the JAWT @@ -228,6 +358,6 @@ public abstract class GLDrawableImpl implements GLDrawable { // result of calling show() on the main thread. To work around this // we prevent any JAWT or OpenGL operations from being done until // addNotify() is called on the surface. - protected boolean realized; + protected volatile boolean realized; } diff --git a/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java index 4c82fc2b3..39de3200d 100644 --- a/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/GLDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.opengl; import com.jogamp.common.os.DynamicLibraryBundle; @@ -36,17 +36,31 @@ public abstract class GLDynamicLibraryBundleInfo implements DynamicLibraryBundle protected GLDynamicLibraryBundleInfo() { } - /** 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> + */ @Override - public boolean shallLinkGlobal() { return false; } + public final boolean shallLinkGlobal() { return true; } - /** default **/ + /** + * {@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..421f06205 100644 --- a/src/jogl/classes/jogamp/opengl/GLDynamicLookupHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDynamicLookupHelper.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.opengl; import com.jogamp.common.os.DynamicLibraryBundle; @@ -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 new file mode 100644 index 000000000..6f5fa3eed --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java @@ -0,0 +1,593 @@ +package jogamp.opengl; + +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.opengl.GL; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.GLFBODrawable; + +import com.jogamp.common.util.VersionUtil; +import com.jogamp.nativewindow.MutableGraphicsConfiguration; +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.FBObject.Attachment; +import com.jogamp.opengl.FBObject.Colorbuffer; +import com.jogamp.opengl.FBObject.TextureAttachment; +import com.jogamp.opengl.JoglVersion; + +/** + * {@link FBObject} offscreen GLDrawable implementation, i.e. {@link GLFBODrawable}. + * <p> + * It utilizes the context lifecycle hook {@link #contextRealized(GLContext, boolean)} + * to initialize the {@link FBObject} instance. + * </p> + * <p> + * It utilizes the context current hook {@link #contextMadeCurrent(GLContext, boolean) contextMadeCurrent(context, true)} + * to {@link FBObject#bind(GL) bind} the FBO. + * </p> + * See {@link GLFBODrawable} for double buffering details. + * + * @see GLDrawableImpl#contextRealized(GLContext, boolean) + * @see GLDrawableImpl#contextMadeCurrent(GLContext, boolean) + * @see GLDrawableImpl#getDefaultDrawFramebuffer() + * @see GLDrawableImpl#getDefaultReadFramebuffer() + */ +public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { + protected static final boolean DEBUG; + protected static final boolean DEBUG_SWAP; + + static { + Debug.initSingleton(); + DEBUG = GLDrawableImpl.DEBUG || Debug.debug("FBObject"); + DEBUG_SWAP = DEBUG || Debug.isPropertyDefined("jogl.debug.FBObject.Swap", true); + } + + private final GLDrawableImpl parent; + private GLCapabilitiesImmutable origParentChosenCaps; + + private boolean initialized; + private int texUnit; + private int samples; + private boolean fboResetQuirk; + + private FBObject[] fbos; + 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; + + /** number of FBOs for double buffering. TODO: Possible to configure! */ + private static final int bufferCount = 2; + + // private DoubleBufferMode doubleBufferMode; // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + + private SwapBufferContext swapBufferContext; + + public static interface SwapBufferContext { + public void swapBuffers(boolean doubleBuffered); + } + + /** + * @param factory + * @param parent + * @param surface + * @param fboCaps the requested FBO capabilities + * @param textureUnit + */ + protected GLFBODrawableImpl(GLDrawableFactoryImpl factory, GLDrawableImpl parent, NativeSurface surface, + GLCapabilitiesImmutable fboCaps, int textureUnit) { + super(factory, surface, fboCaps, false); + this.initialized = false; + + this.parent = parent; + this.origParentChosenCaps = getChosenGLCapabilities(); // just to avoid null, will be reset at initialize(..) + this.texUnit = textureUnit; + this.samples = fboCaps.getNumSamples(); + fboResetQuirk = false; + + // default .. // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + // this.doubleBufferMode = ( samples > 0 || fboCaps.getDoubleBuffered() ) ? DoubleBufferMode.FBO : DoubleBufferMode.NONE ; + + this.swapBufferContext = null; + } + + private final void initialize(boolean realize, GL gl) { + if( !initialized && !realize ) { + if( DEBUG ) { + System.err.println("GLFBODrawableImpl.initialize(): WARNING - Already unrealized!"); + Thread.dumpStack(); + } + return; // NOP, no exception for de-init twice or no init! + } + if( initialized == realize ) { + throw new IllegalStateException("initialize already in state "+realize+": "+this); + } + if(realize) { + final GLCapabilities chosenFBOCaps = (GLCapabilities) getChosenGLCapabilities(); // cloned at setRealized(true) + + final int maxSamples = gl.getMaxRenderbufferSamples(); + { + final int newSamples = samples <= maxSamples ? samples : maxSamples; + if(DEBUG) { + System.err.println("GLFBODrawableImpl.initialize(): samples "+samples+" -> "+newSamples+"/"+maxSamples); + } + samples = newSamples; + } + + final int fbosN; + if(samples > 0) { + fbosN = 1; + } else if( chosenFBOCaps.getDoubleBuffered() ) { + fbosN = bufferCount; + } else { + fbosN = 1; + } + + fbos = new FBObject[fbosN]; + fboIBack = 0; // head + fboIFront = fbos.length - 1; // tail + + for(int i=0; i<fbosN; i++) { + fbos[i] = new FBObject(); + fbos[i].reset(gl, getWidth(), getHeight(), samples, false); + if(fbos[i].getNumSamples() != samples) { + throw new InternalError("Sample number mismatch: "+samples+", fbos["+i+"] "+fbos[i]); + } + if(samples > 0) { + fbos[i].attachColorbuffer(gl, 0, chosenFBOCaps.getAlphaBits()>0); + } else { + fbos[i].attachTexture2D(gl, 0, chosenFBOCaps.getAlphaBits()>0); + } + if( chosenFBOCaps.getStencilBits() > 0 ) { + fbos[i].attachRenderbuffer(gl, Attachment.Type.DEPTH_STENCIL, 24); + } else { + fbos[i].attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + } + } + fbos[fboIFront].resetSamplingSink(gl); + + fbos[0].formatToGLCapabilities(chosenFBOCaps); + chosenFBOCaps.setDoubleBuffered( chosenFBOCaps.getDoubleBuffered() || samples > 0 ); + } else { + for(int i=0; i<fbos.length; i++) { + fbos[i].destroy(gl); + } + fbos=null; + } + fboBound = false; + fboSwapped = false; + pendingFBOReset = -1; + initialized = realize; + + if(DEBUG) { + System.err.println("GLFBODrawableImpl.initialize("+realize+"): "+this); + Thread.dumpStack(); + } + } + + public final void setSwapBufferContext(SwapBufferContext sbc) { + swapBufferContext = sbc; + } + + private final void reset(GL gl, int idx, int width, int height, int samples, int alphaBits, int stencilBits) { + if( !fboResetQuirk ) { + try { + fbos[idx].reset(gl, width, height, samples, false); + if(fbos[idx].getNumSamples() != samples) { + throw new InternalError("Sample number mismatch: "+samples+", fbos["+idx+"] "+fbos[idx]); + } + return; + } catch (GLException e) { + fboResetQuirk = true; + if(DEBUG) { + if(!resetQuirkInfoDumped) { + resetQuirkInfoDumped = true; + System.err.println("GLFBODrawable: FBO Reset failed: "+e.getMessage()); + System.err.println("GLFBODrawable: Enabling FBOResetQuirk, due to GL driver bug."); + final JoglVersion joglVersion = JoglVersion.getInstance(); + if(DEBUG) { + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println(joglVersion.toString()); + System.err.println(JoglVersion.getGLInfo(gl, null)); + } else { + System.err.println(joglVersion.getBriefOSGLBuildInfo(gl, null)); + } + e.printStackTrace(); + } + } + // 'fallthrough' intended + } + } + // resetQuirk fallback + fbos[idx].destroy(gl); + fbos[idx] = new FBObject(); + fbos[idx].reset(gl, getWidth(), getHeight(), samples, false); + if(fbos[idx].getNumSamples() != samples) { + throw new InternalError("Sample number mismatch: "+samples+", fbos["+idx+"] "+fbos[idx]); + } + if(samples > 0) { + fbos[idx].attachColorbuffer(gl, 0, alphaBits>0); + } else { + fbos[idx].attachTexture2D(gl, 0, alphaBits>0); + } + if( stencilBits > 0 ) { + fbos[idx].attachRenderbuffer(gl, Attachment.Type.DEPTH_STENCIL, 24); + } else { + fbos[idx].attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + } + } + + private final void reset(GL gl, int newSamples) throws GLException { + if(!initialized) { + // NOP if not yet initializes + return; + } + + final GLContext curContext = GLContext.getCurrent(); + final GLContext ourContext = gl.getContext(); + final boolean ctxSwitch = null != curContext && curContext != ourContext; + if(DEBUG) { + System.err.println("GLFBODrawableImpl.reset(newSamples "+newSamples+"): BEGIN - ctxSwitch "+ctxSwitch+", "+this); + Thread.dumpStack(); + } + Throwable tFBO = null; + Throwable tGL = null; + 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; + + if(0==samples && 0<newSamples || 0<samples && 0==newSamples) { + // MSAA on/off switch + if(DEBUG) { + System.err.println("GLFBODrawableImpl.reset(): samples [on/off] reconfig: "+samples+" -> "+newSamples+"/"+maxSamples); + } + initialize(false, gl); + samples = newSamples; + initialize(true, gl); + } else { + if(DEBUG) { + System.err.println("GLFBODrawableImpl.reset(): simple reconfig: "+samples+" -> "+newSamples+"/"+maxSamples); + } + final int nWidth = getWidth(); + final int nHeight = getHeight(); + samples = newSamples; + pendingFBOReset = ( 1 < fbos.length ) ? fboIFront : -1; // pending-front reset only w/ double buffering (or zero samples) + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); + for(int i=0; i<fbos.length; i++) { + if( pendingFBOReset != i ) { + reset(gl, i, nWidth, nHeight, samples, caps.getAlphaBits(), caps.getStencilBits()); + } + } + final GLCapabilities fboCapsNative = (GLCapabilities) surface.getGraphicsConfiguration().getChosenCapabilities(); + fbos[0].formatToGLCapabilities(fboCapsNative); + } + } catch (Throwable t) { + tFBO = t; + } finally { + try { + ourContext.release(); + if(ctxSwitch) { + curContext.makeCurrent(); + } + } catch (Throwable t) { + tGL = t; + } + } + if(null != tFBO) { + throw new GLException("GLFBODrawableImpl.reset(..) FBObject.reset(..) exception", tFBO); + } + if(null != tGL) { + throw new GLException("GLFBODrawableImpl.reset(..) GLContext.release() exception", tGL); + } + if(DEBUG) { + System.err.println("GLFBODrawableImpl.reset(newSamples "+newSamples+"): END "+this); + } + } + + // + // GLDrawable + // + + @Override + public final GLContext createContext(GLContext shareWith) { + final GLContext ctx = parent.createContext(shareWith); + ctx.setGLDrawable(this, false); + return ctx; + } + + // + // GLDrawableImpl + // + + @Override + public final GLDynamicLookupHelper getGLDynamicLookupHelper() { + return parent.getGLDynamicLookupHelper(); + } + + @Override + protected final int getDefaultDrawFramebuffer() { return initialized ? fbos[fboIBack].getWriteFramebuffer() : 0; } + + @Override + protected final int getDefaultReadFramebuffer() { return initialized ? fbos[fboIFront].getReadFramebuffer() : 0; } + + @Override + protected final int getDefaultReadBuffer(GL gl, boolean hasDedicatedDrawableRead) { + return initialized ? fbos[fboIFront].getDefaultReadBuffer() : GL.GL_COLOR_ATTACHMENT0 ; + } + + @Override + protected final void setRealizedImpl() { + final MutableGraphicsConfiguration msConfig = (MutableGraphicsConfiguration) surface.getGraphicsConfiguration(); + if(realized) { + parent.setRealized(true); + origParentChosenCaps = (GLCapabilitiesImmutable) msConfig.getChosenCapabilities(); + final GLCapabilities chosenFBOCaps = (GLCapabilities) origParentChosenCaps.cloneMutable(); + chosenFBOCaps.copyFrom(getRequestedGLCapabilities()); + msConfig.setChosenCapabilities(chosenFBOCaps); + } else { + msConfig.setChosenCapabilities(origParentChosenCaps); + parent.setRealized(false); + } + } + + @Override + protected void associateContext(GLContext glc, boolean bound) { + initialize(bound, glc.getGL()); + } + + @Override + protected final void contextMadeCurrent(GLContext glc, boolean current) { + final GL gl = glc.getGL(); + if(current) { + if( !initialized ) { + throw new GLException("Not initialized: "+this); + } + fbos[fboIBack].bind(gl); + fboBound = true; + 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"); + } + } + } + + @Override + protected void swapBuffersImpl(boolean doubleBuffered) { + final GLContext ctx = GLContext.getCurrent(); + boolean doPostSwap; + if( null != ctx && ctx.getGLDrawable() == this && fboBound ) { + swapFBOImpl(ctx); + doPostSwap = true; + fboSwapped = true; + if(DEBUG_SWAP) { + System.err.println("Post FBO swap(@swap): done"); + } + } else { + doPostSwap = false; + } + if( null != swapBufferContext ) { + swapBufferContext.swapBuffers(doubleBuffered); + } + if(doPostSwap) { + swapFBOImplPost(ctx); + } + } + + private final void swapFBOImplPost(GLContext glc) { + // Safely reset the previous front FBO - after completing propagating swap + if(0 <= pendingFBOReset) { + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); + reset(glc.getGL(), pendingFBOReset, getWidth(), getHeight(), samples, caps.getAlphaBits(), caps.getStencilBits()); + pendingFBOReset = -1; + } + } + + private final void swapFBOImpl(GLContext glc) { + final GL gl = glc.getGL(); + fbos[fboIBack].markUnbound(); // fast path, use(gl,..) is called below + + if(DEBUG) { + int _fboIFront = ( fboIFront + 1 ) % fbos.length; + if(_fboIFront != fboIBack) { throw new InternalError("XXX: "+_fboIFront+"!="+fboIBack); } + } + fboIFront = fboIBack; + fboIBack = ( fboIBack + 1 ) % fbos.length; + + final Colorbuffer colorbuffer = samples > 0 ? fbos[fboIFront].getSamplingSink() : fbos[fboIFront].getColorbuffer(0); + final TextureAttachment texAttachment; + if(colorbuffer instanceof TextureAttachment) { + texAttachment = (TextureAttachment) colorbuffer; + } else { + if(null == colorbuffer) { + throw new GLException("Front colorbuffer is null: samples "+samples+", "+this); + } else { + throw new GLException("Front colorbuffer is not a texture: "+colorbuffer.getClass().getName()+": samples "+samples+", "+colorbuffer+", "+this); + } + } + gl.glActiveTexture(GL.GL_TEXTURE0 + texUnit); + fbos[fboIFront].use(gl, texAttachment); + + /* Included in above use command: + gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, fbos[fboIBack].getDrawFramebuffer()); + gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, fbos[fboIFront].getReadFramebuffer()); + } */ + + if(DEBUG_SWAP) { + System.err.println("Post FBO swap(X): fboI back "+fboIBack+", front "+fboIFront+", num "+fbos.length); + } + } + + // + // GLFBODrawable + // + + @Override + public final boolean isInitialized() { + return initialized; + } + + @Override + public final void resetSize(GL gl) throws GLException { + reset(gl, samples); + } + + @Override + public final int getTextureUnit() { return texUnit; } + + @Override + public final void setTextureUnit(int u) { texUnit = u; } + + @Override + public final int getNumSamples() { return samples; } + + @Override + public void setNumSamples(GL gl, int newSamples) throws GLException { + if(samples != newSamples) { + reset(gl, newSamples); + } + } + + @Override + public final int setNumBuffers(int bufferCount) throws GLException { + // FIXME: Implement + return bufferCount; + } + + @Override + public final int getNumBuffers() { + return bufferCount; + } + + /** // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + @Override + public final DoubleBufferMode getDoubleBufferMode() { + return doubleBufferMode; + } + + @Override + public final void setDoubleBufferMode(DoubleBufferMode mode) throws GLException { + if(initialized) { + throw new GLException("Not allowed past initialization: "+this); + } + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); + if(0 == samples && caps.getDoubleBuffered() && DoubleBufferMode.NONE != mode) { + doubleBufferMode = mode; + } + } */ + + @Override + public FBObject getFBObject(int bufferName) throws IllegalArgumentException { + if(!initialized) { + return null; + } + final FBObject res; + switch(bufferName) { + case GL.GL_FRONT: + if( samples > 0 ) { + res = fbos[0].getSamplingSinkFBO(); + } else { + res = fbos[fboIFront]; + } + break; + case GL.GL_BACK: + res = fbos[fboIBack]; + break; + default: + throw new IllegalArgumentException(illegalBufferName+toHexString(bufferName)); + } + return res; + } + + @Override + public final TextureAttachment getTextureBuffer(int bufferName) throws IllegalArgumentException { + if(!initialized) { + return null; + } + final TextureAttachment res; + switch(bufferName) { + case GL.GL_FRONT: + if( samples > 0 ) { + res = fbos[0].getSamplingSink(); + } else { + res = (TextureAttachment) fbos[fboIFront].getColorbuffer(0); + } + break; + case GL.GL_BACK: + if( samples > 0 ) { + throw new IllegalArgumentException("Cannot access GL_BACK buffer of MSAA FBO: "+this); + } else { + res = (TextureAttachment) fbos[fboIBack].getColorbuffer(0); + } + break; + default: + throw new IllegalArgumentException(illegalBufferName+toHexString(bufferName)); + } + return res; + } + private static final String illegalBufferName = "Only GL_FRONT and GL_BACK buffer are allowed, passed "; + + @Override + public String toString() { + return getClass().getSimpleName()+"[Initialized "+initialized+", realized "+isRealized()+", texUnit "+texUnit+", samples "+samples+ + ",\n\tFactory "+getFactory()+ + ",\n\tHandle "+toHexString(getHandle())+ + ",\n\tCaps "+surface.getGraphicsConfiguration().getChosenCapabilities()+ + ",\n\tfboI back "+fboIBack+", front "+fboIFront+", num "+(initialized ? fbos.length : 0)+ + ",\n\tFBO front read "+getDefaultReadFramebuffer()+", "+getFBObject(GL.GL_FRONT)+ + ",\n\tFBO back write "+getDefaultDrawFramebuffer()+", "+getFBObject(GL.GL_BACK)+ + ",\n\tSurface "+surface+ + "]"; + } + + public static class ResizeableImpl extends GLFBODrawableImpl implements GLFBODrawable.Resizeable { + protected ResizeableImpl(GLDrawableFactoryImpl factory, GLDrawableImpl parent, ProxySurface surface, + GLCapabilitiesImmutable fboCaps, int textureUnit) { + super(factory, parent, surface, fboCaps, textureUnit); + } + + @Override + public final void setSize(GLContext context, int newWidth, int newHeight) throws NativeWindowException, GLException { + if(DEBUG) { + System.err.println("GLFBODrawableImpl.ResizeableImpl setSize: ("+getThreadName()+"): "+newWidth+"x"+newHeight+" - surfaceHandle 0x"+Long.toHexString(getNativeSurface().getSurfaceHandle())); + } + int lockRes = lockSurface(); + if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) { + throw new NativeWindowException("Could not lock surface: "+this); + } + try { + // propagate new size + final ProxySurface ps = (ProxySurface) getNativeSurface(); + final UpstreamSurfaceHook ush = ps.getUpstreamSurfaceHook(); + if(ush instanceof UpstreamSurfaceHook.MutableSize) { + ((UpstreamSurfaceHook.MutableSize)ush).setSize(newWidth, newHeight); + } else { + throw new InternalError("GLFBODrawableImpl.ResizableImpl's ProxySurface doesn't hold a UpstreamSurfaceHookMutableSize but "+ush.getClass().getName()+", "+ps+", ush"); + } + if( null != context && context.isCreated() ) { + resetSize(context.getGL()); + } + } finally { + unlockSurface(); + } + } + } +} diff --git a/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationFactory.java index 6fe9f080c..5c6b475b2 100644 --- a/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationFactory.java @@ -38,7 +38,7 @@ import javax.media.opengl.DefaultGLCapabilitiesChooser; public abstract class GLGraphicsConfigurationFactory extends GraphicsConfigurationFactory { protected static int chooseCapabilities(CapabilitiesChooser chooser, CapabilitiesImmutable capsRequested, - List /*<CapabilitiesImmutable>*/ availableCaps, int recommendedIndex) { + List<? extends CapabilitiesImmutable> availableCaps, int recommendedIndex) { if (null == capsRequested) { throw new NativeWindowException("Null requested capabilities"); } diff --git a/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java b/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java index d7958c7f1..702fb77de 100644 --- a/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java +++ b/src/jogl/classes/jogamp/opengl/GLGraphicsConfigurationUtil.java @@ -28,17 +28,22 @@ package jogamp.opengl; -import java.util.ArrayList; - +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLProfile; + +import com.jogamp.opengl.GLRendererQuirks; public class GLGraphicsConfigurationUtil { public static final String NV_coverage_sample = "NV_coverage_sample"; public static final int WINDOW_BIT = 1 << 0; public static final int BITMAP_BIT = 1 << 1; public static final int PBUFFER_BIT = 1 << 2; - public static final int ALL_BITS = WINDOW_BIT | BITMAP_BIT | PBUFFER_BIT ; + public static final int FBO_BIT = 1 << 3; // generic bit must be mapped to native one at impl. level + public static final int ALL_BITS = WINDOW_BIT | BITMAP_BIT | PBUFFER_BIT | FBO_BIT ; public static final StringBuilder winAttributeBits2String(StringBuilder sb, int winattrbits) { if(null==sb) { @@ -61,74 +66,184 @@ public class GLGraphicsConfigurationUtil { sb.append(", "); } sb.append("PBUFFER"); + seperator=true; + } + if( 0 != ( FBO_BIT & winattrbits ) ) { + if(seperator) { + sb.append(", "); + } + sb.append("FBO"); } return sb; } /** - * @return bitmask representing the input boolean in exclusive or logic, ie only one bit will be set - */ - public static final int getWinAttributeBits(boolean isOnscreen, boolean isPBuffer) { + public static final int getWinAttributeBits(boolean isOnscreen, boolean isFBO, boolean isPBuffer, boolean isBitmap) { int winattrbits = 0; if(isOnscreen) { winattrbits |= WINDOW_BIT; - } else if (!isPBuffer) { + } + if(isFBO) { + winattrbits |= FBO_BIT; + } + if(isPBuffer ){ + winattrbits |= PBUFFER_BIT; + } + if(isBitmap) { winattrbits |= BITMAP_BIT; + } + return winattrbits; + } + public static final int getWinAttributeBits(GLCapabilitiesImmutable caps) { + return getWinAttributeBits(caps.isOnscreen(), caps.isFBO(), caps.isPBuffer(), caps.isBitmap()); + } */ + + /** + * @return bitmask representing the input boolean in exclusive or logic, ie only one bit will be set. + */ + public static final int getExclusiveWinAttributeBits(boolean isOnscreen, boolean isFBO, boolean isPBuffer, boolean isBitmap) { + final int winattrbits; + if(isOnscreen) { + winattrbits = WINDOW_BIT; + } else if(isFBO) { + winattrbits = FBO_BIT; + } else if(isPBuffer ){ + winattrbits = PBUFFER_BIT; + } else if(isBitmap) { + winattrbits = BITMAP_BIT; } else { - winattrbits |= PBUFFER_BIT; + throw new InternalError("Empty bitmask"); } return winattrbits; } /** - * @see #getWinAttributeBits(boolean, boolean) + * @see #getExclusiveWinAttributeBits(boolean, boolean, boolean, boolean) */ - public static final int getWinAttributeBits(GLCapabilitiesImmutable caps) { - return getWinAttributeBits(caps.isOnscreen(), caps.isPBuffer()); + public static final int getExclusiveWinAttributeBits(GLCapabilitiesImmutable caps) { + return getExclusiveWinAttributeBits(caps.isOnscreen(), caps.isFBO(), caps.isPBuffer(), caps.isBitmap()); } - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static final boolean addGLCapabilitiesPermutations(ArrayList capsBucket, GLCapabilitiesImmutable temp, int winattrbits) { - int preSize = capsBucket.size(); - if( 0 != ( WINDOW_BIT & winattrbits ) ) { - GLCapabilities cpy = (GLCapabilities) temp.cloneMutable(); - cpy.setOnscreen(true); - capsBucket.add(cpy); - } - if( 0 != ( PBUFFER_BIT & winattrbits ) ) { - GLCapabilities cpy = (GLCapabilities) temp.cloneMutable(); - cpy.setPBuffer(true); - capsBucket.add(cpy); + public static final GLCapabilities fixWinAttribBitsAndHwAccel(AbstractGraphicsDevice device, int winattrbits, GLCapabilities caps) { + caps.setBitmap ( 0 != ( BITMAP_BIT & winattrbits ) ); + caps.setPBuffer ( 0 != ( PBUFFER_BIT & winattrbits ) ); + caps.setFBO ( 0 != ( FBO_BIT & winattrbits ) ); + // we reflect availability semantics, hence setting onscreen at last (maybe overwritten above)! + caps.setOnscreen( 0 != ( WINDOW_BIT & winattrbits ) ); + + final int accel = GLContext.isHardwareRasterizer( device, caps.getGLProfile() ); + if(0 == accel && caps.getHardwareAccelerated() ) { + caps.setHardwareAccelerated(false); } - if( 0 != ( BITMAP_BIT & winattrbits ) ) { - GLCapabilities cpy = (GLCapabilities) temp.cloneMutable(); - cpy.setOnscreen(false); - cpy.setPBuffer(false); - capsBucket.add(cpy); + + return caps; + } + + /** + * Fixes the requested {@link GLCapabilitiesImmutable} according to on- and offscreen usage. + * <p> + * No modification will be made for onscreen usage, for offscreen usage see + * {@link #fixOffscreenGLCapabilities(GLCapabilitiesImmutable, GLDrawableFactory, AbstractGraphicsDevice)}. + * </p> + * @param capsRequested the requested {@link GLCapabilitiesImmutable} + * @param factory the {@link GLDrawableFactory} used to validate the requested capabilities and later used to create the drawable. + * @param device the device on which the drawable will be created, maybe null for the {@link GLDrawableFactory#getDefaultDevice() default device}. + * @return either the given requested {@link GLCapabilitiesImmutable} instance if no modifications were required, or a modified {@link GLCapabilitiesImmutable} instance. + */ + public static GLCapabilitiesImmutable fixGLCapabilities(GLCapabilitiesImmutable capsRequested, + GLDrawableFactory factory, AbstractGraphicsDevice device) { + if( !capsRequested.isOnscreen() ) { + return fixOffscreenGLCapabilities(capsRequested, factory, device); } - return capsBucket.size() > preSize; + return capsRequested; } - public static GLCapabilitiesImmutable fixGLCapabilities(GLCapabilitiesImmutable capsRequested, boolean pbufferAvailable) + public static GLCapabilitiesImmutable fixOnscreenGLCapabilities(GLCapabilitiesImmutable capsRequested) { - if( !capsRequested.isOnscreen() ) { - return fixOffScreenGLCapabilities(capsRequested, pbufferAvailable); + if( !capsRequested.isOnscreen() || capsRequested.isFBO() || capsRequested.isPBuffer() || capsRequested.isBitmap() ) { + // fix caps .. + final GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); + caps2.setBitmap (false); + caps2.setPBuffer (false); + caps2.setFBO (false); + caps2.setOnscreen(true); + return caps2; } return capsRequested; } - public static GLCapabilitiesImmutable fixOffScreenGLCapabilities(GLCapabilitiesImmutable capsRequested, boolean pbufferAvailable) + public static GLCapabilitiesImmutable fixOffscreenBitOnly(GLCapabilitiesImmutable capsRequested) { - if( capsRequested.getDoubleBuffered() || - capsRequested.isOnscreen() || - ( !pbufferAvailable && capsRequested.isPBuffer() ) ) + if( capsRequested.isOnscreen() ) { + // fix caps .. + final GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); + caps2.setOnscreen(false); + return caps2; + } + return capsRequested; + } + + /** + * Fixes the requested {@link GLCapabilitiesImmutable} according to: + * <ul> + * <li>offscreen usage</li> + * <li>availability of FBO, PBuffer, Bitmap</li> + * <li>{@link GLRendererQuirks}</li> + * </ul> + * @param capsRequested the requested {@link GLCapabilitiesImmutable} + * @param factory the {@link GLDrawableFactory} used to validate the requested capabilities and later used to create the drawable. + * @param device the device on which the drawable will be created, maybe null for the {@link GLDrawableFactory#getDefaultDevice() default device}. + * @return either the given requested {@link GLCapabilitiesImmutable} instance if no modifications were required, or a modified {@link GLCapabilitiesImmutable} instance. + */ + public static GLCapabilitiesImmutable fixOffscreenGLCapabilities(GLCapabilitiesImmutable capsRequested, + GLDrawableFactory factory, AbstractGraphicsDevice device) { + if(null == device) { + device = factory.getDefaultDevice(); + } + 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; + final boolean doubleBufferAvailable; + + if(null != glrq) { + bitmapAvailable = !glrq.exist(GLRendererQuirks.NoOffscreenBitmap); + if( capsRequested.getDoubleBuffered() && + ( capsRequested.isPBuffer() && glrq.exist(GLRendererQuirks.NoDoubleBufferedPBuffer) ) || + ( capsRequested.isBitmap() && glrq.exist(GLRendererQuirks.NoDoubleBufferedBitmap) ) ) { + doubleBufferAvailable = false; + } else { + doubleBufferAvailable = true; + } + } else { + bitmapAvailable = true; + doubleBufferAvailable = true; + } + + final boolean auto = !( fboAvailable && capsRequested.isFBO() ) && + !( pbufferAvailable && capsRequested.isPBuffer() ) && + !( bitmapAvailable && capsRequested.isBitmap() ) ; + + final boolean useFBO = fboAvailable && ( auto || capsRequested.isFBO() ) ; + final boolean usePbuffer = !useFBO && pbufferAvailable && ( auto || capsRequested.isPBuffer() ) ; + final boolean useBitmap = !useFBO && !usePbuffer && bitmapAvailable && ( auto || capsRequested.isBitmap() ) ; + + if( capsRequested.isOnscreen() || + useFBO != capsRequested.isFBO() || + usePbuffer != capsRequested.isPBuffer() || + useBitmap != capsRequested.isBitmap() || + !doubleBufferAvailable && capsRequested.getDoubleBuffered() ) { // fix caps .. - GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); - caps2.setDoubleBuffered(false); // FIXME DBLBUFOFFSCRN + final GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); caps2.setOnscreen(false); - if(caps2.isPBuffer() && !pbufferAvailable) { - caps2.setPBuffer(false); + caps2.setFBO( useFBO ); + caps2.setPBuffer( usePbuffer ); + caps2.setBitmap( useBitmap ); + if( !doubleBufferAvailable ) { + caps2.setDoubleBuffered(false); } return caps2; } @@ -137,28 +252,81 @@ public class GLGraphicsConfigurationUtil { public static GLCapabilitiesImmutable fixGLPBufferGLCapabilities(GLCapabilitiesImmutable capsRequested) { - if( capsRequested.getDoubleBuffered() || capsRequested.isOnscreen() || !capsRequested.isPBuffer()) { + if( capsRequested.isOnscreen() || + !capsRequested.isPBuffer() || + capsRequested.isFBO() ) + { // fix caps .. - GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); - caps2.setDoubleBuffered(false); // FIXME DBLBUFOFFSCRN - we don't need to be single buffered .. + final GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); caps2.setOnscreen(false); + caps2.setFBO(false); caps2.setPBuffer(true); + caps2.setBitmap(false); return caps2; } return capsRequested; } - public static GLCapabilitiesImmutable fixOpaqueGLCapabilities(GLCapabilitiesImmutable capsRequested, boolean isOpaque) + /** Fix opaque setting while preserve alpha bits */ + public static GLCapabilities fixOpaqueGLCapabilities(GLCapabilities capsRequested, boolean isOpaque) { - GLCapabilities caps2 = null; - if( capsRequested.isBackgroundOpaque() != isOpaque) { - // fix caps .. - caps2 = (GLCapabilities) capsRequested.cloneMutable(); - caps2.setBackgroundOpaque(isOpaque); + final int alphaBits = capsRequested.getAlphaBits(); + capsRequested.setBackgroundOpaque(isOpaque); + capsRequested.setAlphaBits(alphaBits); + } + return capsRequested; + } + + /** Fix double buffered setting */ + public static GLCapabilitiesImmutable fixDoubleBufferedGLCapabilities(GLCapabilitiesImmutable capsRequested, boolean doubleBuffered) + { + if( capsRequested.getDoubleBuffered() != doubleBuffered) { + final GLCapabilities caps2 = (GLCapabilities) capsRequested.cloneMutable(); + caps2.setDoubleBuffered(doubleBuffered); return caps2; } return capsRequested; } - + + public static GLCapabilitiesImmutable clipRGBAGLCapabilities(GLCapabilitiesImmutable caps, boolean allowRGB555, boolean allowAlpha) + { + final int iR = caps.getRedBits(); + final int iG = caps.getGreenBits(); + final int iB = caps.getBlueBits(); + final int iA = caps.getAlphaBits(); + final int oR = clipColor(iR, allowRGB555); + final int oG = clipColor(iG, allowRGB555); + final int oB = clipColor(iB, allowRGB555); + final int oA = ( allowAlpha && 0 < iA ) ? oR : 0 ; // align alpha to red if requested and allowed + if( iR != oR || iG != oG || iB != oB || iA != oA ) { + final GLCapabilities caps2 = (GLCapabilities) caps.cloneMutable(); + caps2.setRedBits(oR); + caps2.setGreenBits(oG); + caps2.setBlueBits(oB); + caps2.setAlphaBits(oA); + return caps2; + } + return caps; + } + + public static int clipColor(final int compIn, final boolean allowRGB555) { + final int compOut; + if( 5 < compIn || !allowRGB555 ) { + compOut = 8; + } else { + compOut = 5; + } + return compOut; + } + + public static GLCapabilitiesImmutable fixGLProfile(GLCapabilitiesImmutable caps, GLProfile glp) + { + if( caps.getGLProfile() != glp ) { + final GLCapabilities caps2 = (GLCapabilities) caps.cloneMutable(); + caps2.setGLProfile(glp); + return caps2; + } + return caps; + } } diff --git a/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java new file mode 100644 index 000000000..345f08e4c --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java @@ -0,0 +1,140 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl; + +import javax.media.nativewindow.NativeWindowException; +import javax.media.opengl.GL; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLException; +import javax.media.opengl.GLOffscreenAutoDrawable; + +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.GLAutoDrawableDelegate; + +import jogamp.opengl.GLFBODrawableImpl; + +public class GLOffscreenAutoDrawableImpl extends GLAutoDrawableDelegate implements GLOffscreenAutoDrawable { + + /** + * @param drawable a valid {@link GLDrawable}, may not be {@link GLDrawable#isRealized() realized} yet. + * @param context a valid {@link GLContext}, + * may not have been made current (created) yet, + * may not be associated w/ <code>drawable<code> yet, + * may be <code>null</code> for lazy initialization at 1st {@link #display()}. + * @param upstreamWidget optional UI element holding this instance, see {@link #getUpstreamWidget()}. + * @param lock optional upstream lock, may be null + */ + public GLOffscreenAutoDrawableImpl(GLDrawable drawable, GLContext context, Object upstreamWidget, RecursiveLock lock) { + super(drawable, context, upstreamWidget, true, lock); + } + + @Override + public void setSize(int newWidth, int newHeight) throws NativeWindowException, GLException { + this.defaultWindowResizedOp(newWidth, newHeight); + } + + public static class FBOImpl extends GLOffscreenAutoDrawableImpl implements GLOffscreenAutoDrawable.FBO { + /** + * @param drawable a valid {@link GLDrawable}, may not be {@link GLDrawable#isRealized() realized} yet. + * @param context a valid {@link GLContext}, + * may not have been made current (created) yet, + * may not be associated w/ <code>drawable<code> yet, + * may be <code>null</code> for lazy initialization + * @param upstreamWidget optional UI element holding this instance, see {@link #getUpstreamWidget()}. + * @param lock optional upstream lock, may be null + */ + public FBOImpl(GLFBODrawableImpl drawable, GLContext context, Object upstreamWidget, RecursiveLock lock) { + super(drawable, context, upstreamWidget, lock); + } + + @Override + public boolean isInitialized() { + return ((GLFBODrawableImpl)drawable).isInitialized(); + } + + @Override + public final int getTextureUnit() { + return ((GLFBODrawableImpl)drawable).getTextureUnit(); + } + + @Override + public final void setTextureUnit(int unit) { + ((GLFBODrawableImpl)drawable).setTextureUnit(unit); + } + + @Override + public final int getNumSamples() { + return ((GLFBODrawableImpl)drawable).getNumSamples(); + } + + @Override + public final void setNumSamples(GL gl, int newSamples) throws GLException { + ((GLFBODrawableImpl)drawable).setNumSamples(gl, newSamples); + windowRepaintOp(); + } + + @Override + public final int setNumBuffers(int bufferCount) throws GLException { + return ((GLFBODrawableImpl)drawable).setNumBuffers(bufferCount); + } + + @Override + public final int getNumBuffers() { + return ((GLFBODrawableImpl)drawable).getNumBuffers(); + } + + /** // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + @Override + public DoubleBufferMode getDoubleBufferMode() { + return ((GLFBODrawableImpl)drawable).getDoubleBufferMode(); + } + + @Override + public void setDoubleBufferMode(DoubleBufferMode mode) throws GLException { + ((GLFBODrawableImpl)drawable).setDoubleBufferMode(mode); + } */ + + @Override + public final FBObject getFBObject(int bufferName) { + return ((GLFBODrawableImpl)drawable).getFBObject(bufferName); + } + + @Override + public final FBObject.TextureAttachment getTextureBuffer(int bufferName) { + return ((GLFBODrawableImpl)drawable).getTextureBuffer(bufferName); + } + + @Override + public void resetSize(GL gl) throws GLException { + ((GLFBODrawableImpl)drawable).resetSize(gl); + } + } +} diff --git a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java index 0f4f7f8e2..c32957b86 100644 --- a/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLPbufferImpl.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,283 +29,77 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl; -import com.jogamp.common.util.locks.LockFactory; -import com.jogamp.common.util.locks.RecursiveLock; - -import javax.media.nativewindow.AbstractGraphicsDevice; -import javax.media.nativewindow.NativeSurface; -import javax.media.opengl.GL; -import javax.media.opengl.GLAnimatorControl; -import javax.media.opengl.GLCapabilitiesImmutable; -import javax.media.opengl.GLContext; -import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; -import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLPbuffer; -import javax.media.opengl.GLProfile; -import javax.media.opengl.GLRunnable; -/** Platform-independent class exposing pbuffer functionality to - applications. This class is not exposed in the public API as it - would probably add no value; however it implements the GLDrawable - interface so can be interacted with via its display() method. */ +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; -public class GLPbufferImpl implements GLPbuffer { - private GLDrawableImpl pbufferDrawable; - private GLContextImpl context; - private GLDrawableHelper drawableHelper = new GLDrawableHelper(); - private int floatMode; - private int additionalCtxCreationFlags = 0; +@SuppressWarnings("deprecation") +public class GLPbufferImpl extends GLAutoDrawableBase implements GLPbuffer { - public GLPbufferImpl(GLDrawableImpl pbufferDrawable, - GLContext parentContext) { - GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) - pbufferDrawable.getNativeSurface().getGraphicsConfiguration().getChosenCapabilities(); - if(caps.isOnscreen()) { - if(caps.isPBuffer()) { - throw new IllegalArgumentException("Error: Given drawable is Onscreen and Pbuffer: "+pbufferDrawable); - } - throw new IllegalArgumentException("Error: Given drawable is Onscreen: "+pbufferDrawable); - } else { - if(!caps.isPBuffer()) { - throw new IllegalArgumentException("Error: Given drawable is not Pbuffer: "+pbufferDrawable); - } - } - this.pbufferDrawable = pbufferDrawable; - context = (GLContextImpl) pbufferDrawable.createContext(parentContext); + public GLPbufferImpl(GLDrawableImpl pbufferDrawable, GLContextImpl pbufferContext) { + super(pbufferDrawable, pbufferContext, true); // drawable := pbufferDrawable, context := pbufferContext } - public GLContext createContext(GLContext shareWith) { - return pbufferDrawable.createContext(shareWith); - } + // + // pbuffer specifics + // - public void setRealized(boolean realized) { - } + // + // GLDrawable delegation + // - public boolean isRealized() { - return true; + @Override + public final void swapBuffers() throws GLException { + defaultSwapBuffers(); } - public void destroy() { - if(pbufferDrawable.isRealized()) { - final AbstractGraphicsDevice adevice = pbufferDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - - if (null != context && context.isCreated()) { - try { - drawableHelper.disposeGL(GLPbufferImpl.this, pbufferDrawable, context, null); - } catch (GLException gle) { - gle.printStackTrace(); - } - context = null; - // drawableHelper.reset(); - } - pbufferDrawable.destroy(); - pbufferDrawable = null; - - if(null != adevice) { - adevice.close(); - } - } - } + // + // GLAutoDrawable completion + // + private final RecursiveLock lock = LockFactory.createRecursiveLock(); // instance wide lock - public void setSize(int width, int height) { - // FIXME - throw new GLException("Not yet implemented"); - } + @Override + protected final RecursiveLock getLock() { return lock; } - public NativeSurface getNativeSurface() { - return pbufferDrawable.getNativeSurface(); + @Override + public final Object getUpstreamWidget() { + return null; } - public long getHandle() { - return pbufferDrawable.getHandle(); + @Override + public void destroy() { + defaultDestroy(); } + @Override public GLDrawableFactory getFactory() { - return pbufferDrawable.getFactory(); - } - - public int getWidth() { - return pbufferDrawable.getWidth(); - } - - public int getHeight() { - return pbufferDrawable.getHeight(); - } - - public void display() { - invokeGL(displayAction); - } - - public void repaint() { - display(); - } - - public void addGLEventListener(GLEventListener listener) { - drawableHelper.addGLEventListener(listener); - } - - public void addGLEventListener(int index, GLEventListener listener) { - drawableHelper.addGLEventListener(index, listener); - } - - public void removeGLEventListener(GLEventListener listener) { - drawableHelper.removeGLEventListener(listener); - } - - public void setAnimator(GLAnimatorControl animatorControl) { - drawableHelper.setAnimator(animatorControl); - } - - public GLAnimatorControl getAnimator() { - return drawableHelper.getAnimator(); - } - - public void invoke(boolean wait, GLRunnable glRunnable) { - drawableHelper.invoke(this, wait, glRunnable); - } - - public void setContext(GLContext ctx) { - context=(GLContextImpl)ctx; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); - } + return drawable.getFactory(); } - public GLContext getContext() { - return context; - } - - public GLDrawable getDrawable() { - return pbufferDrawable; - } - - public GL getGL() { - return getContext().getGL(); - } - - public GL setGL(GL gl) { - return getContext().setGL(gl); - } - - public void setAutoSwapBufferMode(boolean onOrOff) { - drawableHelper.setAutoSwapBufferMode(onOrOff); - } - - public boolean getAutoSwapBufferMode() { - return drawableHelper.getAutoSwapBufferMode(); - } - - public void swapBuffers() { - invokeGL(swapBuffersAction); - } - - public void setContextCreationFlags(int flags) { - additionalCtxCreationFlags = flags; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); - } - } - - public int getContextCreationFlags() { - return additionalCtxCreationFlags; - } - - public void bindTexture() { - // Doesn't make much sense to try to do this on the event dispatch - // thread given that it has to be called while the context is current - context.bindPbufferToTexture(); - } - - public void releaseTexture() { - // Doesn't make much sense to try to do this on the event dispatch - // thread given that it has to be called while the context is current - context.releasePbufferFromTexture(); - } - - public GLCapabilitiesImmutable getChosenGLCapabilities() { - if (pbufferDrawable == null) - return null; - - return pbufferDrawable.getChosenGLCapabilities(); - } - - public GLCapabilitiesImmutable getRequestedGLCapabilities() { - if (pbufferDrawable == null) - return null; - - return pbufferDrawable.getRequestedGLCapabilities(); - } - - public GLProfile getGLProfile() { - if (pbufferDrawable == null) - return null; - - return pbufferDrawable.getGLProfile(); - } - - private RecursiveLock recurLock = LockFactory.createRecursiveLock(); - - public int lockSurface() throws GLException { - recurLock.lock(); - return NativeSurface.LOCK_SUCCESS; - } - - public void unlockSurface() { - recurLock.unlock(); - } - - public boolean isSurfaceLocked() { - return recurLock.isLocked(); - } - - public int getFloatingPointMode() { - if (floatMode == 0) { - throw new GLException("Pbuffer not initialized, or floating-point support not requested"); - } - return floatMode; - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private void invokeGL(Runnable invokeGLAction) { - drawableHelper.invokeGL(pbufferDrawable, context, invokeGLAction, initAction); - } - - - class InitAction implements Runnable { - public void run() { - floatMode = context.getFloatingPointMode(); - drawableHelper.init(GLPbufferImpl.this); - } - } - private InitAction initAction = new InitAction(); - - class DisplayAction implements Runnable { - public void run() { - drawableHelper.display(GLPbufferImpl.this); - } - } - private DisplayAction displayAction = new DisplayAction(); - - class SwapBuffersAction implements Runnable { - public void run() { - pbufferDrawable.swapBuffers(); + @Override + public final void display() { + final RecursiveLock _lock = lock; + _lock.lock(); // sync: context/drawable could been recreated/destroyed while animating + try { + if( null != context ) { + helper.invokeGL(drawable, context, defaultDisplayAction, defaultInitAction); + } + } finally { + _lock.unlock(); } } - private SwapBuffersAction swapBuffersAction = new SwapBuffersAction(); } diff --git a/src/jogl/classes/jogamp/opengl/GLRunnableTask.java b/src/jogl/classes/jogamp/opengl/GLRunnableTask.java index 448f68423..6de92f533 100644 --- a/src/jogl/classes/jogamp/opengl/GLRunnableTask.java +++ b/src/jogl/classes/jogamp/opengl/GLRunnableTask.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.opengl; import javax.media.opengl.GLRunnable; @@ -39,7 +39,8 @@ public class GLRunnableTask implements GLRunnable { GLRunnable runnable; Object notifyObject; boolean catchExceptions; - boolean isExecuted; + volatile boolean isExecuted; + volatile boolean isFlushed; Throwable runnableException; @@ -48,8 +49,10 @@ public class GLRunnableTask implements GLRunnable { this.notifyObject = notifyObject ; this.catchExceptions = catchExceptions; isExecuted = false; + isFlushed = false; } + @Override public boolean run(GLAutoDrawable drawable) { boolean res = true; if(null == notifyObject) { @@ -85,7 +88,40 @@ public class GLRunnableTask implements GLRunnable { return res; } + /** + * Simply flush this task and notify a waiting executor. + * The executor which might have been blocked until notified + * will be unblocked and the task removed from the queue. + * + * @see #isFlushed() + * @see #isInQueue() + */ + public void flush() { + if(!isExecuted() && null != notifyObject) { + synchronized (notifyObject) { + isFlushed=true; + notifyObject.notifyAll(); + } + } + } + + /** + * @return !{@link #isExecuted()} && !{@link #isFlushed()} + */ + public boolean isInQueue() { return !isExecuted && !isFlushed; } + + /** + * @return whether this task has been executed. + * @see #isInQueue() + */ public boolean isExecuted() { return isExecuted; } + + /** + * @return whether this task has been flushed. + * @see #isInQueue() + */ + public boolean isFlushed() { return isFlushed; } + public Throwable getThrowable() { return runnableException; } } diff --git a/src/jogl/classes/jogamp/opengl/GLStateTracker.java b/src/jogl/classes/jogamp/opengl/GLStateTracker.java index 391f96aed..0e451741c 100644 --- a/src/jogl/classes/jogamp/opengl/GLStateTracker.java +++ b/src/jogl/classes/jogamp/opengl/GLStateTracker.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -40,7 +40,9 @@ package jogamp.opengl; import javax.media.opengl.*; + import com.jogamp.common.util.IntIntHashMap; + import java.nio.IntBuffer; import java.util.ArrayList; @@ -51,68 +53,67 @@ import java.util.ArrayList; * Currently supported states: PixelStorei */ public class GLStateTracker { - - /** Minimum value of MAX_CLIENT_ATTRIB_STACK_DEPTH */ + + /** Minimum value of MAX_CLIENT_ATTRIB_STACK_DEPTH */ public static final int MIN_CLIENT_ATTRIB_STACK_DEPTH = 16; - + /** static size of pixel state map */ static final int PIXEL_STATE_MAP_SIZE = 16; /** avoid rehash of static size pixel state map */ static final int PIXEL_STATE_MAP_CAPACITY = 32; - + private volatile boolean enabled = true; private IntIntHashMap pixelStateMap; private final ArrayList<SavedState> stack; - + private static class SavedState { /** * Empty pixel-store state - */ + */ private IntIntHashMap pixelStateMap; - + /** * set (client) pixel-store state, deep copy - */ + */ private final void setPixelStateMap(IntIntHashMap pixelStateMap) { this.pixelStateMap = (IntIntHashMap) pixelStateMap.clone(); } - + /** * get (client) pixel-store state, return reference - */ + */ private final IntIntHashMap getPixelStateMap() { return pixelStateMap; } } - - public GLStateTracker() { + + public GLStateTracker() { pixelStateMap = new IntIntHashMap(PIXEL_STATE_MAP_CAPACITY, 0.75f); pixelStateMap.setKeyNotFoundValue(0xFFFFFFFF); resetStates(); - + stack = new ArrayList<SavedState>(MIN_CLIENT_ATTRIB_STACK_DEPTH); } - public final void clearStates(boolean enable) { - enabled = enable; + public final void clearStates() { pixelStateMap.clear(); } public final void setEnabled(boolean on) { - enabled = on; + enabled = on; } public final boolean isEnabled() { return enabled; } - /** @return true if found in our map, otherwise false, + /** @return true if found in our map, otherwise false, * which forces the caller to query GL. */ public final boolean getInt(int pname, int[] params, int params_offset) { if(enabled) { - int value = pixelStateMap.get(pname); + final int value = pixelStateMap.get(pname); if(0xFFFFFFFF != value) { params[params_offset] = value; return true; @@ -121,11 +122,11 @@ public class GLStateTracker { return false; } - /** @return true if found in our map, otherwise false, + /** @return true if found in our map, otherwise false, * which forces the caller to query GL. */ public final boolean getInt(int pname, IntBuffer params, int dummy) { if(enabled) { - int value = pixelStateMap.get(pname); + final int value = pixelStateMap.get(pname); if(0xFFFFFFFF != value) { params.put(params.position(), value); return true; @@ -157,7 +158,7 @@ public class GLStateTracker { throw new GLException("stack contains no elements"); } SavedState state = stack.remove(stack.size()-1); // pop - + if(null==state) { throw new GLException("null stack element (remaining stack size "+stack.size()+")"); } @@ -165,7 +166,7 @@ public class GLStateTracker { if ( null != state.getPixelStateMap() ) { // use pulled client pixel-store state from stack pixelStateMap = state.getPixelStateMap(); - } // else: empty-slot, not pushed by GL_CLIENT_PIXEL_STORE_BIT + } // else: empty-slot, not pushed by GL_CLIENT_PIXEL_STORE_BIT } } @@ -176,20 +177,20 @@ public class GLStateTracker { pixelStateMap.put(GL.GL_PACK_ALIGNMENT, 4); pixelStateMap.put(GL2GL3.GL_PACK_SWAP_BYTES, GL.GL_FALSE); pixelStateMap.put(GL2GL3.GL_PACK_LSB_FIRST, GL.GL_FALSE); - pixelStateMap.put(GL2GL3.GL_PACK_ROW_LENGTH, 0); - pixelStateMap.put(GL2GL3.GL_PACK_SKIP_ROWS, 0); - pixelStateMap.put(GL2GL3.GL_PACK_SKIP_PIXELS, 0); + pixelStateMap.put(GL2ES3.GL_PACK_ROW_LENGTH, 0); + pixelStateMap.put(GL2ES3.GL_PACK_SKIP_ROWS, 0); + pixelStateMap.put(GL2ES3.GL_PACK_SKIP_PIXELS, 0); pixelStateMap.put(GL2GL3.GL_PACK_IMAGE_HEIGHT, 0); pixelStateMap.put(GL2GL3.GL_PACK_SKIP_IMAGES, 0); pixelStateMap.put(GL.GL_UNPACK_ALIGNMENT, 4); pixelStateMap.put(GL2GL3.GL_UNPACK_SWAP_BYTES, GL.GL_FALSE); pixelStateMap.put(GL2GL3.GL_UNPACK_LSB_FIRST, GL.GL_FALSE); - pixelStateMap.put(GL2GL3.GL_UNPACK_ROW_LENGTH, 0); - pixelStateMap.put(GL2GL3.GL_UNPACK_SKIP_ROWS, 0); - pixelStateMap.put(GL2GL3.GL_UNPACK_SKIP_PIXELS, 0); - pixelStateMap.put(GL2GL3.GL_UNPACK_IMAGE_HEIGHT, 0); - pixelStateMap.put(GL2GL3.GL_UNPACK_SKIP_IMAGES, 0); + pixelStateMap.put(GL2ES2.GL_UNPACK_ROW_LENGTH, 0); + pixelStateMap.put(GL2ES2.GL_UNPACK_SKIP_ROWS, 0); + pixelStateMap.put(GL2ES2.GL_UNPACK_SKIP_PIXELS, 0); + pixelStateMap.put(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); + pixelStateMap.put(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); } } diff --git a/src/jogl/classes/jogamp/opengl/GLVersionNumber.java b/src/jogl/classes/jogamp/opengl/GLVersionNumber.java index 5bd008f83..431c1a387 100644 --- a/src/jogl/classes/jogamp/opengl/GLVersionNumber.java +++ b/src/jogl/classes/jogamp/opengl/GLVersionNumber.java @@ -28,96 +28,103 @@ package jogamp.opengl; -import java.util.StringTokenizer; import com.jogamp.common.util.VersionNumber; +import com.jogamp.common.util.VersionNumberString; /** * A class for storing and comparing OpenGL version numbers. * This only works for desktop OpenGL at the moment. */ -class GLVersionNumber extends VersionNumber { +public class GLVersionNumber extends VersionNumberString { - protected boolean valid; + private final boolean valid; - public GLVersionNumber(int majorRev, int minorRev, int subMinorRev) { - super(majorRev, minorRev, subMinorRev); - valid = true; + private GLVersionNumber(int[] val, int strEnd, short state, String versionString, boolean valid) { + super(val[0], val[1], val[2], strEnd, state, versionString); + this.valid = valid; } - public GLVersionNumber(String versionString) { - super(); - valid = false; - try { - if (versionString.startsWith("GL_VERSION_")) { - StringTokenizer tok = new StringTokenizer(versionString, "_"); - tok.nextToken(); // GL_ - tok.nextToken(); // VERSION_ - if (!tok.hasMoreTokens()) { - major = 0; - return; + private static java.util.regex.Pattern getUnderscorePattern() { + if( null == _Pattern ) { // volatile dbl-checked-locking OK + synchronized( VersionNumber.class ) { + if( null == _Pattern ) { + _Pattern = getVersionNumberPattern("_"); } - major = Integer.valueOf(tok.nextToken()).intValue(); - if (!tok.hasMoreTokens()) { - minor = 0; - return; - } - minor = Integer.valueOf(tok.nextToken()).intValue(); - if (!tok.hasMoreTokens()) { - sub = 0; - return; - } - sub = Integer.valueOf(tok.nextToken()).intValue(); - } else { - int radix = 10; - if (versionString.length() > 2) { - if (Character.isDigit(versionString.charAt(0)) && versionString.charAt(1) == '.' && Character.isDigit(versionString.charAt(2))) { - major = Character.digit(versionString.charAt(0), radix); - minor = Character.digit(versionString.charAt(2), radix); - // See if there's version-specific information which might - // imply a more recent OpenGL version - StringTokenizer tok = new StringTokenizer(versionString, " "); - if (tok.hasMoreTokens()) { - tok.nextToken(); - if (tok.hasMoreTokens()) { - String token = tok.nextToken(); - int i = 0; - while (i < token.length() && !Character.isDigit(token.charAt(i))) { - i++; - } - if (i < token.length() - 2 && Character.isDigit(token.charAt(i)) && token.charAt(i + 1) == '.' && Character.isDigit(token.charAt(i + 2))) { - int altMajor = Character.digit(token.charAt(i), radix); - int altMinor = Character.digit(token.charAt(i + 2), radix); - // Avoid possibly confusing situations by putting some - // constraints on the upgrades we do to the major and - // minor versions - if ((altMajor == major && altMinor > minor) || altMajor == major + 1) { - major = altMajor; - minor = altMinor; - } - } - } - } - } + } + } + return _Pattern; + } + private static volatile java.util.regex.Pattern _Pattern = null; + + public static final GLVersionNumber create(String versionString) { + int[] val = new int[] { 0, 0, 0 }; + int strEnd = 0; + short state = 0; + boolean valid = false; + if (versionString != null && versionString.length() > 0) { + try { + final java.util.regex.Pattern versionPattern; + if (versionString.startsWith("GL_VERSION_")) { + versionPattern = getUnderscorePattern(); + } else { + versionPattern = VersionNumberString.getDefaultVersionNumberPattern(); } + final VersionNumberString version = new VersionNumberString(versionString, versionPattern); + strEnd = version.endOfStringMatch(); + val[0] = version.getMajor(); + val[1] = version.getMinor(); + state = (short) ( ( version.hasMajor() ? VersionNumber.HAS_MAJOR : (short)0 ) | + ( version.hasMinor() ? VersionNumber.HAS_MINOR : (short)0 ) ); + valid = version.hasMajor() && version.hasMinor(); // Requires at least a defined major and minor version component! + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Info: ExtensionAvailabilityCache: FunctionAvailabilityCache.Version.<init>: " + e); + val[0] = 1; + val[1] = 0; } - valid = true; - } catch (Exception e) { - e.printStackTrace(); - // FIXME: refactor desktop OpenGL dependencies and make this - // class work properly for OpenGL ES - System.err.println("Info: ExtensionAvailabilityCache: FunctionAvailabilityCache.Version.<init>: " + e); - major = 1; - minor = 0; - /* - throw (IllegalArgumentException) - new IllegalArgumentException( - "Illegally formatted version identifier: \"" + versionString + "\"") - .initCause(e); - */ } + return new GLVersionNumber(val, strEnd, state, versionString, valid); } public final boolean isValid() { return valid; } + + /** + * Returns the optional vendor version at the end of the + * <code>GL_VERSION</code> string if exists, otherwise the {@link VersionNumberString#zeroVersion zero version} instance. + * <pre> + * 2.1 Mesa 7.0.3-rc2 -> 7.0.3 (7.0.3-rc2) + * 2.1 Mesa 7.12-devel (git-d6c318e) -> 7.12.0 (7.12-devel) + * 4.2.12171 Compatibility Profile Context 9.01.8 -> 9.1.8 (9.01.8) + * 4.2.12198 Compatibility Profile Context 12.102.3.0 -> 12.102.3 (12.102.3.0) + * 4.3.0 NVIDIA 310.32 -> 310.32 (310.32) + * </pre> + */ + public static final VersionNumberString createVendorVersion(String versionString) { + if (versionString == null || versionString.length() <= 0) { + return null; + } + + // Skip the 1st GL version + String str; + { + final GLVersionNumber glv = create(versionString); + str = versionString.substring(glv.endOfStringMatch()).trim(); + } + + while ( str.length() > 0 ) { + final VersionNumberString version = new VersionNumberString(str, getDefaultVersionNumberPattern()); + final int eosm = version.endOfStringMatch(); + if( 0 < eosm ) { + if( version.hasMajor() && version.hasMinor() ) { // Requires at least a defined major and minor version component! + return version; + } + str = str.substring( eosm ).trim(); + } else { + break; // no match + } + } + return VersionNumberString.zeroVersion; + } } diff --git a/src/jogl/classes/jogamp/opengl/GLWorkerThread.java b/src/jogl/classes/jogamp/opengl/GLWorkerThread.java index f7d59e127..100b46a5e 100644 --- a/src/jogl/classes/jogamp/opengl/GLWorkerThread.java +++ b/src/jogl/classes/jogamp/opengl/GLWorkerThread.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -67,7 +67,7 @@ public class GLWorkerThread { private static volatile Runnable work; // Queue of Runnables to be asynchronously invoked private static List<Runnable> queue = new ArrayList<Runnable>(); - + /** Should only be called by Threading class if creation of the GLWorkerThread was requested via the opengl.1thread system property. <br> @@ -149,7 +149,7 @@ public class GLWorkerThread { } } - public static void invoke(boolean wait, Runnable runnable) + public static void invoke(boolean wait, Runnable runnable) throws InvocationTargetException, InterruptedException { if(wait) { invokeAndWait(runnable); @@ -157,7 +157,7 @@ public class GLWorkerThread { invokeLater(runnable); } } - + public static void invokeAndWait(Runnable runnable) throws InvocationTargetException, InterruptedException { if (!started) { @@ -219,11 +219,10 @@ public class GLWorkerThread { return (Thread.currentThread() == thread); } - protected static String getThreadName() { - return Thread.currentThread().getName(); - } - + protected static String getThreadName() { return Thread.currentThread().getName(); } + static class WorkerRunnable implements Runnable { + @Override public void run() { // Notify starting thread that we're ready synchronized (lock) { @@ -246,7 +245,7 @@ public class GLWorkerThread { break; } } - + if (shouldTerminate) { lock.notifyAll(); thread = null; diff --git a/src/jogl/classes/jogamp/opengl/GLXExtensions.java b/src/jogl/classes/jogamp/opengl/GLXExtensions.java new file mode 100644 index 000000000..9325c6f68 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/GLXExtensions.java @@ -0,0 +1,37 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl; + +/** + * Class holding GLX/WGL/.. extension strings, commonly used by JOGL's implementation. + */ +public class GLXExtensions { + public static final String GLX_MESA_swap_control = "GLX_MESA_swap_control"; + public static final String GLX_SGI_swap_control = "GLX_SGI_swap_control"; + public static final String GLX_NV_swap_group = "GLX_NV_swap_group"; +} diff --git a/src/jogl/classes/jogamp/opengl/ListenerSyncedImplStub.java b/src/jogl/classes/jogamp/opengl/ListenerSyncedImplStub.java index 1cde551be..a64a2f5cd 100644 --- a/src/jogl/classes/jogamp/opengl/ListenerSyncedImplStub.java +++ b/src/jogl/classes/jogamp/opengl/ListenerSyncedImplStub.java @@ -33,9 +33,9 @@ import java.util.ArrayList; /** * Simple locked listener implementation stub to be used for listener handler, * synchronized on it's instance. - * + * * <p>Utilizing simple locking via synchronized.</p> - * + * * @param <E> The listener type */ public class ListenerSyncedImplStub<E> { @@ -48,7 +48,7 @@ public class ListenerSyncedImplStub<E> { public synchronized final void reset() { listeners = new ArrayList<E>(); } - + public synchronized final void destroy() { listeners.clear(); listeners = null; @@ -57,7 +57,7 @@ public class ListenerSyncedImplStub<E> { public synchronized final int size() { return listeners.size(); } - + public synchronized final void addListener(E listener) { addListener(-1, listener); } @@ -68,12 +68,12 @@ public class ListenerSyncedImplStub<E> { } listeners.add(index, listener); } - + public synchronized final void removeListener(E listener) { listeners.remove(listener); } public final ArrayList<E> getListeners() { return listeners; - } + } } diff --git a/src/jogl/classes/jogamp/opengl/MemoryObject.java b/src/jogl/classes/jogamp/opengl/MemoryObject.java index 292700701..6ebefc517 100644 --- a/src/jogl/classes/jogamp/opengl/MemoryObject.java +++ b/src/jogl/classes/jogamp/opengl/MemoryObject.java @@ -31,19 +31,22 @@ package jogamp.opengl; import java.nio.ByteBuffer; import java.util.HashMap; +import javax.media.opengl.GLBufferStorage; + +import com.jogamp.common.util.HashUtil; + /** - * + * @deprecated No more used for GL buffer storage tracking, see {@link GLBufferStorage} and {@link GLBufferObjectTracker}. */ public class MemoryObject { - private long addr; - private long size; - private int hash32; + private final long addr; + private final long size; + private final int hash; private ByteBuffer buffer=null; - public MemoryObject(long addr, long size) { this.addr = addr; this.size = size; - this.hash32 = getHash32(addr, size); + this.hash = HashUtil.getAddrSizeHash32_EqualDist(addr, size); } public void setBuffer(ByteBuffer buffer) { @@ -55,68 +58,46 @@ public class MemoryObject { } /** - * @return the 32bit hash value generated via {@link #getHash32(long, long)} + * @return the 32bit hash value generated via {@link HashUtil#getAddrSizeHash32_EqualDist(long, long)}. */ + @Override public int hashCode() { - return hash32; + return hash; + } + + @Override + public String toString() { + return "MemoryObject[addr 0x"+Long.toHexString(addr)+", size 0x"+Long.toHexString(size)+", hash32: 0x"+Integer.toHexString(hash)+"]"; } /** * Ignores the optional attached <code>ByteBuffer</code> intentionally.<br> - * + * * @return true of reference is equal or <code>obj</code> is of type <code>MemoryObject</code> * and <code>addr</code> and <code>size</code> is equal.<br> */ public boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof MemoryObject) { - MemoryObject m = (MemoryObject) obj; + final MemoryObject m = (MemoryObject) obj; return addr == m.addr && size == m.size ; } return false; } /** - * Generates a 32bit hash value by <code>addr</code> and <code>size</code>.<br> - * Ignores the optional attached <code>ByteBuffer</code> intentionally.<br> - */ - public static int getHash32(long addr, long size) { - // avoid xor collisions of eg low/high parts - // 31 * x == (x << 5) - x - int hash = 31 + (int) addr ; // lo addr - hash = ((hash << 5) - hash) + (int) ( addr >>> 32 ) ; // hi addr - hash = ((hash << 5) - hash) + (int) size ; // lo size - hash = ((hash << 5) - hash) + (int) ( size >>> 32 ) ; // hi size - - return hash; - } - - /** - * Generates a 64bit hash value by <code>addr</code> and <code>size</code>.<br> - * Ignores the optional attached <code>ByteBuffer</code> intentionally.<br> - */ - public static long getHash64(long addr, long size) { - // 31 * x == (x << 5) - x - final long hash = 31 + addr; - return ((hash << 5) - hash) + size; - } - - public String toString() { - return "MemoryObject[addr 0x"+Long.toHexString(addr)+", size 0x"+Long.toHexString(size)+", hash32: 0x"+Integer.toHexString(hash32)+"]"; - } - - /** * @param map the identity HashMap, MemoryObject to MemoryObject * @param obj0 the MemoryObject * @return either the already mapped MemoryObject - not changing the map, or the newly mapped one. */ public static MemoryObject getOrAddSafe(HashMap<MemoryObject,MemoryObject> map, MemoryObject obj0) { - MemoryObject obj1 = map.get(obj0); // get identity (fast) + final MemoryObject obj1 = map.get(obj0); // get identity (fast) if(null == obj1) { map.put(obj0, obj0); - obj1 = obj0; + return obj0; + } else { + return obj1; } - return obj1; } }
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/opengl/ProjectFloat.java b/src/jogl/classes/jogamp/opengl/ProjectFloat.java index ce8405f74..d4fd1c935 100644 --- a/src/jogl/classes/jogamp/opengl/ProjectFloat.java +++ b/src/jogl/classes/jogamp/opengl/ProjectFloat.java @@ -6,9 +6,9 @@ ** this file except in compliance with the License. You may obtain a copy ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: -** +** ** http://oss.sgi.com/projects/FreeB -** +** ** Note that, as provided in the License, the Software is distributed on an ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND @@ -24,13 +24,13 @@ ** except that Section 2.2 and 11 are omitted. Any differences between ** the Alternative License and the SGI License are offered solely by Sun ** and not by SGI. -** +** ** Original Code. The Original Code is: OpenGL Sample Implementation, ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. ** Copyright in any portions created by third parties is as indicated ** elsewhere herein. All Rights Reserved. -** +** ** Additional Notice Provisions: The application programming interfaces ** established by SGI in conjunction with the Original Code are The ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -45,34 +45,34 @@ ** $Header$ */ -/* +/* * Copyright (c) 2002-2004 LWJGL Project * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are + * modification, are permitted provided that the following conditions are * met: - * - * * Redistributions of source code must retain the above copyright + * + * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * * Neither the name of 'LWJGL' nor the names of - * its contributors may be used to endorse or promote products derived + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -80,22 +80,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2011 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -108,7 +108,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -122,21 +122,21 @@ import java.nio.IntBuffer; import javax.media.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.common.nio.Buffers; -import com.jogamp.opengl.FloatUtil; +import com.jogamp.opengl.math.FloatUtil; /** * ProjectFloat.java * <p> * Created 11-jan-2004 * </p> - * + * * @author Erik Duijs * @author Kenneth Russell * @author Sven Gothel */ public class ProjectFloat { - public static final int getRequiredFloatBufferSize() { return 2*16+2*4+3*3; } - + public static final int getRequiredFloatBufferSize() { return 1*16; } + // Note that we have cloned parts of the implementation in order to // support incoming Buffers. The reason for this is to avoid loading // non-direct buffer subclasses unnecessarily, because doing so can @@ -153,62 +153,36 @@ public class ProjectFloat { private final float[] out = new float[4]; // Buffer-based implementation - private FloatBuffer matrixBuf; - private FloatBuffer tempInvertMatrixBuf; - - private FloatBuffer inBuf; - private FloatBuffer outBuf; + private FloatBuffer matrixBuf; // 4x4 - private FloatBuffer forwardBuf; - private FloatBuffer sideBuf; - private FloatBuffer upBuf; + private final float[] forward = new float[3]; // 3 + private final float[] side = new float[3]; // 3 + private final float[] up = new float[3]; // 3 public ProjectFloat() { - this(false); + this(true); } - + public ProjectFloat(boolean useBackingArray) { - this(useBackingArray ? null : Buffers.newDirectByteBuffer(getRequiredFloatBufferSize() * Buffers.SIZEOF_FLOAT), - useBackingArray ? new float[getRequiredFloatBufferSize()] : null, + this(useBackingArray ? null : Buffers.newDirectByteBuffer(getRequiredFloatBufferSize() * Buffers.SIZEOF_FLOAT), + useBackingArray ? new float[getRequiredFloatBufferSize()] : null, 0); } /** * @param floatBuffer source buffer, may be ByteBuffer (recommended) or FloatBuffer or <code>null</code>. - * If used, shall be ≥ {@link #getRequiredFloatBufferSize()} + floatOffset. + * If used, shall be ≥ {@link #getRequiredFloatBufferSize()} + floatOffset. * Buffer's position is ignored and floatPos is being used. * @param floatArray source float array or <code>null</code>. * If used, size shall be ≥ {@link #getRequiredFloatBufferSize()} + floatOffset. * @param floatOffset Offset for either of the given sources (buffer or array) */ - public ProjectFloat(Buffer floatBuffer, float[] floatArray, int floatOffset) { - int floatPos = floatOffset; - int floatSize = 16; - matrixBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); - floatPos += floatSize; - tempInvertMatrixBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); - floatPos += floatSize; - floatSize = 4; - inBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); - floatPos += floatSize; - outBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); - floatPos += floatSize; - floatSize = 3; - forwardBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); - floatPos += floatSize; - sideBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); - floatPos += floatSize; - upBuf = Buffers.slice2Float(floatBuffer, floatArray, floatPos, floatSize); + public ProjectFloat(Buffer floatBuffer, float[] floatArray, int floatOffset) { + matrixBuf = Buffers.slice2Float(floatBuffer, floatArray, floatOffset, 16); } public void destroy() { matrixBuf = null; - tempInvertMatrixBuf = null; - inBuf = null; - outBuf = null; - forwardBuf = null; - sideBuf = null; - upBuf = null; } /** @@ -221,7 +195,7 @@ public class ProjectFloat { public boolean gluInvertMatrixf(float[] src, int srcOffset, float[] inverse, int inverseOffset) { int i, j, k, swap; float t; - float[][] temp = tempInvertMatrix; + final float[][] temp = tempInvertMatrix; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { @@ -285,21 +259,21 @@ public class ProjectFloat { /** * @param src * @param inverse - * + * * @return */ public boolean gluInvertMatrixf(FloatBuffer src, FloatBuffer inverse) { int i, j, k, swap; float t; - int srcPos = src.position(); - int invPos = inverse.position(); + final int srcPos = src.position(); + final int invPos = inverse.position(); - FloatBuffer temp = tempInvertMatrixBuf; + final float[][] temp = tempInvertMatrix; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { - temp.put(i*4+j, src.get(i*4+j + srcPos)); + temp[i][j] = src.get(i*4+j + srcPos); } } FloatUtil.makeIdentityf(inverse); @@ -310,7 +284,7 @@ public class ProjectFloat { // swap = i; for (j = i + 1; j < 4; j++) { - if (Math.abs(temp.get(j*4+i)) > Math.abs(temp.get(i*4+i))) { + if (Math.abs(temp[j][i]) > Math.abs(temp[i][i])) { swap = j; } } @@ -320,9 +294,9 @@ public class ProjectFloat { // Swap rows. // for (k = 0; k < 4; k++) { - t = temp.get(i*4+k); - temp.put(i*4+k, temp.get(swap*4+k)); - temp.put(swap*4+k, t); + t = temp[i][k]; + temp[i][k] = temp[swap][k]; + temp[swap][k] = t; t = inverse.get(i*4+k + invPos); inverse.put(i*4+k + invPos, inverse.get(swap*4+k + invPos)); @@ -330,7 +304,7 @@ public class ProjectFloat { } } - if (temp.get(i*4+i) == 0) { + if (temp[i][i] == 0) { // // No non-zero pivot. The matrix is singular, which shouldn't // happen. This means the user gave us a bad matrix. @@ -338,17 +312,19 @@ public class ProjectFloat { return false; } - t = temp.get(i*4+i); + t = temp[i][i]; for (k = 0; k < 4; k++) { - temp.put(i*4+k, temp.get(i*4+k) / t); - inverse.put(i*4+k + invPos, inverse.get(i*4+k + invPos) / t); + temp[i][k] /= t; + final int z = i*4+k + invPos; + inverse.put(z, inverse.get(z) / t); } for (j = 0; j < 4; j++) { if (j != i) { - t = temp.get(j*4+i); + t = temp[j][i]; for (k = 0; k < 4; k++) { - temp.put(j*4+k, temp.get(j*4+k) - temp.get(i*4+k) * t); - inverse.put(j*4+k + invPos, inverse.get(j*4+k + invPos) - inverse.get(i*4+k + invPos) * t); + temp[j][k] -= temp[i][k] * t; + final int z = j*4+k + invPos; + inverse.put(z, inverse.get(z) - inverse.get(i*4+k + invPos) * t); } } } @@ -359,7 +335,7 @@ public class ProjectFloat { /** * Method gluOrtho2D. - * + * * @param left * @param right * @param bottom @@ -371,15 +347,15 @@ public class ProjectFloat { /** * Method gluPerspective. - * + * * @param fovy * @param aspect * @param zNear * @param zFar */ public void gluPerspective(GLMatrixFunc gl, float fovy, float aspect, float zNear, float zFar) { + final float radians = fovy / 2 * (float) Math.PI / 180; float sine, cotangent, deltaZ; - float radians = fovy / 2 * (float) Math.PI / 180; deltaZ = zFar - zNear; sine = (float) Math.sin(radians); @@ -391,20 +367,20 @@ public class ProjectFloat { cotangent = (float) Math.cos(radians) / sine; FloatUtil.makeIdentityf(matrixBuf); - - matrixBuf.put(0 * 4 + 0, cotangent / aspect); - matrixBuf.put(1 * 4 + 1, cotangent); - matrixBuf.put(2 * 4 + 2, - (zFar + zNear) / deltaZ); - matrixBuf.put(2 * 4 + 3, -1); - matrixBuf.put(3 * 4 + 2, -2 * zNear * zFar / deltaZ); - matrixBuf.put(3 * 4 + 3, 0); + final int mPos = matrixBuf.position(); + matrixBuf.put(0 * 4 + 0 + mPos, cotangent / aspect); + matrixBuf.put(1 * 4 + 1 + mPos, cotangent); + matrixBuf.put(2 * 4 + 2 + mPos, - (zFar + zNear) / deltaZ); + matrixBuf.put(2 * 4 + 3 + mPos, -1); + matrixBuf.put(3 * 4 + 2 + mPos, -2 * zNear * zFar / deltaZ); + matrixBuf.put(3 * 4 + 3 + mPos, 0); gl.glMultMatrixf(matrixBuf); } /** * Method gluLookAt - * + * * @param eyex * @param eyey * @param eyez @@ -419,17 +395,17 @@ public class ProjectFloat { float eyex, float eyey, float eyez, float centerx, float centery, float centerz, float upx, float upy, float upz) { - FloatBuffer forward = this.forwardBuf; - FloatBuffer side = this.sideBuf; - FloatBuffer up = this.upBuf; + final float[] forward = this.forward; + final float[] side = this.side; + final float[] up = this.up; - forward.put(0, centerx - eyex); - forward.put(1, centery - eyey); - forward.put(2, centerz - eyez); + forward[0] = centerx - eyex; + forward[1] = centery - eyey; + forward[2] = centerz - eyez; - up.put(0, upx); - up.put(1, upy); - up.put(2, upz); + up[0] = upx; + up[1] = upy; + up[2] = upz; FloatUtil.normalize(forward); @@ -441,25 +417,26 @@ public class ProjectFloat { FloatUtil.cross(side, forward, up); FloatUtil.makeIdentityf(matrixBuf); - matrixBuf.put(0 * 4 + 0, side.get(0)); - matrixBuf.put(1 * 4 + 0, side.get(1)); - matrixBuf.put(2 * 4 + 0, side.get(2)); + final int mPos = matrixBuf.position(); + matrixBuf.put(0 * 4 + 0 + mPos, side[0]); + matrixBuf.put(1 * 4 + 0 + mPos, side[1]); + matrixBuf.put(2 * 4 + 0 + mPos, side[2]); - matrixBuf.put(0 * 4 + 1, up.get(0)); - matrixBuf.put(1 * 4 + 1, up.get(1)); - matrixBuf.put(2 * 4 + 1, up.get(2)); + matrixBuf.put(0 * 4 + 1 + mPos, up[0]); + matrixBuf.put(1 * 4 + 1 + mPos, up[1]); + matrixBuf.put(2 * 4 + 1 + mPos, up[2]); - matrixBuf.put(0 * 4 + 2, -forward.get(0)); - matrixBuf.put(1 * 4 + 2, -forward.get(1)); - matrixBuf.put(2 * 4 + 2, -forward.get(2)); + matrixBuf.put(0 * 4 + 2 + mPos, -forward[0]); + matrixBuf.put(1 * 4 + 2 + mPos, -forward[1]); + matrixBuf.put(2 * 4 + 2 + mPos, -forward[2]); gl.glMultMatrixf(matrixBuf); gl.glTranslatef(-eyex, -eyey, -eyez); } /** - * Method gluProject - * + * Map object coordinates to window coordinates. + * * @param objx * @param objy * @param objz @@ -467,7 +444,7 @@ public class ProjectFloat { * @param projMatrix * @param viewport * @param win_pos - * + * * @return */ public boolean gluProject(float objx, float objy, float objz, @@ -476,16 +453,16 @@ public class ProjectFloat { int[] viewport, int viewport_offset, float[] win_pos, int win_pos_offset ) { - float[] in = this.in; - float[] out = this.out; + final float[] in = this.in; + final float[] out = this.out; in[0] = objx; in[1] = objy; in[2] = objz; in[3] = 1.0f; - FloatUtil.multMatrixVecf(modelMatrix, modelMatrix_offset, in, 0, out); - FloatUtil.multMatrixVecf(projMatrix, projMatrix_offset, out, 0, in); + FloatUtil.multMatrixVecf(modelMatrix, modelMatrix_offset, in, 0, out, 0); + FloatUtil.multMatrixVecf(projMatrix, projMatrix_offset, out, 0, in, 0); if (in[3] == 0.0f) { return false; @@ -506,45 +483,48 @@ public class ProjectFloat { return true; } + /** + * Map object coordinates to window coordinates. + */ public boolean gluProject(float objx, float objy, float objz, FloatBuffer modelMatrix, FloatBuffer projMatrix, int[] viewport, int viewport_offset, float[] win_pos, int win_pos_offset ) { - FloatBuffer in = this.inBuf; - FloatBuffer out = this.outBuf; + final float[] in = this.in; + final float[] out = this.out; - in.put(0, objx); - in.put(1, objy); - in.put(2, objz); - in.put(3, 1.0f); + in[0] = objx; + in[1] = objy; + in[2] = objz; + in[3] = 1.0f; FloatUtil.multMatrixVecf(modelMatrix, in, out); FloatUtil.multMatrixVecf(projMatrix, out, in); - if (in.get(3) == 0.0f) { + if (in[3] == 0.0f) { return false; } - in.put(3, (1.0f / in.get(3)) * 0.5f); + in[3] = (1.0f / in[3]) * 0.5f; // Map x, y and z to range 0-1 - in.put(0, in.get(0) * in.get(3) + 0.5f); - in.put(1, in.get(1) * in.get(3) + 0.5f); - in.put(2, in.get(2) * in.get(3) + 0.5f); + in[0] = in[0] * in[3] + 0.5f; + in[1] = in[1] * in[3] + 0.5f; + in[2] = in[2] * in[3] + 0.5f; // Map x,y to viewport - win_pos[0+win_pos_offset] = in.get(0) * viewport[2+viewport_offset] + viewport[0+viewport_offset]; - win_pos[1+win_pos_offset] = in.get(1) * viewport[3+viewport_offset] + viewport[1+viewport_offset]; - win_pos[2+win_pos_offset] = in.get(2); + win_pos[0+win_pos_offset] = in[0] * viewport[2+viewport_offset] + viewport[0+viewport_offset]; + win_pos[1+win_pos_offset] = in[1] * viewport[3+viewport_offset] + viewport[1+viewport_offset]; + win_pos[2+win_pos_offset] = in[2]; return true; } - + /** - * Method gluProject - * + * Map object coordinates to window coordinates. + * * @param objx * @param objy * @param objz @@ -552,7 +532,7 @@ public class ProjectFloat { * @param projMatrix * @param viewport * @param win_pos - * + * * @return */ public boolean gluProject(float objx, float objy, float objz, @@ -561,42 +541,42 @@ public class ProjectFloat { IntBuffer viewport, FloatBuffer win_pos) { - FloatBuffer in = this.inBuf; - FloatBuffer out = this.outBuf; + final float[] in = this.in; + final float[] out = this.out; - in.put(0, objx); - in.put(1, objy); - in.put(2, objz); - in.put(3, 1.0f); + in[0] = objx; + in[1] = objy; + in[2] = objz; + in[3] = 1.0f; FloatUtil.multMatrixVecf(modelMatrix, in, out); FloatUtil.multMatrixVecf(projMatrix, out, in); - if (in.get(3) == 0.0f) { + if (in[3] == 0.0f) { return false; } - in.put(3, (1.0f / in.get(3)) * 0.5f); + in[3] = (1.0f / in[3]) * 0.5f; // Map x, y and z to range 0-1 - in.put(0, in.get(0) * in.get(3) + 0.5f); - in.put(1, in.get(1) * in.get(3) + 0.5f); - in.put(2, in.get(2) * in.get(3) + 0.5f); + in[0] = in[0] * in[3] + 0.5f; + in[1] = in[1] * in[3] + 0.5f; + in[2] = in[2] * in[3] + 0.5f; // Map x,y to viewport - int vPos = viewport.position(); - int wPos = win_pos.position(); - win_pos.put(0+wPos, in.get(0) * viewport.get(2+vPos) + viewport.get(0+vPos)); - win_pos.put(1+wPos, in.get(1) * viewport.get(3+vPos) + viewport.get(1+vPos)); - win_pos.put(2+wPos, in.get(2)); + final int vPos = viewport.position(); + final int wPos = win_pos.position(); + win_pos.put(0+wPos, in[0] * viewport.get(2+vPos) + viewport.get(0+vPos)); + win_pos.put(1+wPos, in[1] * viewport.get(3+vPos) + viewport.get(1+vPos)); + win_pos.put(2+wPos, in[2]); return true; } /** - * Method gluUnproject - * + * Map window coordinates to object coordinates. + * * @param winx * @param winy * @param winz @@ -604,7 +584,7 @@ public class ProjectFloat { * @param projMatrix * @param viewport * @param obj_pos - * + * * @return */ public boolean gluUnProject(float winx, float winy, float winz, @@ -612,8 +592,8 @@ public class ProjectFloat { float[] projMatrix, int projMatrix_offset, int[] viewport, int viewport_offset, float[] obj_pos, int obj_pos_offset) { - float[] in = this.in; - float[] out = this.out; + final float[] in = this.in; + final float[] out = this.out; FloatUtil.multMatrixf(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, matrix, 0); @@ -651,13 +631,27 @@ public class ProjectFloat { } + /** + * Map window coordinates to object coordinates. + * + * @param winx + * @param winy + * @param winz + * @param modelMatrix + * @param projMatrix + * @param viewport + * @param viewport_offset + * @param obj_pos + * @param obj_pos_offset + * @return + */ public boolean gluUnProject(float winx, float winy, float winz, - FloatBuffer modelMatrix, + FloatBuffer modelMatrix, FloatBuffer projMatrix, int[] viewport, int viewport_offset, float[] obj_pos, int obj_pos_offset) { - FloatBuffer in = this.inBuf; - FloatBuffer out = this.outBuf; + final float[] in = this.in; + final float[] out = this.out; FloatUtil.multMatrixf(projMatrix, modelMatrix, matrixBuf); @@ -665,38 +659,38 @@ public class ProjectFloat { return false; } - in.put(0, winx); - in.put(1, winy); - in.put(2, winz); - in.put(3, 1.0f); + in[0] = winx; + in[1] = winy; + in[2] = winz; + in[3] = 1.0f; // Map x and y from window coordinates - in.put(0, (in.get(0) - viewport[0+viewport_offset]) / viewport[2+viewport_offset]); - in.put(1, (in.get(1) - viewport[1+viewport_offset]) / viewport[3+viewport_offset]); - + in[0] = (in[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; + in[1] = (in[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; + // Map to range -1 to 1 - in.put(0, in.get(0) * 2 - 1); - in.put(1, in.get(1) * 2 - 1); - in.put(2, in.get(2) * 2 - 1); + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; FloatUtil.multMatrixVecf(matrixBuf, in, out); - if (out.get(3) == 0.0f) { + if (out[3] == 0.0) { return false; } - out.put(3, 1.0f / out.get(3)); + out[3] = 1.0f / out[3]; - obj_pos[0+obj_pos_offset] = out.get(0) * out.get(3); - obj_pos[1+obj_pos_offset] = out.get(1) * out.get(3); - obj_pos[2+obj_pos_offset] = out.get(2) * out.get(3); + obj_pos[0+obj_pos_offset] = out[0] * out[3]; + obj_pos[1+obj_pos_offset] = out[1] * out[3]; + obj_pos[2+obj_pos_offset] = out[2] * out[3]; return true; } - + /** - * Method gluUnproject - * + * Map window coordinates to object coordinates. + * * @param winx * @param winy * @param winz @@ -704,16 +698,16 @@ public class ProjectFloat { * @param projMatrix * @param viewport * @param obj_pos - * + * * @return */ public boolean gluUnProject(float winx, float winy, float winz, - FloatBuffer modelMatrix, + FloatBuffer modelMatrix, FloatBuffer projMatrix, IntBuffer viewport, FloatBuffer obj_pos) { - FloatBuffer in = this.inBuf; - FloatBuffer out = this.outBuf; + final float[] in = this.in; + final float[] out = this.out; FloatUtil.multMatrixf(projMatrix, modelMatrix, matrixBuf); @@ -721,41 +715,41 @@ public class ProjectFloat { return false; } - in.put(0, winx); - in.put(1, winy); - in.put(2, winz); - in.put(3, 1.0f); + in[0] = winx; + in[1] = winy; + in[2] = winz; + in[3] = 1.0f; // Map x and y from window coordinates - int vPos = viewport.position(); - int oPos = obj_pos.position(); - in.put(0, (in.get(0) - viewport.get(0+vPos)) / viewport.get(2+vPos)); - in.put(1, (in.get(1) - viewport.get(1+vPos)) / viewport.get(3+vPos)); + final int vPos = viewport.position(); + final int oPos = obj_pos.position(); + in[0] = (in[0] - viewport.get(0+vPos)) / viewport.get(2+vPos); + in[1] = (in[1] - viewport.get(1+vPos)) / viewport.get(3+vPos); // Map to range -1 to 1 - in.put(0, in.get(0) * 2 - 1); - in.put(1, in.get(1) * 2 - 1); - in.put(2, in.get(2) * 2 - 1); + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; FloatUtil.multMatrixVecf(matrixBuf, in, out); - if (out.get(3) == 0.0f) { + if (out[3] == 0.0) { return false; } - out.put(3, 1.0f / out.get(3)); + out[3] = 1.0f / out[3]; - obj_pos.put(0+oPos, out.get(0) * out.get(3)); - obj_pos.put(1+oPos, out.get(1) * out.get(3)); - obj_pos.put(2+oPos, out.get(2) * out.get(3)); + obj_pos.put(0+oPos, out[0] * out[3]); + obj_pos.put(1+oPos, out[1] * out[3]); + obj_pos.put(2+oPos, out[2] * out[3]); return true; } /** - * Method gluUnproject4 - * + * Map window coordinates to object coordinates. + * * @param winx * @param winy * @param winz @@ -766,7 +760,7 @@ public class ProjectFloat { * @param near * @param far * @param obj_pos - * + * * @return */ public boolean gluUnProject4(float winx, @@ -783,8 +777,8 @@ public class ProjectFloat { float far, float[] obj_pos, int obj_pos_offset ) { - float[] in = this.in; - float[] out = this.out; + final float[] in = this.in; + final float[] out = this.out; FloatUtil.multMatrixf(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, matrix, 0); @@ -808,8 +802,9 @@ public class ProjectFloat { FloatUtil.multMatrixVecf(matrix, in, out); - if (out[3] == 0.0f) + if (out[3] == 0.0f) { return false; + } obj_pos[0+obj_pos_offset] = out[0]; obj_pos[1+obj_pos_offset] = out[1]; @@ -819,8 +814,8 @@ public class ProjectFloat { } /** - * Method gluUnproject4 - * + * Map window coordinates to object coordinates. + * * @param winx * @param winy * @param winz @@ -831,7 +826,7 @@ public class ProjectFloat { * @param near * @param far * @param obj_pos - * + * * @return */ public boolean gluUnProject4(float winx, @@ -844,47 +839,48 @@ public class ProjectFloat { float near, float far, FloatBuffer obj_pos) { - FloatBuffer in = this.inBuf; - FloatBuffer out = this.outBuf; + final float[] in = this.in; + final float[] out = this.out; FloatUtil.multMatrixf(projMatrix, modelMatrix, matrixBuf); if (!gluInvertMatrixf(matrixBuf, matrixBuf)) return false; - in.put(0, winx); - in.put(1, winy); - in.put(2, winz); - in.put(3, clipw); + in[0] = winx; + in[1] = winy; + in[2] = winz; + in[3] = clipw; // Map x and y from window coordinates - int vPos = viewport.position(); - in.put(0, (in.get(0) - viewport.get(0+vPos)) / viewport.get(2+vPos)); - in.put(1, (in.get(1) - viewport.get(1+vPos)) / viewport.get(3+vPos)); - in.put(2, (in.get(2) - near) / (far - near)); + final int vPos = viewport.position(); + in[0] = (in[0] - viewport.get(0+vPos)) / viewport.get(2+vPos); + in[1] = (in[1] - viewport.get(1+vPos)) / viewport.get(3+vPos); + in[2] = (in[2] - near) / (far - near); // Map to range -1 to 1 - in.put(0, in.get(0) * 2 - 1); - in.put(1, in.get(1) * 2 - 1); - in.put(2, in.get(2) * 2 - 1); + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; FloatUtil.multMatrixVecf(matrixBuf, in, out); - if (out.get(3) == 0.0f) + if (out[3] == 0.0f) { return false; + } - int oPos = obj_pos.position(); - obj_pos.put(0+oPos, out.get(0)); - obj_pos.put(1+oPos, out.get(1)); - obj_pos.put(2+oPos, out.get(2)); - obj_pos.put(3+oPos, out.get(3)); + final int oPos = obj_pos.position(); + obj_pos.put(0+oPos, out[0]); + obj_pos.put(1+oPos, out[1]); + obj_pos.put(2+oPos, out[2]); + obj_pos.put(3+oPos, out[3]); return true; } /** * Method gluPickMatrix - * + * * @param x * @param y * @param deltaX @@ -902,7 +898,7 @@ public class ProjectFloat { } /* Translate and scale the picked region to the entire window */ - int vPos = viewport.position(); + final int vPos = viewport.position(); gl.glTranslatef((viewport.get(2+vPos) - 2 * (x - viewport.get(0+vPos))) / deltaX, (viewport.get(3+vPos) - 2 * (y - viewport.get(1+vPos))) / deltaY, 0); @@ -911,7 +907,7 @@ public class ProjectFloat { /** * Method gluPickMatrix - * + * * @param x * @param y * @param deltaX diff --git a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java index 0528d3060..283ecdb9d 100644 --- a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java +++ b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java @@ -33,27 +33,49 @@ import java.util.Iterator; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; +import com.jogamp.opengl.GLRendererQuirks; + public class SharedResourceRunner implements Runnable { protected static final boolean DEBUG = GLDrawableImpl.DEBUG; public static interface Resource { + boolean isValid(); AbstractGraphicsDevice getDevice(); AbstractGraphicsScreen getScreen(); GLDrawableImpl getDrawable(); GLContextImpl getContext(); + GLRendererQuirks getRendererQuirks(); } public static interface Implementation { /** - * @param connection for creation a {@link AbstractGraphicsDevice} instance. - * @return A new shared resource instance + * <p> + * Called within synchronized block. + * </p> + * @param connection for creation a {@link AbstractGraphicsDevice} instance. + * @return <code>true</code> if the device supports all protocols required for the implementation, otherwise <code>false</code>. + */ + boolean isDeviceSupported(String connection); + + /** + * <p> + * Called within synchronized block. + * </p> + * @param connection for creation a {@link AbstractGraphicsDevice} instance. + * @return A new shared resource instance */ Resource createSharedResource(String connection); + + /** Called within synchronized block. */ void releaseSharedResource(Resource shared); + /** Called within synchronized block. */ void clear(); + /** Called within synchronized block. */ Resource mapPut(String connection, Resource resource); + /** Called within synchronized block. */ Resource mapGet(String connection); + /** Called within synchronized block. */ Collection<Resource> mapValues(); } @@ -61,106 +83,115 @@ public class SharedResourceRunner implements Runnable { final Implementation impl; Thread thread; + boolean running; boolean ready; - boolean released; boolean shouldRelease; String initConnection; String releaseConnection; - private boolean getDeviceTried(String connection) { - synchronized (devicesTried) { - return devicesTried.contains(connection); - } + private boolean getDeviceTried(String connection) { // synchronized call + return devicesTried.contains(connection); } - private void addDeviceTried(String connection) { - synchronized (devicesTried) { - devicesTried.add(connection); - } + private void addDeviceTried(String connection) { // synchronized call + devicesTried.add(connection); } - private void removeDeviceTried(String connection) { - synchronized (devicesTried) { - devicesTried.remove(connection); - } + private void removeDeviceTried(String connection) { // synchronized call + devicesTried.remove(connection); } public SharedResourceRunner(Implementation impl) { this.impl = impl; resetState(); } - - private void resetState() { + + private void resetState() { // synchronized call devicesTried.clear(); thread = null; ready = false; - released = false; + running = false; shouldRelease = false; initConnection = null; releaseConnection = null; } - /** + /** * Start the shared resource runner thread, if not running. * <p> * Validate the thread upfront and release all related resource if it was killed. * </p> - * + * * @return the shared resource runner thread. */ public Thread start() { - if(null != thread && !thread.isAlive()) { - // thread was killed unrecognized .. - if (DEBUG) { - System.err.println("SharedResourceRunner.start() - dead-old-thread cleanup - "+Thread.currentThread().getName()); + synchronized (this) { + if(null != thread && !thread.isAlive()) { + // thread was killed unrecognized .. + if (DEBUG) { + System.err.println("SharedResourceRunner.start() - dead-old-thread cleanup - "+getThreadName()); + } + releaseSharedResources(); + thread = null; + running = false; } - releaseSharedResources(); - thread = null; - } - if(null == thread) { - if (DEBUG) { - System.err.println("SharedResourceRunner.start() - start new Thread - "+Thread.currentThread().getName()); + if( null == thread ) { + if (DEBUG) { + System.err.println("SharedResourceRunner.start() - start new Thread - "+getThreadName()); + } + resetState(); + thread = new Thread(this, getThreadName()+"-SharedResourceRunner"); + thread.setDaemon(true); // Allow JVM to exit, even if this one is running + thread.start(); + while (!running) { + try { + this.wait(); + } catch (InterruptedException ex) { } + } } - resetState(); - thread = new Thread(this, Thread.currentThread().getName()+"-SharedResourceRunner"); - thread.setDaemon(true); // Allow JVM to exit, even if this one is running - thread.start(); } return thread; } - + public void stop() { - if(null != thread) { - if (DEBUG) { - System.err.println("SharedResourceRunner.stop() - "+Thread.currentThread().getName()); - } - synchronized (this) { - shouldRelease = true; - this.notifyAll(); - - while (!released) { - try { - this.wait(); - } catch (InterruptedException ex) { + synchronized (this) { + if(null != thread) { + if (DEBUG) { + System.err.println("SharedResourceRunner.stop() - "+getThreadName()); + } + synchronized (this) { + shouldRelease = true; + this.notifyAll(); + + while (running) { + try { + this.wait(); + } catch (InterruptedException ex) { } } } } } } - + public SharedResourceRunner.Resource getOrCreateShared(AbstractGraphicsDevice device) { SharedResourceRunner.Resource sr = null; if(null != device) { - start(); - final String connection = device.getConnection(); - sr = impl.mapGet(connection); - if (null == sr && !getDeviceTried(connection)) { - addDeviceTried(connection); - if (DEBUG) { - System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": trying - "+Thread.currentThread().getName()); - } - doAndWait(connection, null); + synchronized (this) { + start(); + final String connection = device.getConnection(); sr = impl.mapGet(connection); - if (DEBUG) { - System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": "+ ( ( null != sr ) ? "success" : "failed" ) +" - "+Thread.currentThread().getName()); + if (null == sr) { + if ( !getDeviceTried(connection) ) { + addDeviceTried(connection); + if (DEBUG) { + System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": trying - "+getThreadName()); + } + if ( impl.isDeviceSupported(connection) ) { + doAndWait(connection, null); + sr = impl.mapGet(connection); + } + if (DEBUG) { + System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": "+ ( ( null != sr ) ? "success" : "failed" ) +" - "+getThreadName()); + } + } } } } @@ -170,16 +201,18 @@ public class SharedResourceRunner implements Runnable { public SharedResourceRunner.Resource releaseShared(AbstractGraphicsDevice device) { SharedResourceRunner.Resource sr = null; if(null != device) { - String connection = device.getConnection(); - sr = impl.mapGet(connection); - if (null != sr) { - removeDeviceTried(connection); - if (DEBUG) { - System.err.println("SharedResourceRunner.releaseShared() " + connection + ": trying - "+Thread.currentThread().getName()); - } - doAndWait(null, connection); - if (DEBUG) { - System.err.println("SharedResourceRunner.releaseShared() " + connection + ": done - "+Thread.currentThread().getName()); + synchronized (this) { + final String connection = device.getConnection(); + sr = impl.mapGet(connection); + if (null != sr) { + removeDeviceTried(connection); + if (DEBUG) { + System.err.println("SharedResourceRunner.releaseShared() " + connection + ": trying - "+getThreadName()); + } + doAndWait(null, connection); + if (DEBUG) { + System.err.println("SharedResourceRunner.releaseShared() " + connection + ": done - "+getThreadName()); + } } } } @@ -187,18 +220,17 @@ public class SharedResourceRunner implements Runnable { } private final void doAndWait(String initConnection, String releaseConnection) { - // wait until thread becomes ready to init new device, - // pass the device and release the sync - final String threadName = Thread.currentThread().getName(); - if (DEBUG) { - System.err.println("SharedResourceRunner.doAndWait() START init: " + initConnection + ", release: "+releaseConnection+" - "+threadName); - } synchronized (this) { - while (!ready) { + // wait until thread becomes ready to init new device, + // pass the device and release the sync + final String threadName = getThreadName(); + if (DEBUG) { + System.err.println("SharedResourceRunner.doAndWait() START init: " + initConnection + ", release: "+releaseConnection+" - "+threadName); + } + while (!ready && running) { try { this.wait(); - } catch (InterruptedException ex) { - } + } catch (InterruptedException ex) { } } if (DEBUG) { System.err.println("SharedResourceRunner.doAndWait() set command: " + initConnection + ", release: "+releaseConnection+" - "+threadName); @@ -208,11 +240,10 @@ public class SharedResourceRunner implements Runnable { this.notifyAll(); // wait until thread has init/released the device - while (!ready || null != this.initConnection || null != this.releaseConnection) { + while ( running && ( !ready || null != this.initConnection || null != this.releaseConnection ) ) { try { this.wait(); - } catch (InterruptedException ex) { - } + } catch (InterruptedException ex) { } } if (DEBUG) { System.err.println("SharedResourceRunner.initializeAndWait END init: " + initConnection + ", release: "+releaseConnection+" - "+threadName); @@ -221,14 +252,17 @@ public class SharedResourceRunner implements Runnable { // done } + @Override public final void run() { - final String threadName = Thread.currentThread().getName(); + final String threadName = getThreadName(); if (DEBUG) { System.err.println("SharedResourceRunner.run(): STARTED - " + threadName); } synchronized (this) { + running = true; + while (!shouldRelease) { try { // wait for stop or init @@ -238,10 +272,10 @@ public class SharedResourceRunner implements Runnable { } notifyAll(); this.wait(); - } catch (InterruptedException ex) { + } catch (InterruptedException ex) { shouldRelease = true; if(DEBUG) { - System.err.println("SharedResourceRunner.run(): INTERRUPTED - "+Thread.currentThread().getName()); + System.err.println("SharedResourceRunner.run(): INTERRUPTED - "+threadName); ex.printStackTrace(); } } @@ -260,9 +294,7 @@ public class SharedResourceRunner implements Runnable { try { sr = impl.createSharedResource(initConnection); } catch (Exception e) { - if(DEBUG) { - e.printStackTrace(); - } + e.printStackTrace(); } if (null != sr) { impl.mapPut(initConnection, sr); @@ -280,7 +312,7 @@ public class SharedResourceRunner implements Runnable { } catch (Exception e) { e.printStackTrace(); } - } + } } } initConnection = null; @@ -298,25 +330,25 @@ public class SharedResourceRunner implements Runnable { } shouldRelease = false; - released = true; + running = false; thread = null; notifyAll(); } } - private void releaseSharedResources() { - synchronized (devicesTried) { - devicesTried.clear(); - } + private void releaseSharedResources() { // synchronized call + devicesTried.clear(); Collection<Resource> sharedResources = impl.mapValues(); for (Iterator<Resource> iter = sharedResources.iterator(); iter.hasNext();) { try { impl.releaseSharedResource(iter.next()); } catch (Throwable t) { - System.err.println("Catched Exception: "+t.getStackTrace()+" - "+Thread.currentThread().getName()); + System.err.println("Catched Exception on thread "+getThreadName()); t.printStackTrace(); } } impl.clear(); } + + protected static String getThreadName() { return Thread.currentThread().getName(); } } diff --git a/src/jogl/classes/jogamp/opengl/ThreadingImpl.java b/src/jogl/classes/jogamp/opengl/ThreadingImpl.java index 61a47675f..bf700d970 100644 --- a/src/jogl/classes/jogamp/opengl/ThreadingImpl.java +++ b/src/jogl/classes/jogamp/opengl/ThreadingImpl.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2009 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * */ package jogamp.opengl; @@ -49,14 +49,14 @@ import com.jogamp.common.util.ReflectionUtil; public class ThreadingImpl { public enum Mode { - MT(0), ST_AWT(1), ST_WORKER(2); - + MT(0), ST_AWT(1), ST_WORKER(2); + public final int id; Mode(int id){ this.id = id; } - } + } protected static final boolean DEBUG = Debug.debug("Threading"); @@ -68,10 +68,11 @@ public class ThreadingImpl { private static boolean _isX11; private static final ToolkitThreadingPlugin threadingPlugin; - + static { threadingPlugin = AccessController.doPrivileged(new PrivilegedAction<ToolkitThreadingPlugin>() { + @Override public ToolkitThreadingPlugin run() { final String singleThreadProp; { @@ -89,13 +90,12 @@ public class ThreadingImpl { // problems. hasAWT = GLProfile.isAWTAvailable(); - String osType = NativeWindowFactory.getNativeWindowType(false); - _isX11 = NativeWindowFactory.TYPE_X11.equals(osType); + _isX11 = NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(false); // default setting singleThreaded = true; mode = ( hasAWT ? Mode.ST_AWT : Mode.ST_WORKER ); - + if (singleThreadProp != null) { if (singleThreadProp.equals("true") || singleThreadProp.equals("auto")) { @@ -114,7 +114,7 @@ public class ThreadingImpl { throw new RuntimeException("Unsupported value for property jogl.1thread: "+singleThreadProp+", should be [true/auto, worker, awt or false]"); } } - + ToolkitThreadingPlugin threadingPlugin=null; if(hasAWT) { // try to fetch the AWTThreadingPlugin @@ -140,9 +140,9 @@ public class ThreadingImpl { public static boolean isX11() { return _isX11; } public static Mode getMode() { return mode; } - /** If an implementation of the javax.media.opengl APIs offers a - multithreading option but the default behavior is single-threading, - this API provides a mechanism for end users to disable single-threading + /** If an implementation of the javax.media.opengl APIs offers a + multithreading option but the default behavior is single-threading, + this API provides a mechanism for end users to disable single-threading in this implementation. Users are strongly discouraged from calling this method unless they are aware of all of the consequences and are prepared to enforce some amount of @@ -152,7 +152,7 @@ public class ThreadingImpl { GLPbuffer. Currently there is no supported way to re-enable it once disabled, partly to discourage careless use of this method. This method should be called as early as possible in an - application. */ + application. */ public static final void disableSingleThreading() { singleThreaded = false; if (Debug.verbose()) { @@ -184,7 +184,7 @@ public class ThreadingImpl { throw new InternalError("Illegal single-threading mode " + mode); } } - + public static final boolean isToolkitThread() throws GLException { if(null!=threadingPlugin) { return threadingPlugin.isToolkitThread(); @@ -216,7 +216,7 @@ public class ThreadingImpl { throw new InternalError("Illegal single-threading mode " + mode); } } - + public static final void invokeOnWorkerThread(boolean wait, Runnable r) throws GLException { GLWorkerThread.start(); // singleton start via volatile-dbl-checked-locking try { @@ -225,6 +225,6 @@ public class ThreadingImpl { throw new GLException(e.getTargetException()); } catch (InterruptedException e) { throw new GLException(e); - } + } } } diff --git a/src/jogl/classes/jogamp/opengl/ToolkitThreadingPlugin.java b/src/jogl/classes/jogamp/opengl/ToolkitThreadingPlugin.java index 22972953a..06fb0fe91 100644 --- a/src/jogl/classes/jogamp/opengl/ToolkitThreadingPlugin.java +++ b/src/jogl/classes/jogamp/opengl/ToolkitThreadingPlugin.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2012 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -46,7 +46,7 @@ public interface ToolkitThreadingPlugin { /** Indicates whether the current thread is the designated toolkit thread, if such semantics exists. */ public boolean isToolkitThread() throws GLException; - + /** Indicates whether the current thread is the thread on which this implementation of the javax.media.opengl APIs performs all of its OpenGL-related work. This method should only diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java index 23f9161d4..25a0bc15d 100644 --- a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java +++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -28,10 +28,17 @@ package jogamp.opengl.android.av; import java.io.IOException; +import java.util.List; import javax.media.opengl.GL; import javax.media.opengl.GLES2; +import javax.media.opengl.GLException; +import com.jogamp.common.os.AndroidVersion; +import com.jogamp.common.os.Platform; +import com.jogamp.opengl.util.TimeFrameI; +import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; import jogamp.common.os.android.StaticContext; @@ -39,7 +46,9 @@ import jogamp.opengl.util.av.GLMediaPlayerImpl; import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture.OnFrameAvailableListener; +import android.hardware.Camera; import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; import android.net.Uri; import android.view.Surface; @@ -47,6 +56,11 @@ import android.view.Surface; * Android implementation utilizes API level 14 (4.0.? ICS) features * as listed below. * <p> + * Implementation is single threaded only, since we are not able to utilize multiple textures. + * We would need to add an implementation for API level 16 using MediaCodec/MediaExtractor + * to expose multithreading on multiple surface/textures. + * </p> + * <p> * We utilize the {@link MediaPlayer} with direct to texture streaming. * The MediaPlayer uses <code>libstagefright</code> to access the OpenMAX AL implementation * for hardware decoding. @@ -55,47 +69,63 @@ import android.view.Surface; * <li>Android API Level 14: {@link MediaPlayer#setSurface(Surface)}</li> * <li>Android API Level 14: {@link Surface#Surface(android.graphics.SurfaceTexture)}</li> * </ul> + * <p> + * Since the MediaPlayer API can only deal w/ <i>one</i> SurfaceTexture, + * we enforce <code>textureCount</code> = 2 via {@link #validateTextureCount(int)} + * and duplicate the single texture via {@link #createTexFrames(GL, int)} .. etc. + * Two instanceds of TextureFrame are required due our framework implementation w/ Ringbuffer and 'lastFrame' access. + * </p> */ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { static final boolean available; - + static { - available = true; // default .. TODO: May restrict availability ? + boolean _avail = false; + if(Platform.OS_TYPE.equals(Platform.OSType.ANDROID)) { + if(AndroidVersion.SDK_INT >= 14) { + _avail = true; + } + } + available = _avail; } - + public static final boolean isAvailable() { return available; } - - MediaPlayer mp; - volatile boolean updateSurface = false; - Object updateSurfaceLock = new Object(); - TextureSequence.TextureFrame lastTexFrame = null; + + private MediaPlayer mp; + private Camera cam; + private long playStart = 0; + private volatile boolean updateSurface = false; + private final Object updateSurfaceLock = new Object(); + private SurfaceTextureFrame singleSTexFrame = null; + private int sTexFrameCount = 0; + private boolean sTexFrameAttached = false; + private volatile boolean eos = false; /** private static String toString(MediaPlayer m) { if(null == m) return "<nil>"; return "MediaPlayer[playing "+m.isPlaying()+", pos "+m.getCurrentPosition()/1000.0f+"s, "+m.getVideoWidth()+"x"+m.getVideoHeight()+"]"; } */ - + public AndroidGLMediaPlayerAPI14() { super(); if(!available) { throw new RuntimeException("AndroidGLMediaPlayerAPI14 not available"); } this.setTextureTarget(GLES2.GL_TEXTURE_EXTERNAL_OES); - this.setTextureCount(1); - mp = new MediaPlayer(); } @Override - protected boolean setPlaySpeedImpl(float rate) { + protected final boolean setPlaySpeedImpl(float rate) { + // FIXME return false; } @Override - protected boolean startImpl() { - if(null != mp) { + protected final boolean setAudioVolumeImpl(float v) { + if(null != mp) { try { - mp.start(); + mp.setVolume(v, v); return true; } catch (IllegalStateException ise) { if(DEBUG) { @@ -107,11 +137,24 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } @Override - protected boolean pauseImpl() { + protected final boolean playImpl() { + playStart = Platform.currentTimeMillis(); if(null != mp) { - wakeUp(false); try { - mp.pause(); + mp.start(); + eos = false; + mp.setOnCompletionListener(onCompletionListener); + return true; + } catch (IllegalStateException ise) { + if(DEBUG) { + ise.printStackTrace(); + } + } + } else if( null != cam ) { + try { + if( sTexFrameAttached ) { + cam.startPreview(); + } return true; } catch (IllegalStateException ise) { if(DEBUG) { @@ -123,11 +166,21 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } @Override - protected boolean stopImpl() { + protected final boolean pauseImpl() { if(null != mp) { wakeUp(false); try { - mp.stop(); + mp.pause(); + return true; + } catch (IllegalStateException ise) { + if(DEBUG) { + ise.printStackTrace(); + } + } + } else if( null != cam ) { + wakeUp(false); + try { + cam.stopPreview(); return true; } catch (IllegalStateException ise) { if(DEBUG) { @@ -137,9 +190,9 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } return false; } - + @Override - protected int seekImpl(int msec) { + protected final int seekImpl(int msec) { if(null != mp) { mp.seekTo(msec); return mp.getCurrentPosition(); @@ -147,40 +200,6 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { return 0; } - @Override - protected TextureSequence.TextureFrame getLastTextureImpl() { - return lastTexFrame; - } - - @Override - protected TextureSequence.TextureFrame getNextTextureImpl(GL gl, boolean blocking) { - if(null != stex && null != mp) { - // Only block once, no while-loop. - // This relaxes locking code of non crucial resources/events. - boolean update = updateSurface; - if(!update && blocking) { - synchronized(updateSurfaceLock) { - if(!updateSurface) { // volatile OK. - try { - updateSurfaceLock.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - updateSurface = false; - update = true; - } - } - if(update) { - stex.updateTexImage(); - // stex.getTransformMatrix(atex.getSTMatrix()); - lastTexFrame=texFrames[0]; - } - - } - return lastTexFrame; - } - private void wakeUp(boolean newFrame) { synchronized(updateSurfaceLock) { if(newFrame) { @@ -189,29 +208,80 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { updateSurfaceLock.notifyAll(); } } - + @Override - protected int getCurrentPositionImpl() { - return null != mp ? mp.getCurrentPosition() : 0; - } + protected final int getAudioPTSImpl() { return null != mp ? mp.getCurrentPosition() : 0; } @Override - protected void destroyImpl(GL gl) { + protected final void destroyImpl(GL gl) { if(null != mp) { wakeUp(false); + try { + mp.stop(); + } catch (IllegalStateException ise) { + if(DEBUG) { + ise.printStackTrace(); + } + } mp.release(); mp = null; } + if( null != cam ) { + wakeUp(false); + try { + cam.stopPreview(); + } catch (IllegalStateException ise) { + if(DEBUG) { + ise.printStackTrace(); + } + } + cam.release(); + cam = null; + } } - - SurfaceTexture stex = null; - + + public static class SurfaceTextureFrame extends TextureSequence.TextureFrame { + public SurfaceTextureFrame(Texture t, SurfaceTexture stex) { + super(t); + this.surfaceTex = stex; + } + + public String toString() { + return "SurfaceTextureFrame[pts " + pts + " ms, l " + duration + " ms, texID "+ texture.getTextureObject() + ", " + surfaceTex + "]"; + } + public final SurfaceTexture surfaceTex; + } + @Override - protected void initGLStreamImpl(GL gl, int[] texNames) throws IOException { - if(null!=mp && null!=urlConn) { + protected final void initStreamImpl(final int vid, final int aid) throws IOException { + if( null == streamLoc ) { + return; + } + if( null == mp && null == cam ) { + if( null == cameraPath ) { + mp = new MediaPlayer(); + } else { + int cameraId = 0; + try { + cameraId = Integer.valueOf(cameraPath); + } catch (NumberFormatException nfe) {} + if( 0 <= cameraId && cameraId < Camera.getNumberOfCameras() ) { + cam = Camera.open(cameraId); + } else { + cam = Camera.open(); + } + } + } + + if(null!=mp) { + if( GLMediaPlayer.STREAM_ID_NONE == aid ) { + mp.setVolume(0f, 0f); + // FIXME: Disable audio handling + } // else FIXME: Select aid ! + // Note: Both FIXMEs seem to be n/a via Android's MediaPlayer -> Switch to API level 16 MediaCodec/MediaExtractor .. try { - final Uri uri = Uri.parse(urlConn.getURL().toExternalForm()); - mp.setDataSource(StaticContext.getContext(), uri); + final Uri _uri = Uri.parse(streamLoc.toString()); + mp.setDataSource(StaticContext.getContext(), _uri); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (SecurityException e) { @@ -219,44 +289,206 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } catch (IllegalStateException e) { throw new RuntimeException(e); } - stex = new SurfaceTexture(texNames[0]); // only 1 texture - stex.setOnFrameAvailableListener(onFrameAvailableListener); - final Surface surf = new Surface(stex); - mp.setSurface(surf); - surf.release(); + mp.setSurface(null); try { mp.prepare(); } catch (IOException ioe) { - throw new IOException("MediaPlayer failed to process stream <"+urlConn.getURL().toExternalForm()+">: "+ioe.getMessage(), ioe); + throw new IOException("MediaPlayer failed to process stream <"+streamLoc.toString()+">: "+ioe.getMessage(), ioe); + } + final int r_aid = GLMediaPlayer.STREAM_ID_NONE == aid ? GLMediaPlayer.STREAM_ID_NONE : 1 /* fake */; + final String icodec = "android"; + updateAttributes(0 /* fake */, r_aid, + mp.getVideoWidth(), mp.getVideoHeight(), 0, + 0, 0, 0f, + 0, 0, mp.getDuration(), icodec, icodec); + /** + mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(final MediaPlayer mp) { + final int r_aid = GLMediaPlayer.STREAM_ID_NONE == aid ? GLMediaPlayer.STREAM_ID_NONE : 1; // fake + final String icodec = "android"; + updateAttributes(0, r_aid, // fake + mp.getVideoWidth(), mp.getVideoHeight(), 0, + 0, 0, 0f, + 0, 0, mp.getDuration(), icodec, icodec); + } + }); + mp.prepareAsync(); + * + */ + } else if( null != cam ) { + final String icodec = "android"; + final int[] fpsRange = { 0, 0 }; + final Camera.Parameters p = cam.getParameters(); + p.getPreviewFpsRange(fpsRange); + final Camera.Size size = p.getPreviewSize(); + if( DEBUG ) { + final int picFmt = p.getPictureFormat(); + final Camera.Size prefSize = p.getPreferredPreviewSizeForVideo(); + System.err.println("MediaPlayer.Camera: fps "+fpsRange[0]+".."+fpsRange[1]+", size[pref "+camSz2Str(prefSize)+", cur "+camSz2Str(size)+"], fmt "+picFmt); + final List<Camera.Size> supSizes = p.getSupportedVideoSizes(); + if( null != supSizes ) { + for(int i=0; i<supSizes.size(); i++) { + System.err.println("size #"+i+": "+camSz2Str(supSizes.get(i))); + } + } } - updateAttributes(mp.getVideoWidth(), mp.getVideoHeight(), - 0, 0, 0, - 0f, 0, mp.getDuration(), - null, null); + updateAttributes(0 /* fake */, GLMediaPlayer.STREAM_ID_NONE, + size.width, size.height, + 0, 0, 0, + fpsRange[1]/1000f, + 0, 0, 0, icodec, icodec); + } + } + private static String camSz2Str(Camera.Size csize) { + if( null != csize ) { + return csize.width+"x"+csize.height; + } else { + return "n/a"; } } - @Override - protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) { - lastTexFrame = new TextureSequence.TextureFrame( createTexImageImpl(gl, idx, tex, width, height, true) ); - return lastTexFrame; + protected final void initGLImpl(GL gl) throws IOException, GLException { + // NOP } - + + /** + * {@inheritDoc} + * <p> + * Returns {@link #TEXTURE_COUNT_MIN}, using a single texture + * </p> + */ @Override - protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) { - if(null != stex) { - stex.release(); - stex = null; + protected int validateTextureCount(int desiredTextureCount) { + return TEXTURE_COUNT_MIN; + } + + @Override + protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) { + int pts = TimeFrameI.INVALID_PTS; + if(null != mp || null != cam) { + final SurfaceTextureFrame sTexFrame = null != nextFrame ? (SurfaceTextureFrame) nextFrame : singleSTexFrame; + final SurfaceTexture surfTex = sTexFrame.surfaceTex; + if( !sTexFrameAttached ) { + sTexFrameAttached = true; + final Surface surface; + if( null != mp ) { + surface = new Surface(sTexFrame.surfaceTex); + mp.setSurface(surface); + } else { + surface = null; + try { + cam.setPreviewTexture(sTexFrame.surfaceTex); + cam.startPreview(); + } catch (IOException ioe) { + throw new RuntimeException("MediaPlayer failed to process stream <"+streamLoc.toString()+">: "+ioe.getMessage(), ioe); + } + } + if( null != surface ) { + surface.release(); + } + surfTex.setOnFrameAvailableListener(onFrameAvailableListener); + } + if( eos || (null != mp && !mp.isPlaying() ) ) { + eos = true; + pts = TimeFrameI.END_OF_STREAM_PTS; + } else { + // Only block once, no while-loop. + // This relaxes locking code of non crucial resources/events. + boolean update = updateSurface; + if( !update ) { + synchronized(updateSurfaceLock) { + if(!updateSurface) { // volatile OK. + try { + updateSurfaceLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + update = updateSurface; + updateSurface = false; + } + } + if(update) { + surfTex.updateTexImage(); + // nextFrame.setPTS( (int) ( nextSTex.getTimestamp() / 1000000L ) ); // nano -9 -> milli -3 + if( null != mp ) { + pts = mp.getCurrentPosition(); + } else { + pts = (int) ( Platform.currentTimeMillis() - playStart ); + } + // stex.getTransformMatrix(atex.getSTMatrix()); + } + } + nextFrame.setPTS( pts ); + } + return pts; + } + + /** + * {@inheritDoc} + * <p> + * Creates only one single texture and duplicated content to 2 TextureFrames + * </p> + */ + @Override + protected TextureFrame[] createTexFrames(GL gl, final int count) { + final int[] texNames = new int[1]; + gl.glGenTextures(1, texNames, 0); + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("TextureNames creation failed (num: 1/"+count+"): err "+toHexString(err)); + } + final TextureFrame[] texFrames = new TextureFrame[count]; + for(int i=0; i<count; i++) { + texFrames[i] = createTexImage(gl, texNames[0]); } - lastTexFrame = null; - super.destroyTexImage(gl, imgTex); + return texFrames; } - - protected OnFrameAvailableListener onFrameAvailableListener = new OnFrameAvailableListener() { + /** + * {@inheritDoc} + * <p> + * Returns the single texture, which is created at 1st call. + * </p> + */ + @Override + protected final TextureSequence.TextureFrame createTexImage(GL gl, int texName) { + sTexFrameCount++; + if( 1 == sTexFrameCount ) { + singleSTexFrame = new SurfaceTextureFrame( createTexImageImpl(gl, texName, width, height), new SurfaceTexture(texName) ); + } + return singleSTexFrame; + } + + /** + * {@inheritDoc} + * <p> + * Destroys the single texture at last call. + * </p> + */ + @Override + protected final void destroyTexFrame(GL gl, TextureSequence.TextureFrame frame) { + sTexFrameCount--; + if( 0 == sTexFrameCount ) { + singleSTexFrame = null; + sTexFrameAttached = false; + final SurfaceTextureFrame sFrame = (SurfaceTextureFrame) frame; + sFrame.surfaceTex.release(); + super.destroyTexFrame(gl, frame); + } + } + + private final OnFrameAvailableListener onFrameAvailableListener = new OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { wakeUp(true); - AndroidGLMediaPlayerAPI14.this.newFrameAvailable(); - } - }; + } + }; + + private final OnCompletionListener onCompletionListener = new OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + eos = true; + } + }; } diff --git a/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java b/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java index 983f11133..72c9ac54b 100644 --- a/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java +++ b/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -44,7 +44,8 @@ import java.awt.EventQueue; import javax.media.opengl.GLException; -import jogamp.common.awt.AWTEDTExecutor; +import com.jogamp.common.util.awt.AWTEDTExecutor; + import jogamp.opengl.GLWorkerThread; import jogamp.opengl.ThreadingImpl; import jogamp.opengl.ToolkitThreadingPlugin; @@ -53,10 +54,12 @@ public class AWTThreadingPlugin implements ToolkitThreadingPlugin { public AWTThreadingPlugin() {} + @Override public final boolean isToolkitThread() throws GLException { return EventQueue.isDispatchThread(); } - + + @Override public final boolean isOpenGLThread() throws GLException { switch (ThreadingImpl.getMode()) { case ST_AWT: @@ -82,6 +85,7 @@ public class AWTThreadingPlugin implements ToolkitThreadingPlugin { } } + @Override public final void invokeOnOpenGLThread(boolean wait, Runnable r) throws GLException { switch (ThreadingImpl.getMode()) { case ST_AWT: @@ -94,11 +98,7 @@ public class AWTThreadingPlugin implements ToolkitThreadingPlugin { // QFT which is not allowed. For now, on X11 platforms, // continue to perform this work on the EDT. if (wait && Java2D.isOGLPipelineActive() && !ThreadingImpl.isX11()) { - if(wait) { - Java2D.invokeWithOGLContextCurrent(null, r); - } else { - - } + Java2D.invokeWithOGLContextCurrent(null, r); } else { AWTEDTExecutor.singleton.invoke(wait, r); } diff --git a/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java b/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java new file mode 100644 index 000000000..1c1d2350a --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/awt/AWTTilePainter.java @@ -0,0 +1,400 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.awt; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; +import java.util.Map.Entry; + +import javax.imageio.ImageIO; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLEventListener; + +import jogamp.opengl.Debug; + +import com.jogamp.opengl.util.TileRenderer; +import com.jogamp.opengl.util.TileRendererBase; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.AWTGLPixelBufferProvider; + +/** + * Implementing AWT {@link Graphics2D} based {@link TileRenderer} <i>painter</i>. + * <p> + * Maybe utilized for AWT printing. + * </p> + */ +public class AWTTilePainter { + private static final boolean DEBUG_TILES = Debug.debug("TileRenderer.PNG"); + + public final TileRenderer renderer; + public final int componentCount; + public final double scaleMatX, scaleMatY; + public final int customTileWidth, customTileHeight, customNumSamples; + public final boolean verbose; + + /** Default for OpenGL: True */ + public boolean flipVertical; + /** Default for OpenGL: True */ + public boolean originBottomLeft; + private AWTGLPixelBuffer tBuffer = null; + private BufferedImage vFlipImage = null; + private Graphics2D g2d = null; + private AffineTransform saveAT = null; + + public static void dumpHintsAndScale(Graphics2D g2d) { + final RenderingHints rHints = g2d.getRenderingHints(); + final Set<Entry<Object, Object>> rEntries = rHints.entrySet(); + int count = 0; + for(Iterator<Entry<Object, Object>> rEntryIter = rEntries.iterator(); rEntryIter.hasNext(); count++) { + final Entry<Object, Object> rEntry = rEntryIter.next(); + System.err.println("Hint["+count+"]: "+rEntry.getKey()+" -> "+rEntry.getValue()); + } + final AffineTransform aTrans = g2d.getTransform(); + if( null != aTrans ) { + System.err.println(" type "+aTrans.getType()); + System.err.println(" scale "+aTrans.getScaleX()+" x "+aTrans.getScaleY()); + System.err.println(" move "+aTrans.getTranslateX()+" x "+aTrans.getTranslateY()); + System.err.println(" mat "+aTrans); + } else { + System.err.println(" null transform"); + } + } + + /** + * @return resulting number of samples by comparing w/ {@link #customNumSamples} and the caps-config, 0 if disabled + */ + public int getNumSamples(GLCapabilitiesImmutable caps) { + if( 0 > customNumSamples ) { + return 0; + } else if( 0 < customNumSamples ) { + if ( !caps.getGLProfile().isGL2ES3() ) { + return 0; + } + return Math.max(caps.getNumSamples(), customNumSamples); + } else { + return caps.getNumSamples(); + } + } + + /** + * Assumes a configured {@link TileRenderer}, i.e. + * an {@link TileRenderer#attachAutoDrawable(GLAutoDrawable) attached} + * {@link GLAutoDrawable} with {@link TileRenderer#setTileSize(int, int, int) set tile size}. + * <p> + * Sets the renderer to {@link TileRenderer#TR_TOP_TO_BOTTOM} row order. + * </p> + * <p> + * <code>componentCount</code> reflects opaque, i.e. 4 if non opaque. + * </p> + * @param renderer + * @param componentCount + * @param scaleMatX {@link Graphics2D} {@link Graphics2D#scale(double, double) scaling factor}, i.e. rendering 1/scaleMatX * width pixels + * @param scaleMatY {@link Graphics2D} {@link Graphics2D#scale(double, double) scaling factor}, i.e. rendering 1/scaleMatY * height pixels + * @param numSamples custom multisampling value: < 0 turns off, == 0 leaves as-is, > 0 enables using given num samples + * @param tileWidth custom tile width for {@link TileRenderer#setTileSize(int, int, int) tile renderer}, pass -1 for default. + * @param tileHeight custom tile height for {@link TileRenderer#setTileSize(int, int, int) tile renderer}, pass -1 for default. + * @param verbose + */ + public AWTTilePainter(TileRenderer renderer, int componentCount, double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight, boolean verbose) { + this.renderer = renderer; + this.renderer.setGLEventListener(preTileGLEL, postTileGLEL); + this.componentCount = componentCount; + this.scaleMatX = scaleMatX; + this.scaleMatY = scaleMatY; + this.customNumSamples = numSamples; + this.customTileWidth= tileWidth; + this.customTileHeight = tileHeight; + this.verbose = verbose; + this.flipVertical = true; + } + + @Override + public String toString() { + return "AWTTilePainter[flipVertical "+flipVertical+", startFromBottom "+originBottomLeft+", "+ + renderer.toString()+"]"; + } + + /** + * @param flipVertical if <code>true</code>, the image will be flipped vertically (Default for OpenGL). + * @param originBottomLeft if <code>true</code>, the image's origin is on the bottom left (Default for OpenGL). + */ + public void setGLOrientation(boolean flipVertical, boolean originBottomLeft) { + this.flipVertical = flipVertical; + this.originBottomLeft = originBottomLeft; + } + + private static Rectangle2D getClipBounds2D(Graphics2D g) { + final Shape shape = g.getClip(); + return null != shape ? shape.getBounds2D() : null; + } + private static Rectangle2D clipNegative(Rectangle2D in) { + if( null == in ) { return null; } + double x=in.getX(), y=in.getY(), width=in.getWidth(), height=in.getHeight(); + if( 0 > x ) { + width += x; + x = 0; + } + if( 0 > y ) { + height += y; + y = 0; + } + return new Rectangle2D.Double(x, y, width, height); + } + + /** + * Caches the {@link Graphics2D} instance for rendering. + * <p> + * Copies the current {@link Graphics2D} {@link AffineTransform} + * and scales {@link Graphics2D} w/ <code>scaleMatX</code> x <code>scaleMatY</code>.<br> + * After rendering, the {@link AffineTransform} should be reset via {@link #resetGraphics2D()}. + * </p> + * <p> + * Sets the {@link TileRenderer}'s {@link TileRenderer#setImageSize(int, int) image size} + * and {@link TileRenderer#setTileOffset(int, int) tile offset} according the + * the {@link Graphics2D#getClipBounds() graphics clip bounds}. + * </p> + * @param g2d Graphics2D instance used for transform and clipping + * @param width width of the AWT component in case clipping is null + * @param height height of the AWT component in case clipping is null + * @throws NoninvertibleTransformException if the {@link Graphics2D}'s {@link AffineTransform} {@link AffineTransform#invert() inversion} fails. + * Since inversion is tested before scaling the given {@link Graphics2D}, caller shall ignore the whole <i>term</i>. + */ + public void setupGraphics2DAndClipBounds(Graphics2D g2d, int width, int height) throws NoninvertibleTransformException { + this.g2d = g2d; + saveAT = g2d.getTransform(); + if( null == saveAT ) { + saveAT = new AffineTransform(); // use identity + } + // We use double precision for scaling + // + // Setup original rectangles + final Rectangle2D dClipOrigR = getClipBounds2D(g2d); + final Rectangle2D dClipOrig = clipNegative(dClipOrigR); + final Rectangle2D dImageSizeOrig = new Rectangle2D.Double(0, 0, width, height); + + // Retrieve scaled image-size and clip-bounds + // Note: Clip bounds lie within image-size! + final Rectangle2D dImageSizeScaled, dClipScaled; + { + final AffineTransform scaledATI; + { + final AffineTransform scaledAT = new AffineTransform(saveAT); + scaledAT.scale(scaleMatX, scaleMatY); + scaledATI = scaledAT.createInverse(); // -> NoninvertibleTransformException + } + Shape s0 = saveAT.createTransformedShape(dImageSizeOrig); // user in + dImageSizeScaled = scaledATI.createTransformedShape(s0).getBounds2D(); // scaled out + if( null == dClipOrig ) { + dClipScaled = (Rectangle2D) dImageSizeScaled.clone(); + } else { + s0 = saveAT.createTransformedShape(dClipOrig); // user in + dClipScaled = scaledATI.createTransformedShape(s0).getBounds2D(); // scaled out + } + } + final Rectangle iClipScaled = dClipScaled.getBounds(); + final Rectangle iImageSizeScaled = dImageSizeScaled.getBounds(); + renderer.setImageSize(iImageSizeScaled.width, iImageSizeScaled.height); + renderer.clipImageSize(iClipScaled.width, iClipScaled.height); + final int clipH = Math.min(iImageSizeScaled.height, iClipScaled.height); + // Clip bounds lie within image-size! + // GL y-offset is lower-left origin, AWT y-offset upper-left. + scaledYOffset = iClipScaled.y; + renderer.setTileOffset(iClipScaled.x, iImageSizeScaled.height - ( iClipScaled.y + clipH )); + + // Scale actual Grahics2D matrix + g2d.scale(scaleMatX, scaleMatY); + + if( verbose ) { + System.err.println("AWT print.0: image "+dImageSizeOrig + " -> " + dImageSizeScaled + " -> " + iImageSizeScaled); + System.err.println("AWT print.0: clip "+dClipOrigR + " -> " + dClipOrig + " -> " + dClipScaled + " -> " + iClipScaled); + System.err.println("AWT print.0: "+renderer); + } + } + private int scaledYOffset; + + /** See {@ #setupGraphics2DAndClipBounds(Graphics2D)}. */ + public void resetGraphics2D() { + g2d.setTransform(saveAT); + } + + /** + * Disposes resources and {@link TileRenderer#detachAutoDrawable() detaches} + * the {@link TileRenderer}'s {@link GLAutoDrawable}. + */ + public void dispose() { + renderer.detachAutoDrawable(); // tile-renderer -> printGLAD + g2d = null; + if( null != tBuffer ) { + tBuffer.dispose(); + tBuffer = null; + } + if( null != vFlipImage ) { + vFlipImage.flush(); + vFlipImage = null; + } + } + + final GLEventListener preTileGLEL = new GLEventListener() { + @Override + public void init(GLAutoDrawable drawable) {} + @Override + public void dispose(GLAutoDrawable drawable) {} + @Override + public void display(GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + if( null == tBuffer ) { + final int tWidth = renderer.getParam(TileRenderer.TR_TILE_WIDTH); + final int tHeight = renderer.getParam(TileRenderer.TR_TILE_HEIGHT); + final AWTGLPixelBufferProvider printBufferProvider = new AWTGLPixelBufferProvider( true /* allowRowStride */ ); + final GLPixelAttributes pixelAttribs = printBufferProvider.getAttributes(gl, componentCount); + tBuffer = printBufferProvider.allocate(gl, pixelAttribs, tWidth, tHeight, 1, true, 0); + renderer.setTileBuffer(tBuffer); + if( flipVertical ) { + vFlipImage = new BufferedImage(tBuffer.width, tBuffer.height, tBuffer.image.getType()); + } else { + vFlipImage = null; + } + } + if( verbose ) { + System.err.println("XXX tile-pre "+renderer); + } + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + }; + static int _counter = 0; + final GLEventListener postTileGLEL = new GLEventListener() { + @Override + public void init(GLAutoDrawable drawable) { + } + @Override + public void dispose(GLAutoDrawable drawable) {} + @Override + public void display(GLAutoDrawable drawable) { + final DimensionImmutable cis = renderer.getClippedImageSize(); + final int tWidth = renderer.getParam(TileRendererBase.TR_CURRENT_TILE_WIDTH); + final int tHeight = renderer.getParam(TileRendererBase.TR_CURRENT_TILE_HEIGHT); + final int tY = renderer.getParam(TileRendererBase.TR_CURRENT_TILE_Y_POS); + final int tYOff = renderer.getParam(TileRenderer.TR_TILE_Y_OFFSET); + final int imgYOff = originBottomLeft ? 0 : renderer.getParam(TileRenderer.TR_TILE_HEIGHT) - tHeight; // imgYOff will be cut-off via sub-image + final int pX = renderer.getParam(TileRendererBase.TR_CURRENT_TILE_X_POS); // tileX == pX + final int pY = cis.getHeight() - ( tY - tYOff + tHeight ) + scaledYOffset; + + // Copy temporary data into raster of BufferedImage for faster + // blitting Note that we could avoid this copy in the cases + // where !offscreenDrawable.isGLOriented(), + // but that's the software rendering path which is very slow anyway. + final BufferedImage dstImage; + if( DEBUG_TILES ) { + final String fname = String.format("file_%03d_0_tile_[%02d][%02d]_sz_%03dx%03d_pos0_%03d_%03d_yOff_%03d_pos1_%03d_%03d.png", + _counter, + renderer.getParam(TileRenderer.TR_CURRENT_COLUMN), renderer.getParam(TileRenderer.TR_CURRENT_ROW), + tWidth, tHeight, + pX, tY, tYOff, pX, pY).replace(' ', '_'); + System.err.println("XXX file "+fname); + final File fout = new File(fname); + try { + ImageIO.write(tBuffer.image, "png", fout); + } catch (IOException e) { + e.printStackTrace(); + } + } + if( flipVertical ) { + final BufferedImage srcImage = tBuffer.image; + dstImage = vFlipImage; + final int[] src = ((DataBufferInt) srcImage.getRaster().getDataBuffer()).getData(); + final int[] dst = ((DataBufferInt) dstImage.getRaster().getDataBuffer()).getData(); + if( DEBUG_TILES ) { + Arrays.fill(dst, 0x55); + } + final int incr = tBuffer.width; + int srcPos = 0; + int destPos = (tHeight - 1) * tBuffer.width; + for (; destPos >= 0; srcPos += incr, destPos -= incr) { + System.arraycopy(src, srcPos, dst, destPos, incr); + } + } else { + dstImage = tBuffer.image; + } + if( DEBUG_TILES ) { + final String fname = String.format("file_%03d_1_tile_[%02d][%02d]_sz_%03dx%03d_pos0_%03d_%03d_yOff_%03d_pos1_%03d_%03d.png", + _counter, + renderer.getParam(TileRenderer.TR_CURRENT_COLUMN), renderer.getParam(TileRenderer.TR_CURRENT_ROW), + tWidth, tHeight, + pX, tY, tYOff, pX, pY).replace(' ', '_'); + System.err.println("XXX file "+fname); + final File fout = new File(fname); + try { + ImageIO.write(dstImage, "png", fout); + } catch (IOException e) { + e.printStackTrace(); + } + _counter++; + } + // Draw resulting image in one shot + final BufferedImage outImage = dstImage.getSubimage(0, imgYOff, tWidth, tHeight); + final boolean drawDone = g2d.drawImage(outImage, pX, pY, null); // Null ImageObserver since image data is ready. + if( verbose ) { + final Shape oClip = g2d.getClip(); + System.err.println("XXX tile-post.X tile 0 / "+imgYOff+" "+tWidth+"x"+tHeight+", clippedImgSize "+cis); + System.err.println("XXX tile-post.X pYf "+cis.getHeight()+" - ( "+tY+" - "+tYOff+" + "+tHeight+" ) "+scaledYOffset+" = "+ pY); + System.err.println("XXX tile-post.X clip "+oClip+" + "+pX+" / [pY "+tY+", pYOff "+tYOff+", pYf "+pY+"] -> "+g2d.getClip()); + g2d.setColor(Color.BLACK); + g2d.drawRect(pX, pY, tWidth, tHeight); + if( null != oClip ) { + final Rectangle r = oClip.getBounds(); + g2d.setColor(Color.YELLOW); + g2d.drawRect(r.x, r.y, r.width, r.height); + } + System.err.println("XXX tile-post.X "+renderer); + System.err.println("XXX tile-post.X dst-img "+dstImage.getWidth()+"x"+dstImage.getHeight()); + System.err.println("XXX tile-post.X out-img "+outImage.getWidth()+"x"+outImage.getHeight()); + System.err.println("XXX tile-post.X y-flip "+flipVertical+", originBottomLeft "+originBottomLeft+" -> "+pX+"/"+pY+", drawDone "+drawDone); + } + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + }; +} diff --git a/src/jogl/classes/jogamp/opengl/awt/AWTUtil.java b/src/jogl/classes/jogamp/opengl/awt/AWTUtil.java index e15e538c2..dc286ca59 100644 --- a/src/jogl/classes/jogamp/opengl/awt/AWTUtil.java +++ b/src/jogl/classes/jogamp/opengl/awt/AWTUtil.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -85,7 +85,7 @@ public class AWTUtil { NativeWindowFactory.getAWTToolkitLock().lock(); } } catch (Exception e) { j2dOk=false; } - } + } if(!j2dOk) { NativeWindowFactory.getAWTToolkitLock().lock(); } @@ -108,7 +108,7 @@ public class AWTUtil { NativeWindowFactory.getAWTToolkitLock().unlock(); } } catch (Exception e) { j2dOk=false; } - } + } if(!j2dOk) { NativeWindowFactory.getAWTToolkitLock().unlock(); } diff --git a/src/jogl/classes/jogamp/opengl/awt/Java2D.java b/src/jogl/classes/jogamp/opengl/awt/Java2D.java index 3dbfefb19..886cd9368 100644 --- a/src/jogl/classes/jogamp/opengl/awt/Java2D.java +++ b/src/jogl/classes/jogamp/opengl/awt/Java2D.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -69,6 +69,7 @@ public class Java2D { private static boolean DEBUG = Debug.debug("Java2D"); private static boolean isHeadless; private static boolean isOGLPipelineActive; + private static boolean isOGLPipelineResourceCompatible; private static Method invokeWithOGLContextCurrentMethod; private static Method isQueueFlusherThreadMethod; private static Method getOGLViewportMethod; @@ -115,6 +116,7 @@ public class Java2D { static { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { if (DEBUG) { System.err.println("Checking for Java2D/OpenGL support"); @@ -124,18 +126,36 @@ public class Java2D { isHeadless = true; // Figure out whether the default graphics configuration is an // OpenGL graphics configuration - GraphicsConfiguration cfg = - GraphicsEnvironment.getLocalGraphicsEnvironment(). - getDefaultScreenDevice(). - getDefaultConfiguration(); + final GraphicsConfiguration cfg; + final String cfgName; + final boolean java2dOGLDisabledByOS = Platform.OS_TYPE == Platform.OSType.MACOS; + final boolean java2dOGLDisabledByProp; + { + boolean enabled = true; + final String sVal = System.getProperty("sun.java2d.opengl"); + if( null != sVal ) { + enabled = Boolean.valueOf(sVal); + } + java2dOGLDisabledByProp = !enabled; + } + if( !java2dOGLDisabledByProp && !java2dOGLDisabledByOS ) { + cfg = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + cfgName = cfg.getClass().getName(); + } else { + if (DEBUG) { + System.err.println("Java2D support disabled: by Property "+java2dOGLDisabledByProp+", by OS "+java2dOGLDisabledByOS); + } + cfg = null; + cfgName = "nil"; + } // If we get here, we aren't running in headless mode isHeadless = false; - String name = cfg.getClass().getName(); if (DEBUG) { - System.err.println("Java2D support: default GraphicsConfiguration = " + name); + System.err.println("Java2D support: default GraphicsConfiguration = " + cfgName); } - isOGLPipelineActive = Platform.OS_TYPE != Platform.OSType.MACOS && - (name.startsWith("sun.java2d.opengl")); + isOGLPipelineActive = cfgName.startsWith("sun.java2d.opengl"); + isOGLPipelineResourceCompatible = isOGLPipelineActive; if (isOGLPipelineActive) { try { @@ -152,99 +172,101 @@ public class Java2D { new Class[] {}); isQueueFlusherThreadMethod.setAccessible(true); - getOGLViewportMethod = utils.getDeclaredMethod("getOGLViewport", - new Class[] { - Graphics.class, - Integer.TYPE, - Integer.TYPE - }); - getOGLViewportMethod.setAccessible(true); - - getOGLScissorBoxMethod = utils.getDeclaredMethod("getOGLScissorBox", - new Class[] { - Graphics.class - }); - getOGLScissorBoxMethod.setAccessible(true); - - getOGLSurfaceIdentifierMethod = utils.getDeclaredMethod("getOGLSurfaceIdentifier", + if( isOGLPipelineResourceCompatible ) { + getOGLViewportMethod = utils.getDeclaredMethod("getOGLViewport", + new Class[] { + Graphics.class, + Integer.TYPE, + Integer.TYPE + }); + getOGLViewportMethod.setAccessible(true); + + getOGLScissorBoxMethod = utils.getDeclaredMethod("getOGLScissorBox", + new Class[] { + Graphics.class + }); + getOGLScissorBoxMethod.setAccessible(true); + + getOGLSurfaceIdentifierMethod = utils.getDeclaredMethod("getOGLSurfaceIdentifier", + new Class[] { + Graphics.class + }); + getOGLSurfaceIdentifierMethod.setAccessible(true); + + // Try to get additional methods required for proper FBO support + fbObjectSupportInitialized = true; + try { + invokeWithOGLSharedContextCurrentMethod = utils.getDeclaredMethod("invokeWithOGLSharedContextCurrent", + new Class[] { + GraphicsConfiguration.class, + Runnable.class + }); + invokeWithOGLSharedContextCurrentMethod.setAccessible(true); + + getOGLSurfaceTypeMethod = utils.getDeclaredMethod("getOGLSurfaceType", new Class[] { Graphics.class }); - getOGLSurfaceIdentifierMethod.setAccessible(true); - - // Try to get additional methods required for proper FBO support - fbObjectSupportInitialized = true; - try { - invokeWithOGLSharedContextCurrentMethod = utils.getDeclaredMethod("invokeWithOGLSharedContextCurrent", - new Class[] { - GraphicsConfiguration.class, - Runnable.class - }); - invokeWithOGLSharedContextCurrentMethod.setAccessible(true); - - getOGLSurfaceTypeMethod = utils.getDeclaredMethod("getOGLSurfaceType", - new Class[] { - Graphics.class - }); - getOGLSurfaceTypeMethod.setAccessible(true); - } catch (Exception e) { - fbObjectSupportInitialized = false; - if (DEBUG) { - e.printStackTrace(); - System.err.println("Info: Disabling Java2D/JOGL FBO support"); - } - } - - // Try to get an additional method for FBO support in recent Mustang builds - try { - getOGLTextureTypeMethod = utils.getDeclaredMethod("getOGLTextureType", - new Class[] { - Graphics.class - }); - getOGLTextureTypeMethod.setAccessible(true); - } catch (Exception e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println("Info: GL_ARB_texture_rectangle FBO support disabled"); - } - } - - // Try to set up APIs for enabling the bridge on OS X, - // where it isn't possible to create generalized - // external GLDrawables - Class<?> cglSurfaceData = null; - try { - cglSurfaceData = Class.forName("sun.java2d.opengl.CGLSurfaceData"); - } catch (Exception e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println("Info: Unable to find class sun.java2d.opengl.CGLSurfaceData for OS X"); - } - } - if (cglSurfaceData != null) { - // FIXME: for now, assume that FBO support is not enabled on OS X - fbObjectSupportInitialized = false; - - // We need to find these methods in order to make the bridge work on OS X - createOGLContextOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("createOGLContextOnSurface", - new Class[] { - Graphics.class, - Long.TYPE - }); - createOGLContextOnSurfaceMethod.setAccessible(true); - - makeOGLContextCurrentOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("makeOGLContextCurrentOnSurface", - new Class[] { - Graphics.class, - Long.TYPE - }); - makeOGLContextCurrentOnSurfaceMethod.setAccessible(true); - - destroyOGLContextMethod = cglSurfaceData.getDeclaredMethod("destroyOGLContext", - new Class[] { - Long.TYPE - }); - destroyOGLContextMethod.setAccessible(true); + getOGLSurfaceTypeMethod.setAccessible(true); + } catch (Exception e) { + fbObjectSupportInitialized = false; + if (DEBUG) { + e.printStackTrace(); + System.err.println("Info: Disabling Java2D/JOGL FBO support"); + } + } + + // Try to get an additional method for FBO support in recent Mustang builds + try { + getOGLTextureTypeMethod = utils.getDeclaredMethod("getOGLTextureType", + new Class[] { + Graphics.class + }); + getOGLTextureTypeMethod.setAccessible(true); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + System.err.println("Info: GL_ARB_texture_rectangle FBO support disabled"); + } + } + + // Try to set up APIs for enabling the bridge on OS X, + // where it isn't possible to create generalized + // external GLDrawables + Class<?> cglSurfaceData = null; + try { + cglSurfaceData = Class.forName("sun.java2d.opengl.CGLSurfaceData"); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + System.err.println("Info: Unable to find class sun.java2d.opengl.CGLSurfaceData for OS X"); + } + } + if (cglSurfaceData != null) { + // FIXME: for now, assume that FBO support is not enabled on OS X + fbObjectSupportInitialized = false; + + // We need to find these methods in order to make the bridge work on OS X + createOGLContextOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("createOGLContextOnSurface", + new Class[] { + Graphics.class, + Long.TYPE + }); + createOGLContextOnSurfaceMethod.setAccessible(true); + + makeOGLContextCurrentOnSurfaceMethod = cglSurfaceData.getDeclaredMethod("makeOGLContextCurrentOnSurface", + new Class[] { + Graphics.class, + Long.TYPE + }); + makeOGLContextCurrentOnSurfaceMethod.setAccessible(true); + + destroyOGLContextMethod = cglSurfaceData.getDeclaredMethod("destroyOGLContext", + new Class[] { + Long.TYPE + }); + destroyOGLContextMethod.setAccessible(true); + } } } catch (Exception e) { catched = e; @@ -252,6 +274,7 @@ public class Java2D { System.err.println("Info: Disabling Java2D/JOGL integration"); } isOGLPipelineActive = false; + isOGLPipelineResourceCompatible = false; } } } catch (HeadlessException e) { @@ -265,7 +288,7 @@ public class Java2D { if(null != catched) { catched.printStackTrace(); } - System.err.println("JOGL/Java2D integration " + (isOGLPipelineActive ? "enabled" : "disabled")); + System.err.println("JOGL/Java2D OGL Pipeline active " + isOGLPipelineActive + ", resourceCompatible "+isOGLPipelineResourceCompatible); } return null; } @@ -276,6 +299,10 @@ public class Java2D { return isOGLPipelineActive; } + public static boolean isOGLPipelineResourceCompatible() { + return isOGLPipelineResourceCompatible; + } + public static boolean isFBOEnabled() { return fbObjectSupportInitialized; } @@ -291,7 +318,7 @@ public class Java2D { throw (InternalError) new InternalError().initCause(e); } } - + /** Makes current the OpenGL context associated with the passed Graphics object and runs the given Runnable on the Queue Flushing Thread in one atomic action. */ @@ -330,7 +357,7 @@ public class Java2D { false if the passed GraphicsConfiguration was not an OpenGL GraphicsConfiguration. */ public static boolean invokeWithOGLSharedContextCurrent(GraphicsConfiguration g, Runnable r) throws GLException { - checkActive(); + checkCompatible(); try { AWTUtil.lockToolkit(); @@ -355,7 +382,7 @@ public class Java2D { public static Rectangle getOGLViewport(Graphics g, int componentWidth, int componentHeight) { - checkActive(); + checkCompatible(); try { return (Rectangle) getOGLViewportMethod.invoke(null, new Object[] {g, @@ -375,7 +402,7 @@ public class Java2D { passed to a call to glScissor(). Should only be called from the Queue Flusher Thread. */ public static Rectangle getOGLScissorBox(Graphics g) { - checkActive(); + checkCompatible(); try { return (Rectangle) getOGLScissorBoxMethod.invoke(null, new Object[] {g}); @@ -393,7 +420,7 @@ public class Java2D { created (and the old ones destroyed). Should only be called from the Queue Flusher Thread.*/ public static Object getOGLSurfaceIdentifier(Graphics g) { - checkActive(); + checkCompatible(); try { return getOGLSurfaceIdentifierMethod.invoke(null, new Object[] {g}); @@ -408,7 +435,7 @@ public class Java2D { object. This indicates, in particular, whether Java2D is currently rendering into a pbuffer or FBO. */ public static int getOGLSurfaceType(Graphics g) { - checkActive(); + checkCompatible(); try { // FIXME: fallback path for pre-b73 (?) Mustang builds -- remove @@ -429,7 +456,7 @@ public class Java2D { object assuming it is rendering to an FBO. Returns either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB. */ public static int getOGLTextureType(Graphics g) { - checkActive(); + checkCompatible(); if (getOGLTextureTypeMethod == null) { return GL.GL_TEXTURE_2D; @@ -483,7 +510,7 @@ public class Java2D { associated with the given Graphics object, sharing textures and display lists with the specified (CGLContextObj) share context. */ public static long createOGLContextOnSurface(Graphics g, long shareCtx) { - checkActive(); + checkCompatible(); try { return ((Long) createOGLContextOnSurfaceMethod.invoke(null, new Object[] { g, new Long(shareCtx) })).longValue(); @@ -497,7 +524,7 @@ public class Java2D { /** (Mac OS X-specific) Makes the given OpenGL context current on the surface associated with the given Graphics object. */ public static boolean makeOGLContextCurrentOnSurface(Graphics g, long ctx) { - checkActive(); + checkCompatible(); try { return ((Boolean) makeOGLContextCurrentOnSurfaceMethod.invoke(null, new Object[] { g, new Long(ctx) })).booleanValue(); @@ -510,7 +537,7 @@ public class Java2D { /** (Mac OS X-specific) Destroys the given OpenGL context. */ public static void destroyOGLContext(long ctx) { - checkActive(); + checkCompatible(); try { destroyOGLContextMethod.invoke(null, new Object[] { new Long(ctx) }); @@ -527,12 +554,19 @@ public class Java2D { private static void checkActive() { if (!isOGLPipelineActive()) { - throw new GLException("Java2D OpenGL pipeline not active (or necessary support not present)"); + throw new GLException("Java2D OpenGL pipeline not active"); + } + } + + private static void checkCompatible() { + if ( !isOGLPipelineResourceCompatible() ) { + throw new GLException("Java2D OpenGL pipeline not resource compatible"); } } private static int getOGLUtilitiesIntField(final String name) { Integer i = AccessController.doPrivileged(new PrivilegedAction<Integer>() { + @Override public Integer run() { try { Class<?> utils = Class.forName("sun.java2d.opengl.OGLUtilities"); @@ -562,7 +596,7 @@ public class Java2D { // Note 2: the first execution of this method must not be from the // Java2D Queue Flusher Thread. - if (isOGLPipelineActive() && + if (isOGLPipelineResourceCompatible() && isFBOEnabled() && !initializedJ2DFBOShareContext) { @@ -576,6 +610,7 @@ public class Java2D { System.err.println("Starting initialization of J2D FBO share context"); } invokeWithOGLSharedContextCurrent(device.getDefaultConfiguration(), new Runnable() { + @Override public void run() { j2dFBOShareContext = GLDrawableFactory.getFactory(GLProfile.getDefault(GLProfile.getDefaultDevice())).createExternalGLContext(); } diff --git a/src/jogl/classes/jogamp/opengl/awt/Java2DGLContext.java b/src/jogl/classes/jogamp/opengl/awt/Java2DGLContext.java deleted file mode 100644 index 4a5b1db54..000000000 --- a/src/jogl/classes/jogamp/opengl/awt/Java2DGLContext.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.awt; - -import jogamp.opengl.*; -import java.awt.Graphics; - -/** Provides a construct by which the shared GLJPanel code can - * interact with a few methods in the Mac OS X-specific Java2D/JOGL - * bridge implementation. - */ - -public interface Java2DGLContext { - public void setGraphics(Graphics g); -} diff --git a/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java b/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java index 55fb3f9a2..9173a38cb 100644 --- a/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java +++ b/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java @@ -42,7 +42,7 @@ public class VersionApplet extends Applet { va.init(); frame.add(va, BorderLayout.CENTER); frame.validate(); - + frame.setVisible(true); va.start(); } @@ -54,6 +54,7 @@ public class VersionApplet extends Applet { this.f = f; this.va = va; } + @Override public void windowClosing(WindowEvent ev) { f.setVisible(false); va.stop(); @@ -68,7 +69,7 @@ public class VersionApplet extends Applet { if(null != canvas) { return; } setEnabled(true); - + GLProfile glp = GLProfile.getDefault(); GLCapabilities glcaps = new GLCapabilities(glp); @@ -87,7 +88,7 @@ public class VersionApplet extends Applet { /* s = NativeWindowVersion.getInstance().toString(); System.err.println(s); - tareaVersion.append(NativeWindowVersion.getInstance().toString()); + tareaVersion.append(NativeWindowVersion.getInstance().toString()); */ s = JoglVersion.getInstance().toString(); @@ -129,24 +130,28 @@ public class VersionApplet extends Applet { } } + @Override public void init() { System.err.println("VersionApplet: init() - begin"); my_init(); System.err.println("VersionApplet: init() - end"); } + @Override public void start() { System.err.println("VersionApplet: start() - begin"); canvas.setVisible(true); System.err.println("VersionApplet: start() - end"); } + @Override public void stop() { System.err.println("VersionApplet: stop() - begin"); canvas.setVisible(false); System.err.println("VersionApplet: stop() - end"); } + @Override public void destroy() { System.err.println("VersionApplet: destroy() - start"); my_release(); @@ -154,6 +159,7 @@ public class VersionApplet extends Applet { } class GLInfo implements GLEventListener { + @Override public void init(GLAutoDrawable drawable) { GL gl = drawable.getGL(); String s = JoglVersion.getGLInfo(gl, null).toString(); @@ -161,12 +167,15 @@ public class VersionApplet extends Applet { tareaVersion.append(s); } + @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } + @Override public void display(GLAutoDrawable drawable) { } + @Override public void dispose(GLAutoDrawable drawable) { } } diff --git a/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java index cddd142e9..7b85c3e75 100644 --- a/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/DesktopES2DynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package jogamp.opengl.egl; import java.util.*; @@ -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,35 +47,29 @@ 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; } - + @Override public final List<String> getToolGetProcAddressFuncNameList() { List<String> res = new ArrayList<String>(); res.add("eglGetProcAddress"); return res; } + @Override public final long toolGetProcAddress(long toolGetProcAddressHandle, String funcName) { return EGL.eglGetProcAddress(toolGetProcAddressHandle, funcName); } + @Override public final boolean useToolGetProcAdressFirst(String funcName) { return true; } - - public List<List<String>> getToolLibNames() { + + @Override + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> libsGL = new ArrayList<String>(); - - // Be aware that on DRI systems, eg ATI fglrx, etc, + + // Be aware that on DRI systems, eg ATI fglrx, etc, // you have to set LIBGL_DRIVERS_PATH env variable. // Eg on Ubuntu 64bit systems this is: // export LIBGL_DRIVERS_PATH=/usr/lib/fglrx/dri:/usr/lib32/fglrx/dri @@ -92,15 +86,16 @@ public class DesktopES2DynamicLibraryBundleInfo extends GLDynamicLibraryBundleIn // OSX (guess ES2 on OSX will never happen) libsGL.add("/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"); - + // last but not least .. the generic one libsGL.add("GL"); - + libsList.add(libsGL); return libsList; - } - + } + + @Override public final List<String> getGlueLibNames() { return glueLibNames; - } + } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLContext.java b/src/jogl/classes/jogamp/opengl/egl/EGLContext.java index f5f9f62c4..f89ded4f6 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLContext.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLContext.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2011 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,7 +42,9 @@ import java.util.Map; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; @@ -50,11 +52,12 @@ import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableImpl; import com.jogamp.common.nio.Buffers; -import com.jogamp.common.os.Platform; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; +import com.jogamp.opengl.GLRendererQuirks; -public abstract class EGLContext extends GLContextImpl { +public class EGLContext extends GLContextImpl { private boolean eglQueryStringInitialized; private boolean eglQueryStringAvailable; private EGLExt _eglExt; @@ -68,14 +71,15 @@ public abstract class EGLContext extends GLContextImpl { } @Override - protected void resetStates() { + protected void resetStates(boolean isInit) { eglQueryStringInitialized = false; eglQueryStringAvailable = false; eglExtProcAddressTable = null; // no inner state _eglExt = null; - super.resetStates(); + super.resetStates(isInit); } - + + @Override public Object getPlatformGLExtensions() { return getEGLExt(); } @@ -87,6 +91,7 @@ public abstract class EGLContext extends GLContextImpl { return _eglExt; } + @Override public final ProcAddressTable getPlatformExtProcAddressTable() { return eglExtProcAddressTable; } @@ -95,75 +100,80 @@ public abstract class EGLContext extends GLContextImpl { return eglExtProcAddressTable; } + @Override protected Map<String, String> getFunctionNameMap() { return null; } + @Override protected Map<String, String> getExtensionNameMap() { return null; } + @Override public final boolean isGLReadDrawableAvailable() { return true; } + @Override protected void makeCurrentImpl() throws GLException { - if(EGL.EGL_NO_DISPLAY==((EGLDrawable)drawable).getDisplay() ) { - throw new GLException("drawable not properly initialized, NO DISPLAY: "+drawable); - } if (EGL.eglGetCurrentContext() != contextHandle) { - if (!EGL.eglMakeCurrent(((EGLDrawable)drawable).getDisplay(), - drawable.getHandle(), - drawableRead.getHandle(), - contextHandle)) { - throw new GLException("Error making context 0x" + - Long.toHexString(contextHandle) + " current: error code 0x" + Integer.toHexString(EGL.eglGetError())); + final long dpy = drawable.getNativeSurface().getDisplayHandle(); + if (!EGL.eglMakeCurrent(dpy, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + throw new GLException("Error making context " + toHexString(contextHandle) + + " current on Thread " + getThreadName() + + " with display " + toHexString(dpy) + + ", drawableWrite " + toHexString(drawable.getHandle()) + + ", drawableRead "+ toHexString(drawableRead.getHandle()) + + " - Error code " + toHexString(EGL.eglGetError()) + ", " + this); } } } + @Override protected void releaseImpl() throws GLException { - if (!EGL.eglMakeCurrent(((EGLDrawable)drawable).getDisplay(), - EGL.EGL_NO_SURFACE, - EGL.EGL_NO_SURFACE, - EGL.EGL_NO_CONTEXT)) { - throw new GLException("Error freeing OpenGL context 0x" + - Long.toHexString(contextHandle) + ": error code 0x" + Integer.toHexString(EGL.eglGetError())); + if (!EGL.eglMakeCurrent(drawable.getNativeSurface().getDisplayHandle(), EGL.EGL_NO_SURFACE, EGL.EGL_NO_SURFACE, EGL.EGL_NO_CONTEXT)) { + throw new GLException("Error freeing OpenGL context " + toHexString(contextHandle) + + ": error code " + toHexString(EGL.eglGetError())); } } + @Override protected void destroyImpl() throws GLException { - if (!EGL.eglDestroyContext(((EGLDrawable)drawable).getDisplay(), contextHandle)) { - final int eglError = EGL.eglGetError(); - if(EGL.EGL_SUCCESS != eglError) { /* oops, Mesa EGL impl. may return false, but has no EGL error */ - throw new GLException("Error destroying OpenGL context 0x" + - Long.toHexString(contextHandle) + ": error code 0x" + Integer.toHexString(eglError)); - } - } + destroyContextARBImpl(contextHandle); } + @Override protected long createContextARBImpl(long share, boolean direct, int ctp, int major, int minor) { return 0; // FIXME } + @Override protected void destroyContextARBImpl(long _context) { - // FIXME + if (!EGL.eglDestroyContext(drawable.getNativeSurface().getDisplayHandle(), _context)) { + final int eglError = EGL.eglGetError(); + if(EGL.EGL_SUCCESS != eglError) { /* oops, Mesa EGL impl. may return false, but has no EGL error */ + throw new GLException("Error destroying OpenGL context " + toHexString(_context) + + ": error code " + toHexString(eglError)); + } + } } - protected boolean createImpl(GLContextImpl shareWith) throws GLException { - long eglDisplay = ((EGLDrawable)drawable).getDisplay(); - EGLGraphicsConfiguration config = ((EGLDrawable)drawable).getGraphicsConfiguration(); - GLProfile glProfile = drawable.getGLProfile(); - long eglConfig = config.getNativeConfig(); - long shareWithHandle = EGL.EGL_NO_CONTEXT; - - if (eglDisplay == 0) { + @Override + protected boolean createImpl(final long shareWithHandle) throws GLException { + final EGLGraphicsConfiguration config = (EGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); + final long eglDisplay = config.getScreen().getDevice().getHandle(); + final GLProfile glProfile = drawable.getGLProfile(); + final long eglConfig = config.getNativeConfig(); + // 0 == EGL.EGL_NO_CONTEXT; + + if ( 0 == eglDisplay ) { throw new GLException("Error: attempted to create an OpenGL context without a display connection"); } - if (eglConfig == 0) { + if ( 0 == eglConfig ) { throw new GLException("Error: attempted to create an OpenGL context without a graphics configuration"); } try { // might be unavailable on EGL < 1.2 - if(!EGL.eglBindAPI(EGL.EGL_OPENGL_ES_API)) { - throw new GLException("Catched: eglBindAPI to ES failed , error 0x"+Integer.toHexString(EGL.eglGetError())); + if( !EGL.eglBindAPI(EGL.EGL_OPENGL_ES_API) ) { + throw new GLException("Catched: eglBindAPI to ES failed , error "+toHexString(EGL.eglGetError())); } } catch (GLException glex) { if (DEBUG) { @@ -171,26 +181,29 @@ public abstract class EGLContext extends GLContextImpl { } } - if (shareWith != null) { - shareWithHandle = shareWith.getHandle(); - if (shareWithHandle == 0) { - throw new GLException("GLContextShareSet returned an invalid OpenGL context"); - } - } + // Cannot check extension 'EGL_KHR_create_context' before having one current! - final IntBuffer contextAttrsNIO; + 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; + if( GLRendererQuirks.existStickyDeviceQuirk( GLDrawableFactory.getEGLFactory().getDefaultDevice(), GLRendererQuirks.GLES3ViaEGLES2Config) ) { + contextVersionAttr = 2; + } else { + contextVersionAttr = 3; + } + } 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); } + // EGLExt.EGL_CONTEXT_MAJOR_VERSION_KHR == EGL.EGL_CONTEXT_CLIENT_VERSION + 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); @@ -200,31 +213,32 @@ public abstract class EGLContext extends GLContextImpl { } if (DEBUG) { System.err.println(getThreadName() + ": Created OpenGL context 0x" + - Long.toHexString(contextHandle) + + Long.toHexString(contextHandle) + ",\n\twrite surface 0x" + Long.toHexString(drawable.getHandle()) + ",\n\tread surface 0x" + Long.toHexString(drawableRead.getHandle())+ ",\n\t"+this+ ",\n\tsharing with 0x" + Long.toHexString(shareWithHandle)); } - if (!EGL.eglMakeCurrent(((EGLDrawable)drawable).getDisplay(), - drawable.getHandle(), - drawableRead.getHandle(), - contextHandle)) { - throw new GLException("Error making context 0x" + - Long.toHexString(contextHandle) + " current: error code " + EGL.eglGetError()); + if (!EGL.eglMakeCurrent(eglDisplay, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + throw new GLException("Error making context " + + toHexString(contextHandle) + " current: error code " + toHexString(EGL.eglGetError())); } - int ctp = CTX_PROFILE_ES; - int major; - if(glProfile.usesNativeGLES2()) { - ctp |= CTX_IMPL_ES2_COMPAT; - major = 2; - } else { - major = 1; + if( !setGLFunctionAvailability(true, contextVersionReq, 0, CTX_PROFILE_ES, + true /* strictMatch */, // always req. strict match + false /* withinGLVersionsMapping */) ) { + if(DEBUG) { + System.err.println(getThreadName() + ": createImpl: setGLFunctionAvailability FAILED delete "+toHexString(contextHandle)); + } + EGL.eglMakeCurrent(drawable.getNativeSurface().getDisplayHandle(), EGL.EGL_NO_SURFACE, EGL.EGL_NO_SURFACE, EGL.EGL_NO_CONTEXT); + EGL.eglDestroyContext(drawable.getNativeSurface().getDisplayHandle(), contextHandle); + contextHandle = 0; + return false; + } else { + return true; } - setGLFunctionAvailability(true, major, 0, ctp); - return true; } + @Override protected final void updateGLXProcAddressTable() { final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); @@ -255,17 +269,16 @@ public abstract class EGLContext extends GLContextImpl { } } } - + + @Override protected final StringBuilder getPlatformExtensionsStringImpl() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); if (!eglQueryStringInitialized) { - eglQueryStringAvailable = - getDrawableImpl().getGLDynamicLookupHelper().dynamicLookupFunction("eglQueryString") != 0; + eglQueryStringAvailable = getDrawableImpl().getGLDynamicLookupHelper().isFunctionAvailable("eglQueryString"); eglQueryStringInitialized = true; } if (eglQueryStringAvailable) { - final String ret = EGL.eglQueryString(((EGLDrawable)drawable).getDisplay(), - EGL.EGL_EXTENSIONS); + final String ret = EGL.eglQueryString(drawable.getNativeSurface().getDisplayHandle(), EGL.EGL_EXTENSIONS); if (DEBUG) { System.err.println("EGL extensions: " + ret); } @@ -276,22 +289,63 @@ public abstract class EGLContext extends GLContextImpl { @Override protected boolean setSwapIntervalImpl(int interval) { - // FIXME !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // eglSwapInterval(..) issued: - // Android 4.0.3 / Pandaboard ES / PowerVR SGX 540: crashes - // FIXME !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - if( Platform.OSType.ANDROID == Platform.getOSType() && getGLRendererString(true).contains("powervr") ) { - if(DEBUG) { - System.err.println("Ignored: eglSwapInterval("+interval+") - cause: OS "+Platform.getOSType() + " / Renderer " + getGLRendererString(false)); - } + if( hasRendererQuirk(GLRendererQuirks.NoSetSwapInterval) ) { return false; } - return EGL.eglSwapInterval(((EGLDrawable)drawable).getDisplay(), interval); + return EGL.eglSwapInterval(drawable.getNativeSurface().getDisplayHandle(), interval); } - public abstract void bindPbufferToTexture(); + // + // Accessible .. + // - public abstract void releasePbufferFromTexture(); + /* pp */ void mapCurrentAvailableGLVersion(AbstractGraphicsDevice device) { + mapStaticGLVersion(device, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + } + /* pp */ int getContextOptions() { return ctxOptions; } + /* pp */ static void mapStaticGLESVersion(AbstractGraphicsDevice device, GLCapabilitiesImmutable caps) { + final GLProfile glp = caps.getGLProfile(); + final int[] reqMajorCTP = new int[2]; + GLContext.getRequestMajorAndCompat(glp, reqMajorCTP); + 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() ) { + reqMajorCTP[1] |= GLContext.CTX_IMPL_ACCEL_SOFT; + } + mapStaticGLVersion(device, reqMajorCTP[0], 0, reqMajorCTP[1]); + } + /* pp */ static void mapStaticGLVersion(AbstractGraphicsDevice device, int major, int minor, int ctp) { + if( 0 != ( ctp & GLContext.CTX_PROFILE_ES) ) { + // ES1, ES2, ES3, .. + mapStaticGLVersion(device, major /* reqMajor */, major, minor, ctp); + if( 3 == major ) { + // map ES2 -> ES3 + mapStaticGLVersion(device, 2 /* reqMajor */, major, minor, ctp); + } + } + } + private static void mapStaticGLVersion(AbstractGraphicsDevice device, int reqMajor, int major, int minor, int ctp) { + GLContext.mapAvailableGLVersion(device, reqMajor, GLContext.CTX_PROFILE_ES, major, minor, ctp); + if(! ( device instanceof EGLGraphicsDevice ) ) { + final EGLGraphicsDevice eglDevice = new EGLGraphicsDevice(device.getHandle(), EGL.EGL_NO_DISPLAY, device.getConnection(), device.getUnitID(), null); + GLContext.mapAvailableGLVersion(eglDevice, reqMajor, GLContext.CTX_PROFILE_ES, major, minor, ctp); + } + } + protected static String getGLVersion(int major, int minor, int ctp, String gl_version) { + return GLContext.getGLVersion(major, minor, ctp, gl_version); + } + + protected static boolean getAvailableGLVersionsSet(AbstractGraphicsDevice device) { + return GLContext.getAvailableGLVersionsSet(device); + } + protected static void setAvailableGLVersionsSet(AbstractGraphicsDevice device) { + GLContext.setAvailableGLVersionsSet(device); + } protected static String toHexString(int hex) { return GLContext.toHexString(hex); @@ -299,25 +353,23 @@ public abstract class EGLContext extends GLContextImpl { protected static String toHexString(long hex) { return GLContext.toHexString(hex); } - + //---------------------------------------------------------------------- // Currently unimplemented stuff // + @Override protected void copyImpl(GLContext source, int mask) throws GLException { throw new GLException("Not yet implemented"); } - - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { - throw new GLException("Should not call this"); - } - - public boolean offscreenImageNeedsVerticalFlip() { + @Override + public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) { throw new GLException("Should not call this"); } - public int getOffscreenContextPixelDataType() { + @Override + public final void glFreeMemoryNV(ByteBuffer pointer) { throw new GLException("Should not call this"); } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java b/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java index e09400c09..c5f76f667 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDisplayUtil.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -29,105 +29,310 @@ package jogamp.opengl.egl; import java.nio.IntBuffer; +import java.util.Iterator; +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.ToolkitLock; +import javax.media.opengl.GLException; import jogamp.opengl.Debug; -import com.jogamp.common.util.LongIntHashMap; +import com.jogamp.common.util.LongObjectHashMap; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; -/** - * This implementation provides recursive calls to +/** + * This implementation provides recursive calls to * {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)} and {@link EGL#eglTerminate(long)}, * where <code>eglInitialize(..)</code> is issued only for the 1st call per <code>eglDisplay</code> * and <code>eglTerminate(..)</code> is issued only for the last call. * <p> * This class is required, due to implementation bugs within EGL where {@link EGL#eglTerminate(long)} - * does not mark the resource for deletion when still in use, bug releases them immediatly. + * does not mark the resource for deletion when still in use, bug releases them immediately. * </p> */ public class EGLDisplayUtil { - protected static final boolean DEBUG = Debug.debug("EGL"); - - static LongIntHashMap eglDisplayCounter; - + private static final boolean DEBUG = Debug.debug("EGLDisplayUtil"); + private static boolean useSingletonEGLDisplay = false; + private static EGLDisplayRef singletonEGLDisplay = null; + + private static class EGLDisplayRef { + final long eglDisplay; + final Throwable createdStack; + int initRefCount; + + /** + * Returns an already opened {@link EGLDisplayRef} or opens a new {@link EGLDisplayRef}. + * <p> + * Opened {@link EGLDisplayRef}s are mapped against their <code>eglDisplay</code> handle. + * </p> + * <p> + * Method utilizes {@link EGLDisplayRef}'s reference counter, i.e. increases it. + * </p> + * <p> + * An {@link EGLDisplayRef} is <i>opened</i> via {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)}. + * </p> + */ + static EGLDisplayRef getOrCreateOpened(final long eglDisplay, final IntBuffer major, final IntBuffer minor) { + EGLDisplayRef o = (EGLDisplayRef) openEGLDisplays.get(eglDisplay); + if( null == o ) { + if( EGL.eglInitialize(eglDisplay, major, minor) ) { + final EGLDisplayRef n = new EGLDisplayRef(eglDisplay); + openEGLDisplays.put(eglDisplay, n); + n.initRefCount++; + if( null == singletonEGLDisplay ) { + singletonEGLDisplay = n; + } + return n; + } else { + return null; + } + } else { + o.initRefCount++; + return o; + } + } + + /** + * Closes an already opened {@link EGLDisplayRef}. + * <p> + * Method decreases a reference counter and closes the {@link EGLDisplayRef} if it reaches zero. + * </p> + * <p> + * An {@link EGLDisplayRef} is <i>closed</i> via {@link EGL#eglTerminate(long)}. + * </p> + */ + static EGLDisplayRef closeOpened(final long eglDisplay, final boolean[] res) { + final EGLDisplayRef o = (EGLDisplayRef) openEGLDisplays.get(eglDisplay); + res[0] = true; + if( null != o ) { + if( 0 < o.initRefCount ) { // no negative refCount + o.initRefCount--; + if( 0 == o.initRefCount ) { + res[0] = EGL.eglTerminate(eglDisplay); + if( o == singletonEGLDisplay ) { + singletonEGLDisplay = null; + } + } + } + if( 0 >= o.initRefCount ) { + openEGLDisplays.remove(eglDisplay); + } + } + return o; + } + + private EGLDisplayRef(long eglDisplay) { + this.eglDisplay = eglDisplay; + this.initRefCount = 0; + this.createdStack = DEBUG ? new Throwable() : null; + } + + @Override + public String toString() { + return "EGLDisplayRef[0x"+Long.toHexString(eglDisplay)+": refCnt "+initRefCount+"]"; + } + } + private static final LongObjectHashMap openEGLDisplays; + static { - eglDisplayCounter = new LongIntHashMap(); - eglDisplayCounter.setKeyNotFoundValue(0); + openEGLDisplays = new LongObjectHashMap(); + openEGLDisplays.setKeyNotFoundValue(null); + } + + /** + * @return number of unclosed EGL Displays.<br> + */ + public static int shutdown(boolean verbose) { + if(DEBUG || verbose || openEGLDisplays.size() > 0 ) { + System.err.println("EGLDisplayUtil.EGLDisplays: Shutdown (open: "+openEGLDisplays.size()+")"); + if(DEBUG) { + Thread.dumpStack(); + } + if( openEGLDisplays.size() > 0) { + dumpOpenDisplayConnections(); + } + } + return openEGLDisplays.size(); + } + + public static void dumpOpenDisplayConnections() { + System.err.println("EGLDisplayUtil: Open EGL Display Connections: "+openEGLDisplays.size()); + int i=0; + for(Iterator<LongObjectHashMap.Entry> iter = openEGLDisplays.iterator(); iter.hasNext(); i++) { + final LongObjectHashMap.Entry e = iter.next(); + final EGLDisplayRef v = (EGLDisplayRef) e.value; + System.err.println("EGLDisplayUtil: Open["+i+"]: 0x"+Long.toHexString(e.key)+": "+v); + if(null != v.createdStack) { + v.createdStack.printStackTrace(); + } + } } - public static long eglGetDisplay(long nativeDisplay_id) { + /* pp */ static synchronized void setSingletonEGLDisplayOnly(boolean v) { useSingletonEGLDisplay = v; } + + private static synchronized long eglGetDisplay(long nativeDisplay_id) { + if( useSingletonEGLDisplay && null != singletonEGLDisplay ) { + if(DEBUG) { + System.err.println("EGLDisplayUtil.eglGetDisplay.s: eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ + EGLContext.toHexString(singletonEGLDisplay.eglDisplay)+ + ", "+((EGL.EGL_NO_DISPLAY != singletonEGLDisplay.eglDisplay)?"OK":"Failed")+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); + } + return singletonEGLDisplay.eglDisplay; + } final long eglDisplay = EGL.eglGetDisplay(nativeDisplay_id); if(DEBUG) { - System.err.println("EGLDisplayUtil.eglGetDisplay(): eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ + System.err.println("EGLDisplayUtil.eglGetDisplay.X: eglDisplay("+EGLContext.toHexString(nativeDisplay_id)+"): "+ EGLContext.toHexString(eglDisplay)+ - ", "+((EGL.EGL_NO_DISPLAY != eglDisplay)?"OK":"Failed")); + ", "+((EGL.EGL_NO_DISPLAY != eglDisplay)?"OK":"Failed")+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); } return eglDisplay; } - - public static long eglGetDisplay(NativeSurface surface, boolean allowFallBackToDefault) { - final long nDisplay; - if( NativeWindowFactory.TYPE_WINDOWS.equals(NativeWindowFactory.getNativeWindowType(false)) ) { - nDisplay = surface.getSurfaceHandle(); // don't even ask .. - } else { - nDisplay = surface.getDisplayHandle(); // 0 == EGL.EGL_DEFAULT_DISPLAY + + /** + * @param eglDisplay + * @param major + * @param minor + * @return true if the eglDisplay is valid and it's reference counter becomes one and {@link EGL#eglInitialize(long, IntBuffer, IntBuffer)} was successful, otherwise false + * + * @see EGL#eglInitialize(long, IntBuffer, IntBuffer) + */ + private static synchronized boolean eglInitialize(long eglDisplay, IntBuffer major, IntBuffer minor) { + if( EGL.EGL_NO_DISPLAY == eglDisplay) { + return false; + } + final EGLDisplayRef d = EGLDisplayRef.getOrCreateOpened(eglDisplay, major, minor); + if(DEBUG) { + System.err.println("EGLDisplayUtil.eglInitialize("+EGLContext.toHexString(eglDisplay)+" ...): "+d+" = "+(null != d)+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); + // Thread.dumpStack(); + } + return null != d; + } + + /** + * @param nativeDisplayID + * @param eglDisplay array of size 1 holding return value if successful, otherwise {@link EGL#EGL_NO_DISPLAY}. + * @param eglErr array of size 1 holding the EGL error value as retrieved by {@link EGL#eglGetError()} if not successful. + * @param major + * @param minor + * @return {@link EGL#EGL_SUCCESS} if successful, otherwise {@link EGL#EGL_BAD_DISPLAY} if {@link #eglGetDisplay(long)} failed + * or {@link EGL#EGL_NOT_INITIALIZED} if {@link #eglInitialize(long, IntBuffer, IntBuffer)} failed. + * + * @see #eglGetDisplay(long) + * @see #eglInitialize(long, IntBuffer, IntBuffer) + */ + private static synchronized int eglGetDisplayAndInitialize(long nativeDisplayID, long[] eglDisplay, int[] eglErr, IntBuffer major, IntBuffer minor) { + eglDisplay[0] = EGL.EGL_NO_DISPLAY; + final long _eglDisplay = eglGetDisplay( nativeDisplayID ); + if ( EGL.EGL_NO_DISPLAY == _eglDisplay ) { + eglErr[0] = EGL.eglGetError(); + return EGL.EGL_BAD_DISPLAY; } - long eglDisplay = EGLDisplayUtil.eglGetDisplay(nDisplay); - if (eglDisplay == EGL.EGL_NO_DISPLAY && nDisplay != EGL.EGL_DEFAULT_DISPLAY && allowFallBackToDefault) { + if ( !eglInitialize( _eglDisplay, major, minor) ) { + eglErr[0] = EGL.eglGetError(); + return EGL.EGL_NOT_INITIALIZED; + } + eglDisplay[0] = _eglDisplay; + return EGL.EGL_SUCCESS; + } + + /** + * Attempts to {@link #eglGetDisplayAndInitialize(long, long[], int[], IntBuffer, IntBuffer)} with given <code>nativeDisplayID</code>. + * If this fails, method retries with <code>nativeDisplayID</code> {@link EGL#EGL_DEFAULT_DISPLAY} - the fallback mechanism. + * The actual used <code>nativeDisplayID</code> is returned in it's in/out array. + * + * @throws GLException if {@link EGL#eglGetDisplay(long)} or {@link EGL#eglInitialize(long, int[], int, int[], int)} fails incl fallback + * @param nativeDisplayID in/out array of size 1, passing the requested nativeVisualID, may return a different revised nativeVisualID handle + * @return the initialized EGL display ID + * @throws GLException if not successful + */ + private static synchronized long eglGetDisplayAndInitialize(long[] nativeDisplayID) { + final long[] eglDisplay = new long[1]; + final int[] eglError = new int[1]; + int eglRes = EGLDisplayUtil.eglGetDisplayAndInitialize(nativeDisplayID[0], eglDisplay, eglError, null, null); + if( EGL.EGL_SUCCESS == eglRes ) { + return eglDisplay[0]; + } + if( EGL.EGL_DEFAULT_DISPLAY != nativeDisplayID[0] ) { // fallback to DEGAULT_DISPLAY if(DEBUG) { - System.err.println("EGLDisplayUtil.eglGetDisplay(): Fall back to EGL_DEFAULT_DISPLAY"); + System.err.println("EGLDisplayUtil.eglGetAndInitDisplay failed with native "+EGLContext.toHexString(nativeDisplayID[0])+", error "+EGLContext.toHexString(eglRes)+"/"+EGLContext.toHexString(eglError[0])+" - fallback!"); + } + eglRes = EGLDisplayUtil.eglGetDisplayAndInitialize(EGL.EGL_DEFAULT_DISPLAY, eglDisplay, eglError, null, null); + if( EGL.EGL_SUCCESS == eglRes ) { + nativeDisplayID[0] = EGL.EGL_DEFAULT_DISPLAY; + return eglDisplay[0]; } - eglDisplay = EGLDisplayUtil.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); } - return eglDisplay; + throw new GLException("Failed to created/initialize EGL display incl. fallback default: native "+EGLContext.toHexString(nativeDisplayID[0])+", error "+EGLContext.toHexString(eglRes)+"/"+EGLContext.toHexString(eglError[0])); } - - public static synchronized boolean eglInitialize(long eglDisplay, int[] major, int major_offset, int[] minor, int minor_offset) { - final boolean res; - final int refCnt = eglDisplayCounter.get(eglDisplay) + 1; // 0 + 1 = 1 -> 1st init - if(1==refCnt) { - res = EGL.eglInitialize(eglDisplay, major, major_offset, minor, minor_offset); - } else { - res = true; + + /** + * @param eglDisplay the EGL display handle + * @return true if the eglDisplay is valid and it's reference counter becomes zero and {@link EGL#eglTerminate(long)} was successful, otherwise false + */ + private static synchronized boolean eglTerminate(long eglDisplay) { + if( EGL.EGL_NO_DISPLAY == eglDisplay) { + return false; } - eglDisplayCounter.put(eglDisplay, refCnt); + final boolean[] res = new boolean[1]; + final EGLDisplayRef d = EGLDisplayRef.closeOpened(eglDisplay, res); if(DEBUG) { - System.err.println("EGL.eglInitialize(0x"+Long.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res); + System.err.println("EGLDisplayUtil.eglTerminate.X("+EGLContext.toHexString(eglDisplay)+" ...): "+d+" = "+res[0]+", singletonEGLDisplay "+singletonEGLDisplay+" (use "+useSingletonEGLDisplay+")"); + // Thread.dumpStack(); } - return res; + return res[0]; } - - public static synchronized boolean eglInitialize(long eglDisplay, IntBuffer major, IntBuffer minor) { - final boolean res; - final int refCnt = eglDisplayCounter.get(eglDisplay) + 1; // 0 + 1 = 1 -> 1st init - if(1==refCnt) { // only initialize once - res = EGL.eglInitialize(eglDisplay, major, minor); - } else { - res = true; + + private static final EGLGraphicsDevice.EGLDisplayLifecycleCallback eglLifecycleCallback = new EGLGraphicsDevice.EGLDisplayLifecycleCallback() { + @Override + public long eglGetAndInitDisplay(long[] nativeDisplayID) { + return eglGetDisplayAndInitialize(nativeDisplayID); } - eglDisplayCounter.put(eglDisplay, refCnt); - if(DEBUG) { - System.err.println("EGL.eglInitialize(0x"+Long.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res); + @Override + public void eglTerminate(long eglDisplayHandle) { + EGLDisplayUtil.eglTerminate(eglDisplayHandle); } - return res; + }; + + /** + * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. + * <p> + * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation + * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. + * </p> + * <p> + * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. + * </p> + * @param nativeDisplayID + * @param connection + * @param unitID + * @return an uninitialized {@link EGLGraphicsDevice} + */ + public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(long nativeDisplayID, String connection, int unitID) { + return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, connection, unitID, eglLifecycleCallback); } - - public static synchronized boolean eglTerminate(long eglDisplay) { - final boolean res; - final int refCnt = eglDisplayCounter.get(eglDisplay) - 1; // 1 - 1 = 0 -> final terminate - if(0==refCnt) { // no terminate if still in use or already terminated - res = EGL.eglTerminate(eglDisplay); + + /** + * Returns an uninitialized {@link EGLGraphicsDevice}. User needs to issue {@link EGLGraphicsDevice#open()} before usage. + * <p> + * Using {@link #eglGetDisplayAndInitialize(long[])} for the {@link EGLGraphicsDevice#open()} implementation + * and {@link #eglTerminate(long)} for {@link EGLGraphicsDevice#close()}. + * </p> + * <p> + * Using the default {@link ToolkitLock}, via {@link NativeWindowFactory#getDefaultToolkitLock(String, long)}. + * </p> + * @param surface + * @return an uninitialized EGLGraphicsDevice + */ + public static EGLGraphicsDevice eglCreateEGLGraphicsDevice(NativeSurface surface) { + final long nativeDisplayID; + if( NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false) ) { + nativeDisplayID = surface.getSurfaceHandle(); // don't even ask .. } else { - res = true; - } - if(0<=refCnt) { // no negative refCount - eglDisplayCounter.put(eglDisplay, refCnt); - } - if(DEBUG) { - System.err.println("EGL.eglTerminate(0x"+Long.toHexString(eglDisplay)+" ...): #"+refCnt+" = "+res); + nativeDisplayID = surface.getDisplayHandle(); // 0 == EGL.EGL_DEFAULT_DISPLAY } - return res; + final AbstractGraphicsDevice adevice = surface.getGraphicsConfiguration().getScreen().getDevice(); + return new EGLGraphicsDevice(nativeDisplayID, EGL.EGL_NO_DISPLAY, adevice.getConnection(), adevice.getUnitID(), eglLifecycleCallback); } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java index b2119d728..f184edae3 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,229 +29,146 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.egl; -import jogamp.opengl.GLDynamicLookupHelper; -import jogamp.opengl.GLDrawableImpl; +import java.nio.IntBuffer; -import javax.media.nativewindow.*; -import javax.media.nativewindow.VisualIDHolder.VIDType; -import javax.media.opengl.*; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.ProxySurface; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; + +import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLDynamicLookupHelper; -import com.jogamp.nativewindow.egl.*; +import com.jogamp.common.nio.Buffers; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; public abstract class EGLDrawable extends GLDrawableImpl { - protected boolean ownEGLDisplay = false; // for destruction - protected boolean ownEGLSurface = false; // for destruction - private EGLGraphicsConfiguration eglConfig; - protected long eglDisplay; - protected long eglSurface; - protected EGLDrawable(EGLDrawableFactory factory, - NativeSurface component) throws GLException { + protected EGLDrawable(EGLDrawableFactory factory, NativeSurface component) throws GLException { super(factory, component, false); - eglSurface=EGL.EGL_NO_SURFACE; - eglDisplay=0; - } - - public long getDisplay() { - return eglDisplay; - } - - public long getHandle() { - return eglSurface; - } - - public EGLGraphicsConfiguration getGraphicsConfiguration() { - return eglConfig; - } - - public GLCapabilitiesImmutable getChosenGLCapabilities() { - return (null==eglConfig)?super.getChosenGLCapabilities():(GLCapabilitiesImmutable)eglConfig.getChosenCapabilities(); } + @Override public abstract GLContext createContext(GLContext shareWith); - protected abstract long createSurface(long eglDpy, long eglNativeCfg, long surfaceHandle); + protected abstract long createSurface(EGLGraphicsConfiguration config, int width, int height, long nativeSurfaceHandle); - private void recreateSurface() { - // create a new EGLSurface .. - if(EGL.EGL_NO_SURFACE!=eglSurface) { - EGL.eglDestroySurface(eglDisplay, eglSurface); - } + private final long createEGLSurface() { + final EGLWrappedSurface eglws = (EGLWrappedSurface) surface; + final EGLGraphicsConfiguration eglConfig = (EGLGraphicsConfiguration) eglws.getGraphicsConfiguration(); + final NativeSurface upstreamSurface = eglws.getUpstreamSurface(); - if(DEBUG) { - System.err.println(getThreadName() + ": createSurface using eglDisplay "+toHexString(eglDisplay)+", "+eglConfig); - } + long eglSurface = createSurface(eglConfig, eglws.getWidth(), eglws.getHeight(), upstreamSurface.getSurfaceHandle()); - eglSurface = createSurface(eglDisplay, eglConfig.getNativeConfig(), surface.getSurfaceHandle()); - int eglError0 = EGL.EGL_SUCCESS; + int eglError0; if (EGL.EGL_NO_SURFACE == eglSurface) { eglError0 = EGL.eglGetError(); if(EGL.EGL_BAD_NATIVE_WINDOW == eglError0) { - // Try window handle if available and differs (Windows HDC / HWND). + // Try window handle if available and differs (Windows HDC / HWND). // ANGLE impl. required HWND on Windows. - if(surface instanceof NativeWindow) { - final NativeWindow nw = (NativeWindow) surface; + if(upstreamSurface instanceof NativeWindow) { + final NativeWindow nw = (NativeWindow) upstreamSurface; if(nw.getWindowHandle() != nw.getSurfaceHandle()) { if(DEBUG) { System.err.println(getThreadName() + ": Info: Creation of window surface w/ surface handle failed: "+eglConfig+", error "+toHexString(eglError0)+", retry w/ windowHandle"); } - eglSurface = createSurface(eglDisplay, eglConfig.getNativeConfig(), nw.getWindowHandle()); + eglSurface = createSurface(eglConfig, eglws.getWidth(), eglws.getHeight(), nw.getWindowHandle()); if (EGL.EGL_NO_SURFACE == eglSurface) { eglError0 = EGL.eglGetError(); } } } } + } else { + eglError0 = EGL.EGL_SUCCESS; } if (EGL.EGL_NO_SURFACE == eglSurface) { throw new GLException("Creation of window surface failed: "+eglConfig+", "+surface+", error "+toHexString(eglError0)); } - if(DEBUG) { - System.err.println(getThreadName() + ": setSurface using component: handle "+toHexString(surface.getSurfaceHandle())+" -> "+toHexString(eglSurface)); + System.err.println(getThreadName() + ": createEGLSurface handle "+toHexString(eglSurface)); } + return eglSurface; } @Override - protected final void updateHandle() { - if(ownEGLSurface) { - recreateSurface(); + protected final void createHandle() { + final EGLWrappedSurface eglws = (EGLWrappedSurface) surface; + if(DEBUG) { + System.err.println(getThreadName() + ": createHandle of "+eglws); + } + if( eglws.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + if( EGL.EGL_NO_SURFACE != eglws.getSurfaceHandle() ) { + throw new InternalError("Set surface but claimed to be invalid: "+eglws); + } + eglws.setSurfaceHandle( createEGLSurface() ); + } else if( EGL.EGL_NO_SURFACE == eglws.getSurfaceHandle() ) { + throw new InternalError("Nil surface but claimed to be valid: "+eglws); } } - - protected void setRealizedImpl() { - if (realized) { - AbstractGraphicsConfiguration aConfig = surface.getGraphicsConfiguration(); - AbstractGraphicsDevice aDevice = aConfig.getScreen().getDevice(); - if(aDevice instanceof EGLGraphicsDevice) { - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealized(true): using existing EGL config - START"); - } - // just fetch the data .. trust but verify .. - eglDisplay = aDevice.getHandle(); - if (eglDisplay == EGL.EGL_NO_DISPLAY) { - throw new GLException("Invalid EGL display in EGLGraphicsDevice from "+aDevice); - } - if(aConfig instanceof EGLGraphicsConfiguration) { - eglConfig = (EGLGraphicsConfiguration) aConfig; // done .. - if (null == eglConfig) { - throw new GLException("Null EGLGraphicsConfiguration from "+aConfig); - } - int[] tmp = new int[1]; - if ( 0 != surface.getSurfaceHandle() && - EGL.eglQuerySurface(eglDisplay, surface.getSurfaceHandle(), EGL.EGL_CONFIG_ID, tmp, 0) ) { - // surface holds static EGLSurface - eglSurface = surface.getSurfaceHandle(); - if(DEBUG) { - System.err.println(getThreadName() + ": setSurface re-using component's EGLSurface: handle "+toHexString(eglSurface)); - } - } else { - // EGLSurface is ours - subsequent updateHandle() will issue recreateSurface(); - ownEGLSurface=true; - } - } else { - throw new GLException("EGLGraphicsDevice hold by non EGLGraphicsConfiguration: "+aConfig); - } - } else { - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealized(true): creating new EGL config - START"); - } - // create a new EGL config .. - ownEGLDisplay=true; - // EGLSurface is ours .. - ownEGLSurface=true; + @Override + protected void destroyHandle() { + final EGLWrappedSurface eglws = (EGLWrappedSurface) surface; + if(DEBUG) { + System.err.println(getThreadName() + ": destroyHandle of "+eglws); + } + if( EGL.EGL_NO_SURFACE == eglws.getSurfaceHandle() ) { + throw new InternalError("Nil surface but claimed to be valid: "+eglws); + } + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) eglws.getGraphicsConfiguration().getScreen().getDevice(); + if( eglws.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + EGL.eglDestroySurface(eglDevice.getHandle(), eglws.getSurfaceHandle()); + eglws.setSurfaceHandle(EGL.EGL_NO_SURFACE); + } + } - eglDisplay = EGLDisplayUtil.eglGetDisplay(surface, true); - if (eglDisplay == EGL.EGL_NO_DISPLAY) { - throw new GLException("Failed to created EGL display: "+surface+", "+aDevice+", error "+toHexString(EGL.eglGetError())); - } - if (!EGLDisplayUtil.eglInitialize(eglDisplay, null, null)) { - throw new GLException("eglInitialize failed"+", error "+Integer.toHexString(EGL.eglGetError())); - } - EGLGraphicsDevice e = new EGLGraphicsDevice(eglDisplay, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); - AbstractGraphicsScreen s = new DefaultGraphicsScreen(e, aConfig.getScreen().getIndex()); - final GLCapabilitiesImmutable capsRequested = (GLCapabilitiesImmutable) aConfig.getRequestedCapabilities(); - if(aConfig instanceof EGLGraphicsConfiguration) { - final EGLGLCapabilities capsChosen = (EGLGLCapabilities) aConfig.getChosenCapabilities(); - if(0 == capsChosen.getEGLConfig()) { - // 'refresh' the native EGLConfig handle - capsChosen.setEGLConfig(EGLGraphicsConfiguration.EGLConfigId2EGLConfig(eglDisplay, capsChosen.getEGLConfigID())); - if(0 == capsChosen.getEGLConfig()) { - throw new GLException("Refreshing native EGLConfig handle failed: "+capsChosen+" of "+aConfig); - } - } - eglConfig = new EGLGraphicsConfiguration(s, capsChosen, capsRequested, null); - if(DEBUG) { - System.err.println(getThreadName() + ": Reusing chosenCaps: "+eglConfig); - } - } else { - eglConfig = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( - capsRequested, capsRequested, null, s, aConfig.getVisualID(VIDType.NATIVE), false); - - if (null == eglConfig) { - throw new GLException("Couldn't create EGLGraphicsConfiguration from "+s); - } else if(DEBUG) { - System.err.println(getThreadName() + ": Chosen eglConfig: "+eglConfig); - } - } - // subsequent updateHandle() will issue recreateSurface(); - } - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealized(true): END: ownDisplay "+ownEGLDisplay+", ownSurface "+ownEGLSurface); - } - } else if (ownEGLSurface && eglSurface != EGL.EGL_NO_SURFACE) { + protected static boolean isValidEGLSurface(long eglDisplayHandle, long surfaceHandle) { + if( 0 == surfaceHandle ) { + return false; + } + final IntBuffer val = Buffers.newDirectIntBuffer(1); + final boolean eglSurfaceValid = EGL.eglQuerySurface(eglDisplayHandle, surfaceHandle, EGL.EGL_CONFIG_ID, val); + if( !eglSurfaceValid ) { + final int eglErr = EGL.eglGetError(); if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealized(false): ownDisplay "+ownEGLDisplay+", ownSurface "+ownEGLSurface+", eglDisplay: "+toHexString(eglDisplay)+", eglSurface: "+toHexString(eglSurface)); + System.err.println(getThreadName() + ": EGLDrawable.isValidEGLSurface eglQuerySuface failed: error "+toHexString(eglErr)+", "+toHexString(surfaceHandle)); } - // Destroy the window surface - if (!EGL.eglDestroySurface(eglDisplay, eglSurface)) { - throw new GLException("Error destroying window surface (eglDestroySurface)"); - } - eglSurface = EGL.EGL_NO_SURFACE; - if (ownEGLDisplay && EGL.EGL_NO_DISPLAY!=eglDisplay) { - EGLDisplayUtil.eglTerminate(eglDisplay); - } - eglDisplay=EGL.EGL_NO_DISPLAY; - eglConfig=null; } + return eglSurfaceValid; } - protected final void swapBuffersImpl() { - // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() - if(!EGL.eglSwapBuffers(eglDisplay, eglSurface)) { - throw new GLException("Error swapping buffers, eglError "+toHexString(EGL.eglGetError())+", "+this); + @Override + protected final void setRealizedImpl() { + if(DEBUG) { + System.err.println(getThreadName() + ": EGLDrawable.setRealized("+realized+"): NOP - "+surface); } } - /** - * Surface not realizes yet (onscreen) .. Quering EGL surface size only makes sense for external drawable. - * Leave it here for later impl. of an EGLExternalDrawable. - public int getWidth() { - int[] tmp = new int[1]; - if (!EGL.eglQuerySurface(eglDisplay, eglSurface, EGL.EGL_WIDTH, tmp, 0)) { - throw new GLException("Error querying surface width, eglError "+toHexString(EGL.eglGetError())); + @Override + protected final void swapBuffersImpl(boolean doubleBuffered) { + if(doubleBuffered) { + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) surface.getGraphicsConfiguration().getScreen().getDevice(); + // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() + if(!EGL.eglSwapBuffers(eglDevice.getHandle(), surface.getSurfaceHandle())) { + throw new GLException("Error swapping buffers, eglError "+toHexString(EGL.eglGetError())+", "+this); + } } - return tmp[0]; } - public int getHeight() { - int[] tmp = new int[1]; - if (!EGL.eglQuerySurface(eglDisplay, eglSurface, EGL.EGL_HEIGHT, tmp, 0)) { - throw new GLException("Error querying surface height, eglError "+toHexString(EGL.eglGetError())); - } - return tmp[0]; - } */ - + @Override public GLDynamicLookupHelper getGLDynamicLookupHelper() { - if (getGLProfile().usesNativeGLES2()) { + if (getGLProfile().usesNativeGLES3()) { + return getFactoryImpl().getGLDynamicLookupHelper(3); + } else if (getGLProfile().usesNativeGLES2()) { return getFactoryImpl().getGLDynamicLookupHelper(2); } else if (getGLProfile().usesNativeGLES1()) { return getFactoryImpl().getGLDynamicLookupHelper(1); @@ -260,12 +177,13 @@ public abstract class EGLDrawable extends GLDrawableImpl { } } + @Override public String toString() { return getClass().getName()+"[realized "+isRealized()+ ",\n\tfactory "+getFactory()+ ",\n\tsurface "+getNativeSurface()+ - ",\n\teglSurface "+toHexString(eglSurface)+ - ",\n\teglConfig "+eglConfig+ + ",\n\teglSurface "+toHexString(surface.getSurfaceHandle())+ + ",\n\teglConfig "+surface.getGraphicsConfiguration()+ ",\n\trequested "+getRequestedGLCapabilities()+ ",\n\tchosen "+getChosenGLCapabilities()+"]"; } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java index 4b77bfa87..9ee0134f1 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,93 +29,109 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.egl; -import javax.media.nativewindow.*; -import javax.media.opengl.*; -import javax.media.opengl.GLProfile.ShutdownType; - -import com.jogamp.common.JogampRuntimeException; -import com.jogamp.common.os.Platform; -import com.jogamp.common.util.*; -import com.jogamp.nativewindow.WrappedSurface; -import com.jogamp.nativewindow.egl.EGLGraphicsDevice; - -import jogamp.opengl.*; - +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.MutableSurface; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.opengl.GL; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesChooser; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import jogamp.nativewindow.WrappedSurface; +import jogamp.opengl.Debug; +import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLDrawableFactoryImpl; +import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLDynamicLookupHelper; +import jogamp.opengl.GLGraphicsConfigurationUtil; +import jogamp.opengl.SharedResourceRunner; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.nio.PointerBuffer; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; +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; + + 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 boolean isANGLE = false; - + 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 dl.isFunctionAvailable("glLoadIdentity") && + dl.isFunctionAvailable("glEnableClientState") && + dl.isFunctionAvailable("glColorPointer"); + } + public EGLDrawableFactory() { super(); - + // Register our GraphicsConfigurationFactory implementations // The act of constructing them causes them to be registered EGLGraphicsConfigurationFactory.registerFactory(); // Check for other underlying stuff .. - if(NativeWindowFactory.TYPE_X11.equals(NativeWindowFactory.getNativeWindowType(true))) { + if(NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true)) { + hasX11 = true; try { ReflectionUtil.createInstance("jogamp.opengl.x11.glx.X11GLXGraphicsConfigurationFactory", EGLDrawableFactory.class.getClassLoader()); - } catch (JogampRuntimeException jre) { /* n/a .. */ } + } catch (Exception jre) { /* n/a .. */ } } - defaultDevice = new EGLGraphicsDevice(AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); - - // FIXME: Probably need to move EGL from a static model - // to a dynamic one, where there can be 2 instances + // FIXME: Probably need to move EGL from a static model + // to a dynamic one, where there can be 2 instances // for each ES profile with their own ProcAddressTable. synchronized(EGLDrawableFactory.class) { - /** - * Currently AMD's EGL impl. crashes at eglGetDisplay(EGL_DEFAULT_DISPLAY) - * - // Check Desktop ES2 Availability first (AMD, ..) - if(null==eglES2DynamicLookupHelper) { - GLDynamicLookupHelper tmp=null; - try { - tmp = new GLDynamicLookupHelper(new DesktopES2DynamicLibraryBundleInfo()); - } catch (GLException gle) { - if(DEBUG) { - gle.printStackTrace(); - } - } - if(null!=tmp && tmp.isLibComplete()) { - eglES2DynamicLookupHelper = tmp; - EGL.resetProcAddressTable(eglES2DynamicLookupHelper); - if (GLProfile.DEBUG) { - System.err.println("Info: EGLDrawableFactory: Desktop ES2 - OK"); - } - } else if (GLProfile.DEBUG) { - System.err.println("Info: EGLDrawableFactory: Desktop ES2 - NOPE"); - } - } */ final boolean hasDesktopES2 = null != eglES2DynamicLookupHelper; - + if(!hasDesktopES2 && null==eglES1DynamicLookupHelper) { GLDynamicLookupHelper tmp=null; try { @@ -124,18 +140,18 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { if(DEBUG) { gle.printStackTrace(); } - } + } if(null!=tmp && tmp.isLibComplete()) { eglES1DynamicLookupHelper = tmp; EGL.resetProcAddressTable(eglES1DynamicLookupHelper); final boolean isANGLEES1 = isANGLE(eglES1DynamicLookupHelper); isANGLE |= isANGLEES1; - if (GLProfile.DEBUG) { + if (DEBUG || GLProfile.DEBUG) { System.err.println("Info: EGLDrawableFactory: EGL ES1 - OK, isANGLE: "+isANGLEES1); - } - } else if (GLProfile.DEBUG) { - System.err.println("Info: EGLDrawableFactory: EGL ES1 - NOPE"); - } + } + } else if (DEBUG || GLProfile.DEBUG) { + System.err.println("Info: EGLDrawableFactory: EGL ES1 - NOPE (ES1 lib)"); + } } if(!hasDesktopES2 && null==eglES2DynamicLookupHelper) { GLDynamicLookupHelper tmp=null; @@ -145,196 +161,545 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { if(DEBUG) { gle.printStackTrace(); } - } + } if(null!=tmp && tmp.isLibComplete()) { eglES2DynamicLookupHelper = tmp; EGL.resetProcAddressTable(eglES2DynamicLookupHelper); + final boolean includesES1 = null == eglES1DynamicLookupHelper && includesES1(eglES2DynamicLookupHelper); + if(includesES1) { + eglES1DynamicLookupHelper = tmp; + } final boolean isANGLEES2 = isANGLE(eglES2DynamicLookupHelper); isANGLE |= isANGLEES2; - if (GLProfile.DEBUG) { - System.err.println("Info: EGLDrawableFactory: EGL ES2 - OK, isANGLE: "+isANGLEES2); - } - } else if (GLProfile.DEBUG) { + if (DEBUG || GLProfile.DEBUG) { + System.err.println("Info: EGLDrawableFactory: EGL ES2 - OK (includesES1 "+includesES1+", isANGLE: "+isANGLEES2+")"); + if(includesES1) { + System.err.println("Info: EGLDrawableFactory: EGL ES1 - OK (ES2 lib)"); + } + } + } else if (DEBUG || GLProfile.DEBUG) { System.err.println("Info: EGLDrawableFactory: EGL ES2 - NOPE"); - } + } + } + if( null != eglES2DynamicLookupHelper || null != eglES1DynamicLookupHelper ) { + if(isANGLE && !enableANGLE) { + if(DEBUG || GLProfile.DEBUG) { + System.err.println("Info: EGLDrawableFactory.init - EGL/ES2 ANGLE disabled"); + } + } else { + if( isANGLE && ( DEBUG || GLProfile.DEBUG ) ) { + System.err.println("Info: EGLDrawableFactory.init - EGL/ES2 ANGLE enabled"); + } + sharedMap = new HashMap<String /*uniqueKey*/, SharedResource>(); + sharedMapCreateAttempt = new HashSet<String>(); + // FIXME: defaultDevice.open() triggers eglInitialize(..) which crashed on Windows w/ Chrome/ANGLE, FF/ANGLE! + defaultDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(EGL.EGL_DEFAULT_DISPLAY, AbstractGraphicsDevice.DEFAULT_CONNECTION, AbstractGraphicsDevice.DEFAULT_UNIT); + } } } } - protected final void destroy(ShutdownType shutdownType) { + @Override + protected final boolean isComplete() { + return null != sharedMap; // null != eglES2DynamicLookupHelper || null != eglES1DynamicLookupHelper; + } + + + @Override + protected final void shutdownImpl() { + if( DEBUG ) { + System.err.println("EGLDrawableFactory.shutdown"); + } if(null != sharedMap) { + if(DEBUG) { + dumpMap(); + } Collection<SharedResource> srl = sharedMap.values(); for(Iterator<SharedResource> sri = srl.iterator(); sri.hasNext(); ) { SharedResource sr = sri.next(); if(DEBUG) { - System.err.println("EGLDrawableFactory.destroy("+shutdownType+"): "+sr.device.toString()); - } - final long eglDisplay = sr.device.getHandle(); - if(EGL.EGL_NO_DISPLAY != eglDisplay) { - EGLDisplayUtil.eglTerminate(eglDisplay); + System.err.println("EGLDrawableFactory.shutdown: "+sr.device.toString()); } + sr.device.close(); } sharedMap.clear(); + sharedMapCreateAttempt.clear(); sharedMap = null; + sharedMapCreateAttempt = null; + } + if(null != defaultSharedResource) { + defaultSharedResource = null; + } + if(null != defaultDevice) { + defaultDevice.close(); + defaultDevice = null; } - defaultDevice = null; /** * Pulling away the native library may cause havoc .. */ - if(ShutdownType.COMPLETE == shutdownType) { - if(null != eglES1DynamicLookupHelper) { - // eglES1DynamicLookupHelper.destroy(); - eglES1DynamicLookupHelper = null; - } - if(null != eglES2DynamicLookupHelper) { - // eglES2DynamicLookupHelper.destroy(); - eglES2DynamicLookupHelper = null; - } + if(null != eglES1DynamicLookupHelper) { + // eglES1DynamicLookupHelper.destroy(); + eglES1DynamicLookupHelper = null; + } + if(null != eglES2DynamicLookupHelper) { + // eglES2DynamicLookupHelper.destroy(); + eglES2DynamicLookupHelper = null; } EGLGraphicsConfigurationFactory.unregisterFactory(); + EGLDisplayUtil.shutdown(DEBUG); + } + + private void dumpMap() { + synchronized(sharedMap) { + System.err.println("EGLDrawableFactory.map "+sharedMap.size()); + int i=0; + Set<String> keys = sharedMap.keySet(); + for(Iterator<String> keyI = keys.iterator(); keyI.hasNext(); i++) { + 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/3 [es2 "+sr.wasES2ContextCreated+", es3 "+sr.wasES3ContextCreated+", [pbuffer "+sr.hasPBufferES3ES2+", quirks "+sr.rendererQuirksES3ES2+", ctp "+EGLContext.getGLVersion(2, 0, sr.ctpES3ES2, null)+"]]"); + } + ; + } } - private HashMap<String /*connection*/, SharedResource> sharedMap = new HashMap<String /*connection*/, SharedResource>(); - private EGLGraphicsDevice defaultDevice; + private HashMap<String /*uniqueKey*/, SharedResource> sharedMap = null; + private HashSet<String> sharedMapCreateAttempt = null; + private EGLGraphicsDevice defaultDevice = null; + private SharedResource defaultSharedResource = null; + private boolean isANGLE = false; + private boolean hasX11 = false; - static class SharedResource { + static class SharedResource implements SharedResourceRunner.Resource { private final EGLGraphicsDevice device; - // private final EGLDrawable drawable; // private final EGLContext contextES1; // private final EGLContext contextES2; + // 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 hasPBufferES3ES2; - SharedResource(EGLGraphicsDevice dev, boolean wasContextES1Created, boolean wasContextES2Created - /*EGLDrawable draw, EGLContext ctxES1, EGLContext ctxES2 */) { + SharedResource(EGLGraphicsDevice dev, + boolean wasContextES1Created, boolean hasPBufferES1, GLRendererQuirks rendererQuirksES1, int ctpES1, + boolean wasContextES2Created, boolean wasContextES3Created, + boolean hasPBufferES3ES2, GLRendererQuirks rendererQuirksES3ES2, int ctpES3ES2) { this.device = dev; - // this.drawable = draw; // this.contextES1 = ctxES1; - // this.contextES2 = ctxES2; this.wasES1ContextCreated = wasContextES1Created; + this.hasPBufferES1= hasPBufferES1; + this.rendererQuirksES1 = rendererQuirksES1; + this.ctpES1 = ctpES1; + + // this.contextES2 = ctxES2; + // this.contextES3 = ctxES3; this.wasES2ContextCreated = wasContextES2Created; + this.wasES3ContextCreated = wasContextES3Created; + this.hasPBufferES3ES2= hasPBufferES3ES2; + this.rendererQuirksES3ES2 = rendererQuirksES3ES2; + this.ctpES3ES2 = ctpES3ES2; } - final EGLGraphicsDevice getDevice() { return device; } - // final EGLDrawable getDrawable() { return drawable; } + @Override + public final boolean isValid() { + return wasES1ContextCreated || wasES2ContextCreated || wasES3ContextCreated; + } + @Override + public final EGLGraphicsDevice getDevice() { return device; } // final EGLContext getContextES1() { return contextES1; } // final EGLContext getContextES2() { return contextES2; } - final boolean wasES1ContextAvailable() { return wasES1ContextCreated; } - final boolean wasES2ContextAvailable() { return wasES2ContextCreated; } + // final EGLContext getContextES3() { return contextES3; } + + @Override + public AbstractGraphicsScreen getScreen() { + return null; + } + @Override + public GLDrawableImpl getDrawable() { + return null; + } + @Override + public GLContextImpl getContext() { + return null; + } + @Override + public GLRendererQuirks getRendererQuirks() { + return null != rendererQuirksES3ES2 ? rendererQuirksES3ES2 : rendererQuirksES1 ; + } } + @Override public final AbstractGraphicsDevice getDefaultDevice() { return defaultDevice; } + @Override public final boolean getIsDeviceCompatible(AbstractGraphicsDevice device) { // via mappings (X11/WGL/.. -> EGL) we shall be able to handle all types. - return null!=eglES2DynamicLookupHelper || null!=eglES1DynamicLookupHelper; + return null != sharedMap ; // null!=eglES2DynamicLookupHelper || null!=eglES1DynamicLookupHelper; } - /** - private boolean isEGLContextAvailable(EGLGraphicsDevice sharedDevice, String profile) { - boolean madeCurrent = false; - final GLCapabilities caps = new GLCapabilities(GLProfile.get(profile)); - caps.setRedBits(5); caps.setGreenBits(5); caps.setBlueBits(5); caps.setAlphaBits(0); - caps.setDoubleBuffered(false); - caps.setOnscreen(false); - caps.setPBuffer(true); - final EGLDrawable drawable = (EGLDrawable) createGLDrawable( createOffscreenSurfaceImpl(sharedDevice, caps, caps, null, 64, 64) ); - if(null!=drawable) { - final EGLContext context = (EGLContext) drawable.createContext(null); - if (null != context) { - context.setSynchronized(true); - try { - context.makeCurrent(); // could cause exception - madeCurrent = context.isCurrent(); - } catch (GLException gle) { - if (DEBUG) { - System.err.println("EGLDrawableFactory.createShared: INFO: makeCurrent failed"); - gle.printStackTrace(); - } - } finally { - context.destroy(); - } + private static List<GLCapabilitiesImmutable> getAvailableEGLConfigs(EGLGraphicsDevice eglDisplay, GLCapabilitiesImmutable caps) { + final IntBuffer numConfigs = Buffers.newDirectIntBuffer(1); + if(!EGL.eglGetConfigs(eglDisplay.getHandle(), null, 0, numConfigs)) { + throw new GLException("EGLDrawableFactory.getAvailableEGLConfigs: Get maxConfigs (eglGetConfigs) call failed, error "+EGLContext.toHexString(EGL.eglGetError())); + } + if(0 < numConfigs.get(0)) { + final PointerBuffer configs = PointerBuffer.allocateDirect(numConfigs.get(0)); + final IntBuffer attrs = EGLGraphicsConfiguration.GLCapabilities2AttribList(caps); + final int winattrmask = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(caps); + if( EGL.eglChooseConfig(eglDisplay.getHandle(), attrs, configs, configs.capacity(), numConfigs) && numConfigs.get(0) > 0) { + return EGLGraphicsConfigurationFactory.eglConfigs2GLCaps(eglDisplay, caps.getGLProfile(), configs, numConfigs.get(0), winattrmask, false /* forceTransparentFlag */, false /* onlyFirstValid */); } - drawable.destroy(); } - return madeCurrent; - } */ - - /* package */ SharedResource getOrCreateEGLSharedResource(AbstractGraphicsDevice adevice) { - if(null == eglES1DynamicLookupHelper && null == eglES2DynamicLookupHelper) { - return null; + return new ArrayList<GLCapabilitiesImmutable>(0); + } + + private static void dumpEGLInfo(final String prefix, final long eglDisplay) { + final String eglVendor = EGL.eglQueryString(eglDisplay, EGL.EGL_VENDOR); + final String eglClientAPIs = EGL.eglQueryString(eglDisplay, EGL.EGL_CLIENT_APIS); + final String eglVersion = EGL.eglQueryString(eglDisplay, EGL.EGL_VERSION); + System.err.println(prefix+"EGL vendor "+eglVendor+", version "+eglVersion+", clientAPIs "+eglClientAPIs); + } + + private boolean mapAvailableEGLESConfig(AbstractGraphicsDevice adevice, int[] esProfile, + boolean[] hasPBuffer, GLRendererQuirks[] rendererQuirks, int[] ctp) { + final String profileString; + switch( esProfile[0] ) { + case 3: + profileString = GLProfile.GLES3; break; + case 2: + profileString = GLProfile.GLES2; break; + case 1: + profileString = GLProfile.GLES1; break; + default: + throw new GLException("Invalid ES profile number "+esProfile); } - String connection = adevice.getConnection(); - SharedResource sr; - synchronized(sharedMap) { - sr = (SharedResource) sharedMap.get(connection); - } - if(null==sr) { - long eglDisplay = EGLDisplayUtil.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); - if (eglDisplay == EGL.EGL_NO_DISPLAY) { - throw new GLException("Failed to created EGL default display: error 0x"+Integer.toHexString(EGL.eglGetError())); - } else if(DEBUG) { - System.err.println("EGLDrawableFactory.createShared: eglDisplay(EGL_DEFAULT_DISPLAY): 0x"+Long.toHexString(eglDisplay)); + if ( !GLProfile.isAvailable(adevice, profileString) ) { + if( DEBUG ) { + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: "+profileString+" n/a on "+adevice); } - if (!EGLDisplayUtil.eglInitialize(eglDisplay, null, null)) { - throw new GLException("eglInitialize failed"+", error 0x"+Integer.toHexString(EGL.eglGetError())); + return false; + } + final GLProfile glp = GLProfile.get(adevice, profileString) ; + final GLDrawableFactoryImpl desktopFactory = (GLDrawableFactoryImpl) GLDrawableFactory.getDesktopFactory(); + final boolean initDefaultDevice = 0 == defaultDevice.getHandle(); // Note: GLProfile always triggers EGL device initialization first! + final boolean mapsADeviceToDefaultDevice = !QUERY_EGL_ES_NATIVE_TK || initDefaultDevice || + 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; + ProxySurface upstreamSurface = null; // X11, GLX, .. + ProxySurface downstreamSurface = null; // EGL + boolean success = false; + try { + final GLCapabilities reqCapsAny = new GLCapabilities(glp); + reqCapsAny.setRedBits(5); reqCapsAny.setGreenBits(5); reqCapsAny.setBlueBits(5); reqCapsAny.setAlphaBits(0); + reqCapsAny.setDoubleBuffered(false); + + if( mapsADeviceToDefaultDevice ) { + // In this branch, any non EGL device is mapped to EGL default shared resources (default behavior). + // Only one default shared resource instance is ever be created. + if( initDefaultDevice ) { + defaultDevice.open(); + + // Probe for GLRendererQuirks.SingletonEGLDisplayOnly + final long secondEGLDisplay = EGL.eglGetDisplay(EGL.EGL_DEFAULT_DISPLAY); + if ( EGL.EGL_NO_DISPLAY == secondEGLDisplay ) { + final int[] quirks = { GLRendererQuirks.SingletonEGLDisplayOnly }; + GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, 0, 1); + EGLDisplayUtil.setSingletonEGLDisplayOnly(true); + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirks[0])+": cause: Second eglGetDisplay(EGL_DEFAULT_DISPLAY) failed"); + } + } + } + if( DEBUG ) { + dumpEGLInfo("EGLDrawableFactory.mapAvailableEGLESConfig: ", defaultDevice.getHandle()); + } + + final GLCapabilitiesImmutable reqCapsPBuffer = GLGraphicsConfigurationUtil.fixGLPBufferGLCapabilities(reqCapsAny); + final List<GLCapabilitiesImmutable> availablePBufferCapsL = getAvailableEGLConfigs(defaultDevice, reqCapsPBuffer); + hasPBuffer[0] = availablePBufferCapsL.size() > 0; + + // 1st case: adevice is not the EGL default device, map default shared resources + if( adevice != defaultDevice ) { + if(null == defaultSharedResource) { + return false; + } + switch(esProfile[0]) { + 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; + } + if( null != rendererQuirks[0] ) { + GLRendererQuirks.addStickyDeviceQuirks(adevice, rendererQuirks[0]); + } + EGLContext.mapStaticGLVersion(adevice, esProfile[0], 0, ctp[0]); + return true; + } + + // attempt to created the default shared resources .. + + if( hasPBuffer[0] ) { + // 2nd case create defaultDevice shared resource using pbuffer surface + downstreamSurface = createDummySurfaceImpl(defaultDevice, false, reqCapsPBuffer, reqCapsPBuffer, null, 64, 64); // egl pbuffer offscreen + if( null != downstreamSurface ) { + downstreamSurface.createNotify(); + } + surface = downstreamSurface; + } else { + // 3rd case fake creation of defaultDevice shared resource, no pbuffer available + final List<GLCapabilitiesImmutable> capsAnyL = getAvailableEGLConfigs(defaultDevice, reqCapsAny); + if(capsAnyL.size() > 0) { + final GLCapabilitiesImmutable chosenCaps = capsAnyL.get(0); + EGLContext.mapStaticGLESVersion(defaultDevice, chosenCaps); + success = true; + } + if(DEBUG) { + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig() no pbuffer config available, detected !pbuffer config: "+success); + EGLGraphicsConfigurationFactory.printCaps("!PBufferCaps", capsAnyL, System.err); + } + } + eglDevice = defaultDevice; // reuse + } else { + // 4th case always creates a true mapping of given device to EGL + upstreamSurface = desktopFactory.createDummySurface(adevice, reqCapsAny, null, 64, 64); // X11, WGL, .. dummy window + if(null != upstreamSurface) { + upstreamSurface.createNotify(); + } + surface = upstreamSurface; + eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(surface); + eglDevice.open(); + if( DEBUG ) { + dumpEGLInfo("EGLDrawableFactory.mapAvailableEGLESConfig: ", eglDevice.getHandle()); + } + hasPBuffer[0] = true; } - final EGLGraphicsDevice sharedDevice = new EGLGraphicsDevice(eglDisplay, connection, adevice.getUnitID()); - // final boolean madeCurrentES1 = isEGLContextAvailable(sharedDevice, GLProfile.GLES1); - // final boolean madeCurrentES2 = isEGLContextAvailable(sharedDevice, GLProfile.GLES2); - final boolean madeCurrentES1 = true; // FIXME - final boolean madeCurrentES2 = true; // FIXME - sr = new SharedResource(sharedDevice, madeCurrentES1, madeCurrentES2); - synchronized(sharedMap) { - sharedMap.put(connection, sr); + + if(null != surface) { + final EGLDrawable drawable = (EGLDrawable) createOnscreenDrawableImpl ( surface ); // works w/ implicit pbuffer surface via proxy-hook + drawable.setRealized(true); + final EGLContext context = (EGLContext) drawable.createContext(null); + if (null != context) { + try { + context.makeCurrent(); // could cause exception + if(context.isCurrent()) { + final String glVersion = context.getGL().glGetString(GL.GL_VERSION); + if(null != glVersion) { + context.mapCurrentAvailableGLVersion(eglDevice); + if(eglDevice != adevice) { + context.mapCurrentAvailableGLVersion(adevice); + } + rendererQuirks[0] = context.getRendererQuirks(); + ctp[0] = context.getContextOptions(); + esProfile[0] = context.getGLVersionNumber().getMajor(); + success = true; + } else { + // Oops .. something is wrong + if(DEBUG) { + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: "+eglDevice+", "+context.getGLVersion()+" - VERSION is null, dropping availability!"); + } + } + } + } catch (Throwable t) { + if (DEBUG) { + System.err.println("EGLDrawableFactory.mapAvailableEGLESConfig: INFO: context create/makeCurrent failed"); + t.printStackTrace(); + } + } finally { + context.destroy(); + } + } + drawable.setRealized(false); + } + } catch (Throwable t) { + if(DEBUG) { + System.err.println("Catched Exception on thread "+getThreadName()); + t.printStackTrace(); + } + success = false; + } finally { + if(null != downstreamSurface) { + downstreamSurface.destroyNotify(); + } + if( defaultDevice != eglDevice ) { // don't close default device + if(null != eglDevice) { + eglDevice.close(); + } + if(null != upstreamSurface) { + upstreamSurface.destroyNotify(); + } } - if (DEBUG) { - System.err.println("EGLDrawableFactory.createShared: device: " + sharedDevice); - System.err.println("EGLDrawableFactory.createShared: context ES1: " + madeCurrentES1); - System.err.println("EGLDrawableFactory.createShared: context ES2: " + madeCurrentES2); - } } - return sr; + return success; } - protected final Thread getSharedResourceThread() { - return null; + private final boolean needsToCreateSharedResource(String key, SharedResource[] existing) { + synchronized(sharedMap) { + final SharedResource sr = sharedMap.get(key); + if( null == sr ) { + final boolean createAttempted = sharedMapCreateAttempt.contains(key); + if(!createAttempted) { + sharedMapCreateAttempt.add(key); + } + return !createAttempted; + } else { + if(null != existing) { + existing[0] = sr; + } + return false; + } + } } - - protected final boolean createSharedResource(AbstractGraphicsDevice device) { - try { - SharedResource sr = getOrCreateEGLSharedResource(device); - if(null!=sr) { - return sr.wasES1ContextAvailable() || sr.wasES2ContextAvailable(); + + @Override + protected final SharedResource getOrCreateSharedResourceImpl(AbstractGraphicsDevice adevice) { + if(null == sharedMap) { // null == eglES1DynamicLookupHelper && null == eglES2DynamicLookupHelper + return null; + } + + if( needsToCreateSharedResource(defaultDevice.getUniqueID(), null) ) { + if (DEBUG) { + System.err.println("EGLDrawableFactory.createShared: (defaultDevice): req. device: "+adevice+", defaultDevice "+defaultDevice); + Thread.dumpStack(); } - } catch (GLException gle) { - if(DEBUG) { - System.err.println("Catched Exception while EGL Shared Resource initialization"); - gle.printStackTrace(); + if(null != defaultSharedResource) { + dumpMap(); + throw new InternalError("defaultSharedResource already exist: "+defaultSharedResource); } + defaultSharedResource = createEGLSharedResourceImpl(defaultDevice); + } + + final String key = adevice.getUniqueID(); + if( defaultDevice.getUniqueID().equals(key) ) { + return defaultSharedResource; + } else { + if( null == defaultSharedResource) { // defaultDevice must be initialized before host-device + dumpMap(); + throw new InternalError("defaultSharedResource does not exist"); + } + final SharedResource[] existing = new SharedResource[] { null }; + if ( !needsToCreateSharedResource(key, existing) ) { + return existing[0]; + } + return createEGLSharedResourceImpl(adevice); } - return false; } - - protected final GLContext getOrCreateSharedContextImpl(AbstractGraphicsDevice device) { - return null; // n/a for EGL .. since we don't keep the resources + + private SharedResource createEGLSharedResourceImpl(AbstractGraphicsDevice adevice) { + final boolean madeCurrentES1; + boolean[] hasPBufferES1 = new boolean[] { false }; + boolean[] hasPBufferES3ES2 = new boolean[] { false }; + // EGLContext[] eglCtxES1 = new EGLContext[] { null }; + // EGLContext[] eglCtxES2 = new EGLContext[] { null }; + GLRendererQuirks[] rendererQuirksES1 = new GLRendererQuirks[] { null }; + GLRendererQuirks[] rendererQuirksES3ES2 = new GLRendererQuirks[] { null }; + int[] ctpES1 = new int[] { -1 }; + int[] ctpES3ES2 = new int[] { -1 }; + + + if (DEBUG) { + System.err.println("EGLDrawableFactory.createShared(): device "+adevice); + } + + if( null != eglES1DynamicLookupHelper ) { + final int[] esProfile = { 1 }; + madeCurrentES1 = mapAvailableEGLESConfig(adevice, esProfile, hasPBufferES1, rendererQuirksES1, ctpES1) && 1 == esProfile[0]; + } else { + madeCurrentES1 = false; + } + boolean madeCurrentES2 = false; + boolean madeCurrentES3 = false; + if( null != eglES2DynamicLookupHelper ) { + // ES3 Query + final int[] esProfile = { 3 }; + madeCurrentES3 = mapAvailableEGLESConfig(adevice, esProfile, hasPBufferES3ES2, rendererQuirksES3ES2, ctpES3ES2) && 3 == esProfile[0]; + if( !madeCurrentES3 ) { + // ES2 Query, may result in ES3 + esProfile[0] = 2; + if( mapAvailableEGLESConfig(adevice, esProfile, hasPBufferES3ES2, rendererQuirksES3ES2, ctpES3ES2) ) { + switch( esProfile[0] ) { + case 2: madeCurrentES2 = true; break; + case 3: madeCurrentES3 = true; break; + default: throw new InternalError("XXXX Got "+esProfile[0]); + } + } + } + } + if( !EGLContext.getAvailableGLVersionsSet(adevice) ) { + // Even though we override the non EGL native mapping intentionally, + // avoid exception due to double 'set' - carefull exception of the rule. + EGLContext.setAvailableGLVersionsSet(adevice); + } + if( hasX11 ) { + handleDontCloseX11DisplayQuirk(rendererQuirksES1[0]); + handleDontCloseX11DisplayQuirk(rendererQuirksES3ES2[0]); + } + final SharedResource sr = new SharedResource(defaultDevice, madeCurrentES1, hasPBufferES1[0], rendererQuirksES1[0], ctpES1[0], + madeCurrentES2, madeCurrentES3, hasPBufferES3ES2[0], rendererQuirksES3ES2[0], ctpES3ES2[0]); + + synchronized(sharedMap) { + sharedMap.put(adevice.getUniqueID(), sr); + } + 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]+", quirks "+rendererQuirksES1[0]); + System.err.println("EGLDrawableFactory.createShared: context ES2: " + madeCurrentES2 + ", hasPBuffer "+hasPBufferES3ES2[0]+", quirks "+rendererQuirksES3ES2[0]); + System.err.println("EGLDrawableFactory.createShared: context ES3: " + madeCurrentES3 + ", hasPBuffer "+hasPBufferES3ES2[0]+", quirks "+rendererQuirksES3ES2[0]); + dumpMap(); + } + return sr; } - - protected AbstractGraphicsDevice getOrCreateSharedDeviceImpl(AbstractGraphicsDevice device) { - SharedResource sr = getOrCreateEGLSharedResource(device); - if(null!=sr) { - return sr.getDevice(); + + private void handleDontCloseX11DisplayQuirk(GLRendererQuirks quirks) { + if( null != quirks && quirks.exist( GLRendererQuirks.DontCloseX11Display ) ) { + jogamp.nativewindow.x11.X11Util.markAllDisplaysUnclosable(); } + } + + @Override + protected final Thread getSharedResourceThread() { return null; } - public boolean isANGLE() { + public final boolean isANGLE() { return isANGLE; } - + + @Override public GLDynamicLookupHelper getGLDynamicLookupHelper(int esProfile) { - if (2==esProfile) { + if ( 2==esProfile || 3==esProfile ) { return eglES2DynamicLookupHelper; } else if (1==esProfile) { return eglES1DynamicLookupHelper; @@ -343,20 +708,23 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { } } + @Override protected List<GLCapabilitiesImmutable> getAvailableCapabilitiesImpl(AbstractGraphicsDevice device) { - if(null == eglES1DynamicLookupHelper && null == eglES2DynamicLookupHelper) { + if(null == sharedMap) { // null == eglES1DynamicLookupHelper && null == eglES2DynamicLookupHelper return new ArrayList<GLCapabilitiesImmutable>(); // null } return EGLGraphicsConfigurationFactory.getAvailableCapabilities(this, device); } + @Override protected GLDrawableImpl createOnscreenDrawableImpl(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); } - return new EGLOnscreenDrawable(this, target); + return new EGLOnscreenDrawable(this, EGLWrappedSurface.get(target)); } + @Override protected GLDrawableImpl createOffscreenDrawableImpl(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); @@ -367,47 +735,104 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { throw new GLException("Non pbuffer not yet implemented"); } // PBuffer GLDrawable Creation - return new EGLPbufferDrawable(this, target); + return new EGLPbufferDrawable(this, EGLWrappedSurface.get(target)); } - public boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { + @Override + public boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { + // SharedResource sr = getOrCreateEGLSharedResource(device); + // return sr.hasES1PBuffer() || sr.hasES2PBuffer(); return true; } - protected NativeSurface createOffscreenSurfaceImpl(AbstractGraphicsDevice device, GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, int width, int height) { - WrappedSurface ns = new WrappedSurface(EGLGraphicsConfigurationFactory.createOffscreenGraphicsConfiguration(device, capsChosen, capsRequested, chooser)); - ns.surfaceSizeChanged(width, height); - return ns; + @Override + protected ProxySurface createMutableSurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstreamHook) { + final boolean ownDevice; + final EGLGraphicsDevice device; + if( createNewDevice || ! (deviceReq instanceof EGLGraphicsDevice) ) { + final long nativeDisplayID = ( deviceReq instanceof EGLGraphicsDevice) ? + ( (EGLGraphicsDevice) deviceReq ).getNativeDisplayID() : deviceReq.getHandle() ; + device = EGLDisplayUtil.eglCreateEGLGraphicsDevice(nativeDisplayID, deviceReq.getConnection(), deviceReq.getUnitID()); + device.open(); + ownDevice = true; + } else { + device = (EGLGraphicsDevice) deviceReq; + ownDevice = false; + } + final DefaultGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); + final EGLGraphicsConfiguration config = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen, VisualIDHolder.VID_UNDEFINED, false); + if(null == config) { + throw new GLException("Choosing GraphicsConfiguration failed w/ "+capsChosen+" on "+screen); + } + return new WrappedSurface(config, 0, upstreamHook, ownDevice); + } + + @Override + public final ProxySurface createDummySurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable chosenCaps, GLCapabilitiesImmutable requestedCaps, GLCapabilitiesChooser chooser, int width, int height) { + chosenCaps = GLGraphicsConfigurationUtil.fixGLPBufferGLCapabilities(chosenCaps); // complete validation in EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(..) above + return createMutableSurfaceImpl(deviceReq, createNewDevice, chosenCaps, requestedCaps, chooser, new EGLDummyUpstreamSurfaceHook(width, height)); + } + + /** + * @param ms {@link MutableSurface} which dimensions and config are being used to create the pbuffer surface. + * It will also hold the resulting pbuffer surface handle. + * @param useTexture + * @return the passed {@link MutableSurface} which now has the EGL pbuffer surface set as it's handle + */ + protected static MutableSurface createPBufferSurfaceImpl(MutableSurface ms, boolean useTexture) { + return null; + } + protected static long createPBufferSurfaceImpl(EGLGraphicsConfiguration config, int width, int height, boolean useTexture) { + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) config.getScreen().getDevice(); + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final int texFormat; + + if(useTexture) { + texFormat = caps.getAlphaBits() > 0 ? EGL.EGL_TEXTURE_RGBA : EGL.EGL_TEXTURE_RGB ; + } else { + texFormat = EGL.EGL_NO_TEXTURE; + } + + if (DEBUG) { + System.out.println("Pbuffer config: " + config); + } + + final IntBuffer attrs = EGLGraphicsConfiguration.CreatePBufferSurfaceAttribList(width, height, texFormat); + final long surf = EGL.eglCreatePbufferSurface(eglDevice.getHandle(), config.getNativeConfig(), attrs); + if (EGL.EGL_NO_SURFACE==surf) { + throw new GLException("Creation of window surface (eglCreatePbufferSurface) failed, dim "+width+"x"+height+", "+eglDevice+", "+config+", error 0x"+Integer.toHexString(EGL.eglGetError())); + } else if(DEBUG) { + System.err.println("PBuffer setSurface result: eglSurface 0x"+Long.toHexString(surf)); + } + return surf; } - protected ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice adevice, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { - // FIXME device/windowHandle -> screen ?! - EGLGraphicsDevice device = (EGLGraphicsDevice) adevice; - DefaultGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); - EGLGraphicsConfiguration cfg = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen, VisualIDHolder.VID_UNDEFINED, false); - WrappedSurface ns = new WrappedSurface(cfg, windowHandle); - return ns; + @Override + protected ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice deviceReq, int screenIdx, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream) { + final EGLGraphicsDevice eglDeviceReq = (EGLGraphicsDevice) deviceReq; + final EGLGraphicsDevice device = EGLDisplayUtil.eglCreateEGLGraphicsDevice(eglDeviceReq.getNativeDisplayID(), deviceReq.getConnection(), deviceReq.getUnitID()); + device.open(); + final DefaultGraphicsScreen screen = new DefaultGraphicsScreen(device, screenIdx); + final EGLGraphicsConfiguration cfg = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen, VisualIDHolder.VID_UNDEFINED, false); + return new WrappedSurface(cfg, windowHandle, upstream, true); } - + + @Override protected GLContext createExternalGLContextImpl() { AbstractGraphicsScreen absScreen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_EGL); return new EGLExternalContext(absScreen); } + @Override public boolean canCreateExternalGLDrawable(AbstractGraphicsDevice device) { return false; } + @Override protected GLDrawable createExternalGLDrawableImpl() { throw new GLException("Not yet implemented"); } - - public boolean canCreateContextOnJava2DSurface(AbstractGraphicsDevice device) { - return false; - } - - public GLContext createContextOnJava2DSurface(Object graphics, GLContext shareWith) - throws GLException { - throw new GLException("Unimplemented on this platform"); - } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDummyUpstreamSurfaceHook.java b/src/jogl/classes/jogamp/opengl/egl/EGLDummyUpstreamSurfaceHook.java new file mode 100644 index 000000000..818f32607 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDummyUpstreamSurfaceHook.java @@ -0,0 +1,60 @@ +package jogamp.opengl.egl; + +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; + +/** Uses a PBuffer offscreen surface */ +public class EGLDummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize { + /** + * @param width the initial width as returned by {@link NativeSurface#getWidth()} via {@link UpstreamSurfaceHook#getWidth(ProxySurface)}, + * not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getHeight()} via {@link UpstreamSurfaceHook#getHeight(ProxySurface)}, + * not the actual dummy surface height, + * The latter is platform specific and small + */ + public EGLDummyUpstreamSurfaceHook(int width, int height) { + super(width, height); + } + + @Override + public final void create(ProxySurface s) { + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) s.getGraphicsConfiguration().getScreen().getDevice(); + eglDevice.lock(); + try { + if(0 == eglDevice.getHandle()) { + eglDevice.open(); + s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + if( EGL.EGL_NO_SURFACE == s.getSurfaceHandle() ) { + s.setSurfaceHandle( EGLDrawableFactory.createPBufferSurfaceImpl((EGLGraphicsConfiguration)s.getGraphicsConfiguration(), 64, 64, false) ); + s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } + s.addUpstreamOptionBits(ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE); + } finally { + eglDevice.unlock(); + } + } + + @Override + public final void destroy(ProxySurface s) { + if( s.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) s.getGraphicsConfiguration().getScreen().getDevice(); + if( EGL.EGL_NO_SURFACE == s.getSurfaceHandle() ) { + throw new InternalError("Owns upstream surface, but no EGL surface: "+s); + } + eglDevice.lock(); + try { + EGL.eglDestroySurface(eglDevice.getHandle(), s.getSurfaceHandle()); + s.setSurfaceHandle(EGL.EGL_NO_SURFACE); + s.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } finally { + eglDevice.unlock(); + } + } + } +} diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java index fe9d7573d..ebe8f49c8 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,15 +20,16 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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"); @@ -51,27 +52,23 @@ public abstract class EGLDynamicLibraryBundleInfo extends GLDynamicLibraryBundle 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> + /** + * Returns <code>true</code> on <code>Android</code>, + * and <code>false</code> otherwise. + * <p> + * {@inheritDoc} + * </p> */ @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; } // default behavior for other platforms return false; } - + @Override public final List<String> getToolGetProcAddressFuncNameList() { List<String> res = new ArrayList<String>(); @@ -93,26 +90,26 @@ public abstract class EGLDynamicLibraryBundleInfo extends GLDynamicLibraryBundle return true; } } - - 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 + + // this is the default EGL lib name, according to the spec eglLibNames.add("libEGL.so.1"); - + // try these as well, if spec fails eglLibNames.add("libEGL.so"); eglLibNames.add("EGL"); - - // for windows distributions using the 'unlike' lib prefix, + + // for windows distributions using the 'unlike' lib prefix, // where our tool does not add it. eglLibNames.add("libEGL"); - + return eglLibNames; } @Override public final List<String> getGlueLibNames() { return glueLibNames; - } + } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java index 0a373eb7f..361ec26ff 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLES1DynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,48 +20,49 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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() { + @Override + 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 + + // this is the default lib name, according to the spec libsGL.add("libGLESv1_CM.so.2"); - + // try these as well, if spec fails libsGL.add("libGLESv1_CM.so"); - libsGL.add("GLESv1_CM"); + libsGL.add("GLESv1_CM"); // alternative names libsGL.add("GLES_CM"); libsGL.add("GLES_CL"); - - // for windows distributions using the 'unlike' lib prefix, + + // for windows distributions using the 'unlike' lib prefix, // where our tool does not add it. libsGL.add("libGLESv1_CM"); libsGL.add("libGLES_CM"); libsGL.add("libGLES_CL"); - + libsList.add(libsGL); } libsList.add(getEGLLibNamesList()); - + return libsList; - } + } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java index d4ee852b1..74738463f 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLES2DynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,49 +20,70 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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() { + @Override + 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 - libsGL.add("libGLESv2.so"); + + // 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"); - libsGL.add("libGLES20"); - + libsGL.add("libGLES20"); + libsList.add(libsGL); } libsList.add(getEGLLibNamesList()); - + return libsList; - } - + } + } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLExternalContext.java b/src/jogl/classes/jogamp/opengl/egl/EGLExternalContext.java index ff60a5262..aff18fc81 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLExternalContext.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLExternalContext.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +28,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -36,51 +36,27 @@ package jogamp.opengl.egl; import javax.media.opengl.*; + import jogamp.opengl.*; + import javax.media.nativewindow.*; public class EGLExternalContext extends EGLContext { - private GLContext lastContext; public EGLExternalContext(AbstractGraphicsScreen screen) { super(null, null); GLContextShareSet.contextCreated(this); - setGLFunctionAvailability(false, 0, 0, CTX_IS_ARB_CREATED|CTX_PROFILE_ES); - getGLStateTracker().setEnabled(false); // external context usage can't track state in Java - } - - public int makeCurrent() throws GLException { - // Save last context if necessary to allow external GLContexts to - // talk to other GLContexts created by this library - GLContext cur = getCurrent(); - if (cur != null && cur != this) { - lastContext = cur; - setCurrent(null); + if( !setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_ES, false /* strictMatch */, false /* withinGLVersionsMapping */) ) { // use GL_VERSION + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); } - return super.makeCurrent(); - } - - public void release() throws GLException { - super.release(); - setCurrent(lastContext); - lastContext = null; - } - - protected void makeCurrentImpl() throws GLException { + getGLStateTracker().setEnabled(false); // external context usage can't track state in Java } + @Override protected void releaseImpl() throws GLException { } + @Override protected void destroyImpl() throws GLException { } - - public void bindPbufferToTexture() { - throw new GLException("Should not call this"); - } - - public void releasePbufferFromTexture() { - throw new GLException("Should not call this"); - } - } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java b/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java index f813edf86..e28b53235 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLGLCapabilities.java @@ -33,39 +33,43 @@ import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; + public class EGLGLCapabilities extends GLCapabilities { private long eglcfg; final private int eglcfgid; - final private int renderableType; + final private int renderableType; final private int nativeVisualID; - + /** - * + * * @param eglcfg * @param eglcfgid * @param visualID native visualID if valid, otherwise VisualIDHolder.VID_UNDEFINED - * @param glp desired GLProfile, or null if determined by renderableType + * @param glp desired GLProfile * @param renderableType actual EGL renderableType - * + * * May throw GLException if given GLProfile is not compatible w/ renderableType */ public EGLGLCapabilities(long eglcfg, int eglcfgid, int visualID, GLProfile glp, int renderableType) { - super( ( null != glp ) ? glp : getCompatible(renderableType) ); + super( glp ); this.eglcfg = eglcfg; this.eglcfgid = eglcfgid; if(!isCompatible(glp, renderableType)) { - throw new GLException("Incompatible "+glp+ - " with EGL-RenderableType["+renderableTypeToString(null, renderableType)+"]"); + throw new GLException("Requested GLProfile "+glp+ + " not compatible with EGL-RenderableType["+renderableTypeToString(null, renderableType)+"]"); } this.renderableType = renderableType; this.nativeVisualID = visualID; } + @Override public Object cloneMutable() { return clone(); } + @Override public Object clone() { try { return super.clone(); @@ -73,13 +77,13 @@ public class EGLGLCapabilities extends GLCapabilities { throw new GLException(e); } } - + final protected void setEGLConfig(long v) { eglcfg=v; } final public long getEGLConfig() { return eglcfg; } final public int getEGLConfigID() { return eglcfgid; } final public int getRenderableType() { return renderableType; } final public int getNativeVisualID() { return nativeVisualID; } - + @Override final public int getVisualID(VIDType type) throws NativeWindowException { switch(type) { @@ -90,43 +94,50 @@ public class EGLGLCapabilities extends GLCapabilities { return getNativeVisualID(); default: throw new NativeWindowException("Invalid type <"+type+">"); - } + } } - + public static boolean isCompatible(GLProfile glp, int renderableType) { if(null == glp) { return true; } - if(0 != (renderableType & EGL.EGL_OPENGL_ES_BIT) && glp.usesNativeGLES1()) { + 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; } return false; } - public static GLProfile getCompatible(int renderableType) { - if(0 != (renderableType & EGL.EGL_OPENGL_ES2_BIT) && GLProfile.isAvailable(GLProfile.GLES2)) { - return GLProfile.get(GLProfile.GLES2); + public static GLProfile getCompatible(EGLGraphicsDevice device, int renderableType) { + if(0 != (renderableType & EGLExt.EGL_OPENGL_ES3_BIT_KHR) && GLProfile.isAvailable(device, GLProfile.GLES3)) { + return GLProfile.get(device, GLProfile.GLES3); + } + if(0 != (renderableType & EGL.EGL_OPENGL_ES2_BIT) && GLProfile.isAvailable(device, GLProfile.GLES2)) { + return GLProfile.get(device, GLProfile.GLES2); } - if(0 != (renderableType & EGL.EGL_OPENGL_ES_BIT) && GLProfile.isAvailable(GLProfile.GLES1)) { - return GLProfile.get(GLProfile.GLES1); + if(0 != (renderableType & EGL.EGL_OPENGL_ES_BIT) && GLProfile.isAvailable(device, GLProfile.GLES1)) { + return GLProfile.get(device, GLProfile.GLES1); } if(0 != (renderableType & EGL.EGL_OPENGL_BIT)) { - return GLProfile.getDefault(); + return GLProfile.getDefault(device); } return null; } - + public static StringBuilder renderableTypeToString(StringBuilder sink, int renderableType) { if(null == sink) { sink = new StringBuilder(); } boolean first=true; + sink.append("0x").append(Integer.toHexString(renderableType)).append(": "); if(0 != (renderableType & EGL.EGL_OPENGL_BIT)) { sink.append("GL"); first=false; } @@ -136,12 +147,16 @@ public class EGLGLCapabilities extends GLCapabilities { if(0 != (renderableType & EGL.EGL_OPENGL_ES2_BIT)) { if(!first) sink.append(", "); sink.append("GLES2"); first=false; } + if(0 != (renderableType & EGLExt.EGL_OPENGL_ES3_BIT_KHR)) { + if(!first) sink.append(", "); sink.append("GLES3"); first=false; + } if(0 != (renderableType & EGL.EGL_OPENVG_API)) { if(!first) sink.append(", "); sink.append("VG"); first=false; } - return sink; + return sink; } - + + @Override public StringBuilder toString(StringBuilder sink) { if(null == sink) { sink = new StringBuilder(); diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfiguration.java b/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfiguration.java index 875bcb95b..88ed0be92 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfiguration.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfiguration.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -37,15 +37,16 @@ package jogamp.opengl.egl; import java.nio.IntBuffer; -import java.util.ArrayList; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.VisualIDHolder; import javax.media.opengl.DefaultGLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; @@ -55,9 +56,14 @@ import com.jogamp.common.nio.Buffers; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.nativewindow.MutableGraphicsConfiguration; import com.jogamp.nativewindow.egl.EGLGraphicsDevice; +import com.jogamp.opengl.GLRendererQuirks; public class EGLGraphicsConfiguration extends MutableGraphicsConfiguration implements Cloneable { - + + private static final String dbgCfgFailIntro = "Info: EGLConfig could not retrieve "; + private static final String dbgCfgFailForConfig = " for config "; + private static final String dbgCfgFailError = ", error "; + public final long getNativeConfig() { return ((EGLGLCapabilities)capabilitiesChosen).getEGLConfig(); } @@ -66,13 +72,20 @@ public class EGLGraphicsConfiguration extends MutableGraphicsConfiguration imple return ((EGLGLCapabilities)capabilitiesChosen).getEGLConfigID(); } - EGLGraphicsConfiguration(AbstractGraphicsScreen absScreen, + EGLGraphicsConfiguration(AbstractGraphicsScreen absScreen, EGLGLCapabilities capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { super(absScreen, capsChosen, capsRequested); this.chooser = chooser; } - public static EGLGraphicsConfiguration create(GLCapabilitiesImmutable capsRequested, AbstractGraphicsScreen absScreen, int cfgID) { + /** + * @param capsRequested + * @param absScreen + * @param eglConfigID {@link EGL#EGL_CONFIG_ID} for which the config is being created for. + * @return + * @throws GLException if invalid EGL display. + */ + public static EGLGraphicsConfiguration create(GLCapabilitiesImmutable capsRequested, AbstractGraphicsScreen absScreen, int eglConfigID) { final AbstractGraphicsDevice absDevice = absScreen.getDevice(); if(null==absDevice || !(absDevice instanceof EGLGraphicsDevice)) { throw new GLException("GraphicsDevice must be a valid EGLGraphicsDevice"); @@ -81,9 +94,11 @@ public class EGLGraphicsConfiguration extends MutableGraphicsConfiguration imple if (dpy == EGL.EGL_NO_DISPLAY) { throw new GLException("Invalid EGL display: "+absDevice); } - final long cfg = EGLConfigId2EGLConfig(dpy, cfgID); + final long cfg = EGLConfigId2EGLConfig(dpy, eglConfigID); if(0 < cfg) { - final EGLGLCapabilities caps = EGLConfig2Capabilities(capsRequested.getGLProfile(), dpy, cfg, false, capsRequested.isOnscreen(), capsRequested.isPBuffer(), false); + final GLRendererQuirks defaultQuirks = GLRendererQuirks.getStickyDeviceQuirks( GLDrawableFactory.getEGLFactory().getDefaultDevice() ); + final int winattrmask = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(capsRequested); + final EGLGLCapabilities caps = EGLConfig2Capabilities(defaultQuirks, (EGLGraphicsDevice)absDevice, capsRequested.getGLProfile(), cfg, winattrmask, false); return new EGLGraphicsConfiguration(absScreen, caps, capsRequested, new DefaultGLCapabilitiesChooser()); } return null; @@ -93,11 +108,12 @@ public class EGLGraphicsConfiguration extends MutableGraphicsConfiguration imple public Object clone() { return super.clone(); } - + void updateGraphicsConfiguration() { + CapabilitiesImmutable capsChosen = getChosenCapabilities(); EGLGraphicsConfiguration newConfig = (EGLGraphicsConfiguration) - GraphicsConfigurationFactory.getFactory(getScreen().getDevice()).chooseGraphicsConfiguration( - getChosenCapabilities(), getRequestedCapabilities(), chooser, getScreen()); + GraphicsConfigurationFactory.getFactory(getScreen().getDevice(), capsChosen).chooseGraphicsConfiguration( + capsChosen, getRequestedCapabilities(), chooser, getScreen(), VisualIDHolder.VID_UNDEFINED); if(null!=newConfig) { // FIXME: setScreen( ... ); setChosenCapabilities(newConfig.getChosenCapabilities()); @@ -108,260 +124,379 @@ public class EGLGraphicsConfiguration extends MutableGraphicsConfiguration imple } public static long EGLConfigId2EGLConfig(long display, int configID) { - int[] attrs = new int[] { + final IntBuffer attrs = Buffers.newDirectIntBuffer(new int[] { EGL.EGL_CONFIG_ID, configID, EGL.EGL_NONE - }; - PointerBuffer configs = PointerBuffer.allocateDirect(1); - int[] numConfigs = new int[1]; + }); + final PointerBuffer configs = PointerBuffer.allocateDirect(1); + final IntBuffer numConfigs = Buffers.newDirectIntBuffer(1); if (!EGL.eglChooseConfig(display, - attrs, 0, + attrs, configs, 1, - numConfigs, 0)) { + numConfigs)) { return 0; } - if (numConfigs[0] == 0) { + if (numConfigs.get(0) == 0) { return 0; } return configs.get(0); } - static int EGLConfigDrawableTypeBits(final long display, final long config) { + public static boolean isEGLConfigValid(long display, long config) { + if(0 == config) { + return false; + } + final IntBuffer val = Buffers.newDirectIntBuffer(1); + + // get the configID + if(!EGL.eglGetConfigAttrib(display, config, EGL.EGL_CONFIG_ID, val)) { + final int eglErr = EGL.eglGetError(); + if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_CONFIG_ID"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(eglErr)); + } + return false; + } + return true; + } + + static int EGLConfigDrawableTypeBits(final EGLGraphicsDevice device, final long config) { int val = 0; - int[] stype = new int[1]; - if(! EGL.eglGetConfigAttrib(display, config, EGL.EGL_SURFACE_TYPE, stype, 0)) { + final IntBuffer stype = Buffers.newDirectIntBuffer(1); + if(! EGL.eglGetConfigAttrib(device.getHandle(), config, EGL.EGL_SURFACE_TYPE, stype)) { throw new GLException("Could not determine EGL_SURFACE_TYPE"); } - if ( 0 != ( stype[0] & EGL.EGL_WINDOW_BIT ) ) { + final int _stype = stype.get(0); + if ( 0 != ( _stype & EGL.EGL_WINDOW_BIT ) ) { val |= GLGraphicsConfigurationUtil.WINDOW_BIT; } - if ( 0 != ( stype[0] & EGL.EGL_PIXMAP_BIT ) ) { + if ( 0 != ( _stype & EGL.EGL_PIXMAP_BIT ) ) { val |= GLGraphicsConfigurationUtil.BITMAP_BIT; } - if ( 0 != ( stype[0] & EGL.EGL_PBUFFER_BIT ) ) { - val |= GLGraphicsConfigurationUtil.PBUFFER_BIT; + if ( 0 != ( _stype & EGL.EGL_PBUFFER_BIT ) ) { + val |= GLGraphicsConfigurationUtil.PBUFFER_BIT | + GLGraphicsConfigurationUtil.FBO_BIT; } - return val; } - public static EGLGLCapabilities EGLConfig2Capabilities(GLProfile glp, long display, long config, - boolean relaxed, boolean onscreen, boolean usePBuffer, boolean forceTransparentFlag) { - ArrayList bucket = new ArrayList(); - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); - if( EGLConfig2Capabilities(bucket, glp, display, config, winattrmask, forceTransparentFlag) ) { - return (EGLGLCapabilities) bucket.get(0); - } else if ( relaxed && EGLConfig2Capabilities(bucket, glp, display, config, GLGraphicsConfigurationUtil.ALL_BITS, forceTransparentFlag) ) { - return (EGLGLCapabilities) bucket.get(0); - } - return null; - } - - public static boolean EGLConfig2Capabilities(ArrayList capsBucket, - GLProfile glp, long display, long config, - int winattrmask, boolean forceTransparentFlag) { - final int allDrawableTypeBits = EGLConfigDrawableTypeBits(display, config); - final int drawableTypeBits = winattrmask & allDrawableTypeBits; - - if( 0 == drawableTypeBits ) { - return false; - } - - final IntBuffer val = Buffers.newDirectIntBuffer(1); + /** + * @param defaultQuirks GLRendererQuirks of the EGLDrawableFactory's defaultDevice + * @param device + * @param glp desired GLProfile, may be null + * @param config + * @param winattrmask + * @param forceTransparentFlag + * @return + */ + public static EGLGLCapabilities EGLConfig2Capabilities(GLRendererQuirks defaultQuirks, EGLGraphicsDevice device, GLProfile glp, + long config, int winattrmask, boolean forceTransparentFlag) { + final long display = device.getHandle(); final int cfgID; final int rType; final int visualID; - + + final int _attributes[] = { + EGL.EGL_CONFIG_ID, // 0 + EGL.EGL_RENDERABLE_TYPE, + EGL.EGL_NATIVE_VISUAL_ID, + EGL.EGL_CONFIG_CAVEAT, + EGL.EGL_RED_SIZE, // 4 + EGL.EGL_GREEN_SIZE, + EGL.EGL_BLUE_SIZE, + EGL.EGL_ALPHA_SIZE, // 7 + EGL.EGL_STENCIL_SIZE, // 8 + EGL.EGL_DEPTH_SIZE, + EGL.EGL_TRANSPARENT_TYPE, // 10 + EGL.EGL_TRANSPARENT_RED_VALUE, + EGL.EGL_TRANSPARENT_GREEN_VALUE, + EGL.EGL_TRANSPARENT_BLUE_VALUE, + EGL.EGL_SAMPLES, // 14 + EGLExt.EGL_COVERAGE_BUFFERS_NV, // 15 + EGLExt.EGL_COVERAGE_SAMPLES_NV + }; + final IntBuffer attributes = Buffers.newDirectIntBuffer(_attributes); + final IntBuffer values = Buffers.newDirectIntBuffer(attributes.remaining()); + EGL.eglGetConfigAttributes(display, config, attributes, values); + // get the configID - if(!EGL.eglGetConfigAttrib(display, config, EGL.EGL_CONFIG_ID, val)) { + if( EGL.EGL_CONFIG_ID != attributes.get(0) ) { if(DEBUG) { // FIXME: this happens on a ATI PC Emulation .. - System.err.println("EGL couldn't retrieve ConfigID for config "+toHexString(config)+", error "+toHexString(EGL.eglGetError())); + System.err.println(dbgCfgFailIntro+"ConfigID"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - return false; + return null; } - cfgID = val.get(0); - - if(!EGL.eglGetConfigAttrib(display, config, EGL.EGL_RENDERABLE_TYPE, val)) { + cfgID = values.get(0); + + if( EGL.EGL_RENDERABLE_TYPE != attributes.get(1) ) { if(DEBUG) { - System.err.println("EGL couldn't retrieve EGL_RENDERABLE_TYPE for config "+toHexString(config)+", error "+toHexString(EGL.eglGetError())); + System.err.println(dbgCfgFailIntro+"EGL_RENDERABLE_TYPE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - return false; + return null; } - rType = val.get(0); - - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_NATIVE_VISUAL_ID, val)) { - visualID = val.get(0); + { + final int rTypeOrig = values.get(1); + if( defaultQuirks.exist(GLRendererQuirks.GLES3ViaEGLES2Config) && 0 != ( EGL.EGL_OPENGL_ES2_BIT & rTypeOrig ) ) { + rType = rTypeOrig | EGLExt.EGL_OPENGL_ES3_BIT_KHR; + } else { + rType = rTypeOrig; + } + } + + if( EGL.EGL_NATIVE_VISUAL_ID == attributes.get(2) ) { + visualID = values.get(2); } else { + if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_NATIVE_VISUAL_ID"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); + } visualID = VisualIDHolder.VID_UNDEFINED; } - - EGLGLCapabilities caps = null; + + EGLGLCapabilities caps = null; try { + if(null == glp) { + glp = EGLGLCapabilities.getCompatible(device, rType); + } + if(!EGLGLCapabilities.isCompatible(glp, rType)) { + if(DEBUG) { + System.err.println("config "+toHexString(config)+": Requested GLProfile "+glp+ + " with quirks "+defaultQuirks+" not compatible with EGL-RenderableType["+EGLGLCapabilities.renderableTypeToString(null, rType)+"]"); + } + return null; + } caps = new EGLGLCapabilities(config, cfgID, visualID, glp, rType); } catch (GLException gle) { if(DEBUG) { System.err.println("config "+toHexString(config)+": "+gle); } - return false; - } - - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_RED_SIZE, val)) { - caps.setRedBits(val.get(0)); + return null; } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_GREEN_SIZE, val)) { - caps.setGreenBits(val.get(0)); + + if( EGL.EGL_CONFIG_CAVEAT == attributes.get(3) ) { + if( EGL.EGL_SLOW_CONFIG == values.get(3) ) { + caps.setHardwareAccelerated(false); + } + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_CONFIG_CAVEAT"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_BLUE_SIZE, val)) { - caps.setBlueBits(val.get(0)); + // ALPHA shall be set at last - due to it's auto setting by the above (!opaque / samples) + if( EGL.EGL_RED_SIZE == attributes.get(4) ) { + caps.setRedBits(values.get(4)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_RED_SIZE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_ALPHA_SIZE, val)) { - caps.setAlphaBits(val.get(0)); + if( EGL.EGL_GREEN_SIZE == attributes.get(5) ) { + caps.setGreenBits(values.get(5)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_GREEN_SIZE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_STENCIL_SIZE, val)) { - caps.setStencilBits(val.get(0)); + if( EGL.EGL_BLUE_SIZE == attributes.get(6) ) { + caps.setBlueBits(values.get(6)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_BLUE_SIZE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_DEPTH_SIZE, val)) { - caps.setDepthBits(val.get(0)); + if( EGL.EGL_ALPHA_SIZE == attributes.get(7) ) { + caps.setAlphaBits(values.get(7)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_ALPHA_SIZE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_SAMPLES, val)) { - caps.setSampleBuffers(val.get(0)>0?true:false); - caps.setNumSamples(val.get(0)); + if( EGL.EGL_STENCIL_SIZE == attributes.get(8) ) { + caps.setStencilBits(values.get(8)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_STENCIL_SIZE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(!caps.getSampleBuffers()) { - // try NV_coverage_sample extension - if(EGL.eglGetConfigAttrib(display, config, EGLExt.EGL_COVERAGE_BUFFERS_NV, val)) { - if(val.get(0)>0 && - EGL.eglGetConfigAttrib(display, config, EGLExt.EGL_COVERAGE_SAMPLES_NV, val)) { - caps.setSampleExtension(GLGraphicsConfigurationUtil.NV_coverage_sample); - caps.setSampleBuffers(true); - caps.setNumSamples(val.get(0)); - } - } + if( EGL.EGL_DEPTH_SIZE == attributes.get(9) ) { + caps.setDepthBits(values.get(9)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_DEPTH_SIZE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(forceTransparentFlag) { + if( forceTransparentFlag ) { caps.setBackgroundOpaque(false); - } else if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_TRANSPARENT_TYPE, val)) { - caps.setBackgroundOpaque(val.get(0) != EGL.EGL_TRANSPARENT_RGB); + } else if( EGL.EGL_TRANSPARENT_TYPE == attributes.get(10) ) { + caps.setBackgroundOpaque(values.get(10) != EGL.EGL_TRANSPARENT_RGB); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_TRANSPARENT_TYPE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } if(!caps.isBackgroundOpaque()) { - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_TRANSPARENT_RED_VALUE, val)) { - caps.setTransparentRedValue(val.get(0)==EGL.EGL_DONT_CARE?-1:val.get(0)); + if( EGL.EGL_TRANSPARENT_RED_VALUE == attributes.get(11) ) { + final int v = values.get(11); + caps.setTransparentRedValue(EGL.EGL_DONT_CARE==v?-1:v); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_TRANSPARENT_RED_VALUE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_TRANSPARENT_GREEN_VALUE, val)) { - caps.setTransparentGreenValue(val.get(0)==EGL.EGL_DONT_CARE?-1:val.get(0)); + if( EGL.EGL_TRANSPARENT_GREEN_VALUE == attributes.get(12) ) { + final int v = values.get(12); + caps.setTransparentGreenValue(EGL.EGL_DONT_CARE==v?-1:v); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_TRANSPARENT_GREEN_VALUE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_TRANSPARENT_BLUE_VALUE, val)) { - caps.setTransparentBlueValue(val.get(0)==EGL.EGL_DONT_CARE?-1:val.get(0)); + if( EGL.EGL_TRANSPARENT_BLUE_VALUE == attributes.get(13) ) { + final int v = values.get(13); + caps.setTransparentBlueValue(EGL.EGL_DONT_CARE==v?-1:v); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_TRANSPARENT_BLUE_VALUE"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); } - /** Not defined in EGL - if(EGL.eglGetConfigAttrib(display, config, EGL.EGL_TRANSPARENT_ALPHA_VALUE, val)) { - caps.setTransparentAlphaValue(val.get(0)==EGL.EGL_DONT_CARE?-1:val.get(0)); + /** Not defined in EGL + if( EGL.EGL_TRANSPARENT_ALPHA_VALUE == attributes.get(??) ) { + final int v = values.get(??); + caps.setTransparentAlphaValue(EGL.EGL_DONT_CARE==v?-1:v); + } else if(DEBUG) { + System.err.println(dbgStr01+"EGL_TRANSPARENT_ALPHA_VALUE"+dbgStr02+toHexString(config)+dbgEGLCfgFailError+toHexString(EGL.eglGetError())); } */ } - return GLGraphicsConfigurationUtil.addGLCapabilitiesPermutations(capsBucket, caps, drawableTypeBits ); + if( EGL.EGL_SAMPLES == attributes.get(14) ) { + final int numSamples = values.get(14); + caps.setSampleBuffers(numSamples>0?true:false); + caps.setNumSamples(numSamples); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_SAMPLES"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); + } + if(!caps.getSampleBuffers()) { + // try NV_coverage_sample extension + if( EGLExt.EGL_COVERAGE_BUFFERS_NV == attributes.get(15) ) { + final boolean enabled = values.get(15) > 0; + if( enabled && EGLExt.EGL_COVERAGE_SAMPLES_NV == attributes.get(16) ) { + caps.setSampleExtension(GLGraphicsConfigurationUtil.NV_coverage_sample); + caps.setSampleBuffers(true); + caps.setNumSamples(values.get(16)); + } else if(DEBUG) { + System.err.println(dbgCfgFailIntro+"EGL_COVERAGE_SAMPLES_NV"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); + } + } /** else if(DEBUG) { // Not required - vendor extension - don't be verbose! + System.err.println(dbgCfgFailIntro+"EGL_COVERAGE_BUFFERS_NV"+dbgCfgFailForConfig+toHexString(config)+dbgCfgFailError+toHexString(EGL.eglGetError())); + } */ + } + + // Since the passed GLProfile may be null, + // we use EGL_RENDERABLE_TYPE derived profile as created in the EGLGLCapabilities constructor. + final int availableTypeBits = EGLConfigDrawableTypeBits(device, config); + final int drawableTypeBits = winattrmask & availableTypeBits; + + if( 0 == drawableTypeBits ) { + return null; + } + + return (EGLGLCapabilities) GLGraphicsConfigurationUtil.fixWinAttribBitsAndHwAccel(device, drawableTypeBits, caps); } - public static int[] GLCapabilities2AttribList(GLCapabilitiesImmutable caps) { - int[] attrs = new int[32]; + public static IntBuffer GLCapabilities2AttribList(GLCapabilitiesImmutable caps) { + final IntBuffer attrs = Buffers.newDirectIntBuffer(32); int idx=0; - attrs[idx++] = EGL.EGL_SURFACE_TYPE; - attrs[idx++] = caps.isOnscreen() ? ( EGL.EGL_WINDOW_BIT ) : ( caps.isPBuffer() ? EGL.EGL_PBUFFER_BIT : EGL.EGL_PIXMAP_BIT ) ; + attrs.put(idx++, EGL.EGL_SURFACE_TYPE); + final int surfaceType; + if( caps.isOnscreen() ) { + surfaceType = EGL.EGL_WINDOW_BIT; + } else if( caps.isFBO() ) { + surfaceType = EGL.EGL_PBUFFER_BIT; // native replacement! + } else if( caps.isPBuffer() ) { + surfaceType = EGL.EGL_PBUFFER_BIT; + } else if( caps.isBitmap() ) { + surfaceType = EGL.EGL_PIXMAP_BIT; + } else { + throw new GLException("no surface type set in caps: "+caps); + } + attrs.put(idx++, surfaceType); - attrs[idx++] = EGL.EGL_RED_SIZE; - attrs[idx++] = caps.getRedBits(); + attrs.put(idx++, EGL.EGL_RED_SIZE); + attrs.put(idx++, caps.getRedBits()); - attrs[idx++] = EGL.EGL_GREEN_SIZE; - attrs[idx++] = caps.getGreenBits(); + attrs.put(idx++, EGL.EGL_GREEN_SIZE); + attrs.put(idx++, caps.getGreenBits()); - attrs[idx++] = EGL.EGL_BLUE_SIZE; - attrs[idx++] = caps.getBlueBits(); + attrs.put(idx++, EGL.EGL_BLUE_SIZE); + attrs.put(idx++, caps.getBlueBits()); if(caps.getAlphaBits()>0) { - attrs[idx++] = EGL.EGL_ALPHA_SIZE; - attrs[idx++] = caps.getAlphaBits(); + attrs.put(idx++, EGL.EGL_ALPHA_SIZE); + attrs.put(idx++, caps.getAlphaBits()); } - + if(caps.getStencilBits()>0) { - attrs[idx++] = EGL.EGL_STENCIL_SIZE; - attrs[idx++] = caps.getStencilBits(); + attrs.put(idx++, EGL.EGL_STENCIL_SIZE); + attrs.put(idx++, caps.getStencilBits()); } - attrs[idx++] = EGL.EGL_DEPTH_SIZE; - attrs[idx++] = caps.getDepthBits(); + attrs.put(idx++, EGL.EGL_DEPTH_SIZE); + attrs.put(idx++, caps.getDepthBits()); if(caps.getSampleBuffers()) { if(caps.getSampleExtension().equals(GLGraphicsConfigurationUtil.NV_coverage_sample)) { - attrs[idx++] = EGLExt.EGL_COVERAGE_BUFFERS_NV; - attrs[idx++] = 1; - attrs[idx++] = EGLExt.EGL_COVERAGE_SAMPLES_NV; - attrs[idx++] = caps.getNumSamples(); + attrs.put(idx++, EGLExt.EGL_COVERAGE_BUFFERS_NV); + attrs.put(idx++, 1); + attrs.put(idx++, EGLExt.EGL_COVERAGE_SAMPLES_NV); + attrs.put(idx++, caps.getNumSamples()); } else { // try default .. - attrs[idx++] = EGL.EGL_SAMPLE_BUFFERS; - attrs[idx++] = 1; - attrs[idx++] = EGL.EGL_SAMPLES; - attrs[idx++] = caps.getNumSamples(); + attrs.put(idx++, EGL.EGL_SAMPLE_BUFFERS); + attrs.put(idx++, 1); + attrs.put(idx++, EGL.EGL_SAMPLES); + attrs.put(idx++, caps.getNumSamples()); } } - attrs[idx++] = EGL.EGL_TRANSPARENT_TYPE; - attrs[idx++] = caps.isBackgroundOpaque() ? EGL.EGL_NONE : EGL.EGL_TRANSPARENT_TYPE; + attrs.put(idx++, EGL.EGL_TRANSPARENT_TYPE); + attrs.put(idx++, caps.isBackgroundOpaque() ? EGL.EGL_NONE : EGL.EGL_TRANSPARENT_TYPE); // 22 if(!caps.isBackgroundOpaque()) { - attrs[idx++] = EGL.EGL_TRANSPARENT_RED_VALUE; - attrs[idx++] = caps.getTransparentRedValue()>=0?caps.getTransparentRedValue():EGL.EGL_DONT_CARE; + attrs.put(idx++, EGL.EGL_TRANSPARENT_RED_VALUE); + attrs.put(idx++, caps.getTransparentRedValue()>=0?caps.getTransparentRedValue():EGL.EGL_DONT_CARE); - attrs[idx++] = EGL.EGL_TRANSPARENT_GREEN_VALUE; - attrs[idx++] = caps.getTransparentGreenValue()>=0?caps.getTransparentGreenValue():EGL.EGL_DONT_CARE; + attrs.put(idx++, EGL.EGL_TRANSPARENT_GREEN_VALUE); + attrs.put(idx++, caps.getTransparentGreenValue()>=0?caps.getTransparentGreenValue():EGL.EGL_DONT_CARE); - attrs[idx++] = EGL.EGL_TRANSPARENT_BLUE_VALUE; - attrs[idx++] = caps.getTransparentBlueValue()>=0?caps.getTransparentBlueValue():EGL.EGL_DONT_CARE; + attrs.put(idx++, EGL.EGL_TRANSPARENT_BLUE_VALUE); + attrs.put(idx++, caps.getTransparentBlueValue()>=0?caps.getTransparentBlueValue():EGL.EGL_DONT_CARE); /** Not define in EGL - attrs[idx++] = EGL.EGL_TRANSPARENT_ALPHA_VALUE; - attrs[idx++] = caps.getTransparentAlphaValue()>=0?caps.getTransparentAlphaValue():EGL.EGL_DONT_CARE; */ + attrs.put(idx++, EGL.EGL_TRANSPARENT_ALPHA_VALUE; + attrs.put(idx++, caps.getTransparentAlphaValue()>=0?caps.getTransparentAlphaValue():EGL.EGL_DONT_CARE; */ } - // 28 - attrs[idx++] = EGL.EGL_RENDERABLE_TYPE; + // 28 + attrs.put(idx++, EGL.EGL_RENDERABLE_TYPE); if(caps.getGLProfile().usesNativeGLES1()) { - attrs[idx++] = EGL.EGL_OPENGL_ES_BIT; + attrs.put(idx++, EGL.EGL_OPENGL_ES_BIT); } else if(caps.getGLProfile().usesNativeGLES2()) { - attrs[idx++] = EGL.EGL_OPENGL_ES2_BIT; + attrs.put(idx++, EGL.EGL_OPENGL_ES2_BIT); + } else if(caps.getGLProfile().usesNativeGLES3()) { + if( GLRendererQuirks.existStickyDeviceQuirk(GLDrawableFactory.getEGLFactory().getDefaultDevice(), GLRendererQuirks.GLES3ViaEGLES2Config) ) { + attrs.put(idx++, EGL.EGL_OPENGL_ES2_BIT); + } else { + attrs.put(idx++, EGLExt.EGL_OPENGL_ES3_BIT_KHR); + } } else { - attrs[idx++] = EGL.EGL_OPENGL_BIT; + attrs.put(idx++, EGL.EGL_OPENGL_BIT); } // 30 - attrs[idx++] = EGL.EGL_NONE; + attrs.put(idx++, EGL.EGL_NONE); return attrs; } - public static int[] CreatePBufferSurfaceAttribList(int width, int height, int texFormat) { - int[] attrs = new int[16]; + public static IntBuffer CreatePBufferSurfaceAttribList(int width, int height, int texFormat) { + IntBuffer attrs = Buffers.newDirectIntBuffer(16); int idx=0; - attrs[idx++] = EGL.EGL_WIDTH; - attrs[idx++] = width; + attrs.put(idx++, EGL.EGL_WIDTH); + attrs.put(idx++, width); - attrs[idx++] = EGL.EGL_HEIGHT; - attrs[idx++] = height; + attrs.put(idx++, EGL.EGL_HEIGHT); + attrs.put(idx++, height); - attrs[idx++] = EGL.EGL_TEXTURE_FORMAT; - attrs[idx++] = texFormat; + attrs.put(idx++, EGL.EGL_TEXTURE_FORMAT); + attrs.put(idx++, texFormat); - attrs[idx++] = EGL.EGL_TEXTURE_TARGET; - attrs[idx++] = EGL.EGL_NO_TEXTURE==texFormat ? EGL.EGL_NO_TEXTURE : EGL.EGL_TEXTURE_2D; + attrs.put(idx++, EGL.EGL_TEXTURE_TARGET); + attrs.put(idx++, EGL.EGL_NO_TEXTURE==texFormat ? EGL.EGL_NO_TEXTURE : EGL.EGL_TEXTURE_2D); - attrs[idx++] = EGL.EGL_NONE; + attrs.put(idx++, EGL.EGL_NONE); return attrs; } @@ -376,6 +511,6 @@ public class EGLGraphicsConfiguration extends MutableGraphicsConfiguration imple } - private GLCapabilitiesChooser chooser; + private final GLCapabilitiesChooser chooser; } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfigurationFactory.java index 904110eb2..5cfa378cb 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -38,15 +38,14 @@ import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; -import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.nativewindow.NativeWindowFactory; - import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.GLDrawableFactory; @@ -54,6 +53,7 @@ import javax.media.opengl.GLDrawableFactory; import com.jogamp.common.nio.Buffers; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.nativewindow.egl.EGLGraphicsDevice; +import com.jogamp.opengl.GLRendererQuirks; import jogamp.opengl.GLGraphicsConfigurationFactory; import jogamp.opengl.GLGraphicsConfigurationUtil; @@ -74,43 +74,51 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact static VisualIDHolder.VIDComparator EglCfgIDComparator = new VisualIDHolder.VIDComparator(VisualIDHolder.VIDType.EGL_CONFIG); static GraphicsConfigurationFactory nativeGraphicsConfigurationFactory = null; static GraphicsConfigurationFactory kdeglGraphicsConfigurationFactory = null; - + static GraphicsConfigurationFactory fallbackGraphicsConfigurationFactory = null; + static void registerFactory() { GraphicsConfigurationFactory eglFactory = new EGLGraphicsConfigurationFactory(); - + // become the pre-selector for X11/.. to match the native visual id w/ EGL, if native ES is selected final String nwType = NativeWindowFactory.getNativeWindowType(false); if(NativeWindowFactory.TYPE_X11 == nwType) { - nativeGraphicsConfigurationFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, eglFactory); + nativeGraphicsConfigurationFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, GLCapabilitiesImmutable.class, eglFactory); + if(null != nativeGraphicsConfigurationFactory) { + fallbackGraphicsConfigurationFactory = nativeGraphicsConfigurationFactory; + } else { + fallbackGraphicsConfigurationFactory = GraphicsConfigurationFactory.getFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, CapabilitiesImmutable.class); + } } /* else if(NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false)) { nativeGraphicsConfigurationFactory = GraphicsConfigurationFactory.registerFactory(javax.media.nativewindow.windows.WindowsGraphicsDevice.class, eglFactory); - } else if(NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) { + } else if(NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) { } */ - + // become the selector for KD/EGL .. - kdeglGraphicsConfigurationFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.egl.EGLGraphicsDevice.class, eglFactory); + kdeglGraphicsConfigurationFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.egl.EGLGraphicsDevice.class, GLCapabilitiesImmutable.class, eglFactory); } - + static void unregisterFactory() { final String nwType = NativeWindowFactory.getNativeWindowType(false); if(NativeWindowFactory.TYPE_X11 == nwType) { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, nativeGraphicsConfigurationFactory); + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, GLCapabilitiesImmutable.class, nativeGraphicsConfigurationFactory); } /* else if(NativeWindowFactory.TYPE_WINDOWS == NativeWindowFactory.getNativeWindowType(false)) { GraphicsConfigurationFactory.registerFactory(javax.media.nativewindow.windows.WindowsGraphicsDevice.class, nativeGraphicsConfigurationFactory); - } else if(NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) { + } else if(NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) { } */ nativeGraphicsConfigurationFactory = null; - - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.egl.EGLGraphicsDevice.class, kdeglGraphicsConfigurationFactory); + fallbackGraphicsConfigurationFactory = null; + + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.egl.EGLGraphicsDevice.class, GLCapabilitiesImmutable.class, kdeglGraphicsConfigurationFactory); kdeglGraphicsConfigurationFactory = null; } - + private EGLGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl ( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { + CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { if (absScreen == null) { throw new IllegalArgumentException("This NativeWindowFactory accepts only AbstractGraphicsDevice objects"); } @@ -132,21 +140,21 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact AbstractGraphicsDevice absDevice = absScreen.getDevice(); if(null==absDevice) { throw new GLException("Null AbstractGraphicsDevice"); - } - + } + AbstractGraphicsConfiguration cfg = null; - + if( absDevice instanceof EGLGraphicsDevice ) { cfg = chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable) capsChosen, (GLCapabilitiesImmutable) capsRequested, (GLCapabilitiesChooser) chooser, - absScreen, VisualIDHolder.VID_UNDEFINED, false); + absScreen, nativeVisualID, false); } else { - // handle non native cases (X11, ..) - if(null == nativeGraphicsConfigurationFactory) { - throw new InternalError("Native GraphicsConfigurationFactory is null, but call issued for device: "+absDevice+" of type "+absDevice.getClass().getSimpleName()); + // handle non native cases (X11, ..) + if(null == fallbackGraphicsConfigurationFactory) { + throw new InternalError("Native fallback GraphicsConfigurationFactory is null, but call issued for device: "+absDevice+" of type "+absDevice.getClass().getSimpleName()); } - + if(glCapsChosen.getGLProfile().usesNativeGLES()) { if(DEBUG) { System.err.println("EGLGraphicsConfigurationFactory.choose..: Handle native device "+absDevice.getClass().getSimpleName()); @@ -154,7 +162,7 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact cfg = chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable) capsChosen, (GLCapabilitiesImmutable) capsRequested, (GLCapabilitiesChooser) chooser, - absScreen, VisualIDHolder.VID_UNDEFINED, false); + absScreen, nativeVisualID, false); if(null == cfg || VisualIDHolder.VID_UNDEFINED == cfg.getVisualID(VIDType.NATIVE)) { cfg = null; if(DEBUG) { @@ -165,23 +173,25 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact if(null == cfg) { // fwd to native config factory (only X11 for now) if(DEBUG) { - System.err.println("EGLGraphicsConfigurationFactory.choose..: Delegate to "+nativeGraphicsConfigurationFactory.getClass().getSimpleName()); + System.err.println("EGLGraphicsConfigurationFactory.choose..: Delegate to "+fallbackGraphicsConfigurationFactory.getClass().getSimpleName()); } - cfg = nativeGraphicsConfigurationFactory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, absScreen); - } + cfg = fallbackGraphicsConfigurationFactory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, absScreen, nativeVisualID); + } } return cfg; } protected static List<GLCapabilitiesImmutable> getAvailableCapabilities(EGLDrawableFactory factory, AbstractGraphicsDevice device) { - EGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateEGLSharedResource(device); + final EGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResourceImpl(device); if(null == sharedResource) { throw new GLException("Shared resource for device n/a: "+device); } - EGLGraphicsDevice eglDevice = sharedResource.getDevice(); - long eglDisplay = eglDevice.getHandle(); - - List/*<EGLGLCapabilities>*/ availableCaps = null; + final EGLGraphicsDevice eglDevice = sharedResource.getDevice(); + final long eglDisplay = eglDevice.getHandle(); + if(0 == eglDisplay) { + throw new GLException("null eglDisplay"); + } + List<GLCapabilitiesImmutable> availableCaps = null; IntBuffer numConfigs = Buffers.newDirectIntBuffer(1); if(!EGL.eglGetConfigs(eglDisplay, null, 0, numConfigs)) { @@ -197,19 +207,18 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact throw new GLException("Graphics configuration get all configs (eglGetConfigs) call failed, error "+toHexString(EGL.eglGetError())); } if (numConfigs.get(0) > 0) { - availableCaps = eglConfigs2GLCaps(null, eglDisplay, configs, numConfigs.get(0), GLGraphicsConfigurationUtil.ALL_BITS, false); + availableCaps = eglConfigs2GLCaps(eglDevice, null, configs, numConfigs.get(0), GLGraphicsConfigurationUtil.ALL_BITS, false /* forceTransparentFlag */, false /* onlyFirstValid */); if( null != availableCaps && availableCaps.size() > 1) { Collections.sort(availableCaps, EglCfgIDComparator); } } - return availableCaps; } public static EGLGraphicsConfiguration chooseGraphicsConfigurationStatic(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, - AbstractGraphicsScreen absScreen, int nativeVisualID, + AbstractGraphicsScreen absScreen, int nativeVisualID, boolean forceTransparentFlag) { if (capsChosen == null) { capsChosen = new GLCapabilities(null); @@ -222,101 +231,107 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact if(null==absDevice) { throw new GLException("Null AbstractGraphicsDevice"); } - - final long eglDisplay; + + final EGLGraphicsDevice eglDevice; final boolean ownEGLDisplay; - if( !(absDevice instanceof EGLGraphicsDevice) ) { - eglDisplay = EGLDisplayUtil.eglGetDisplay(absDevice.getHandle()); - if (eglDisplay == EGL.EGL_NO_DISPLAY) { - throw new GLException("Could not get EGL display from: "+absDevice); - } - if (!EGLDisplayUtil.eglInitialize(eglDisplay, null, null)) { - throw new GLException("eglInitialize failed eglDisplay 0x"+Long.toHexString(eglDisplay)+", "+absDevice+", error 0x"+Integer.toHexString(EGL.eglGetError())); - } - ownEGLDisplay = true; - } else { - eglDisplay = absDevice.getHandle(); - if (eglDisplay == EGL.EGL_NO_DISPLAY) { - throw new GLException("Invalid EGL display: "+absDevice); + if( absDevice instanceof EGLGraphicsDevice ) { + eglDevice = (EGLGraphicsDevice) absDevice; + if (eglDevice.getHandle() == EGL.EGL_NO_DISPLAY) { + throw new GLException("Invalid EGL display: "+eglDevice); } ownEGLDisplay = false; + } else { + eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(absDevice.getHandle(), absDevice.getConnection(), absDevice.getUnitID()); + eglDevice.open(); + ownEGLDisplay = true; } - EGLDrawableFactory factory = (EGLDrawableFactory) GLDrawableFactory.getEGLFactory(); - capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, factory.canCreateGLPbuffer(absDevice) ); - - GLProfile glp = capsChosen.getGLProfile(); - GLCapabilities fixedCaps; - - EGLGraphicsConfiguration res = eglChooseConfig(eglDisplay, capsChosen, capsReq, chooser, absScreen, nativeVisualID, forceTransparentFlag); + final GLProfile glp = capsChosen.getGLProfile(); + capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, GLDrawableFactory.getEGLFactory(), absDevice); + EGLGraphicsConfiguration res = eglChooseConfig(eglDevice, capsChosen, capsReq, chooser, absScreen, nativeVisualID, forceTransparentFlag); if(null==res) { if(DEBUG) { System.err.println("eglChooseConfig failed with given capabilities "+capsChosen); } - + // Last try .. add a fixed embedded profile [ATI, Nokia, Intel, ..] // // rgb888 - d16, s4 - fixedCaps = new GLCapabilities(glp); + final GLCapabilities fixedCaps = new GLCapabilities(glp); + fixedCaps.setSampleBuffers(true); + fixedCaps.setNumSamples(4); fixedCaps.setRedBits(8); fixedCaps.setGreenBits(8); fixedCaps.setBlueBits(8); fixedCaps.setDepthBits(16); - fixedCaps.setSampleBuffers(true); - fixedCaps.setNumSamples(4); + if( !capsChosen.isOnscreen() ) { + fixedCaps.setOnscreen(false); + fixedCaps.setPBuffer(capsChosen.isPBuffer()); + fixedCaps.setFBO(capsChosen.isFBO()); + } if(DEBUG) { System.err.println("trying fixed caps (1): "+fixedCaps); } - res = eglChooseConfig(eglDisplay, fixedCaps, capsReq, chooser, absScreen, nativeVisualID, false); + res = eglChooseConfig(eglDevice, fixedCaps, capsReq, chooser, absScreen, nativeVisualID, false); } if(null==res) { // // rgb565 - d16, s0 - fixedCaps = new GLCapabilities(glp); + final GLCapabilities fixedCaps = new GLCapabilities(glp); fixedCaps.setRedBits(5); fixedCaps.setGreenBits(6); fixedCaps.setBlueBits(5); fixedCaps.setDepthBits(16); + if( !capsChosen.isOnscreen() ) { + fixedCaps.setOnscreen(false); + fixedCaps.setPBuffer(capsChosen.isPBuffer()); + fixedCaps.setFBO(capsChosen.isFBO()); + } if(DEBUG) { System.err.println("trying fixed caps (2): "+fixedCaps); } - res = eglChooseConfig(eglDisplay, fixedCaps, capsReq, chooser, absScreen, nativeVisualID, false); + res = eglChooseConfig(eglDevice, fixedCaps, capsReq, chooser, absScreen, nativeVisualID, false); } if(null==res) { // // rgb565 - d16, s4 - fixedCaps = new GLCapabilities(glp); + final GLCapabilities fixedCaps = new GLCapabilities(glp); + fixedCaps.setSampleBuffers(true); + fixedCaps.setNumSamples(4); fixedCaps.setRedBits(5); fixedCaps.setGreenBits(6); fixedCaps.setBlueBits(5); fixedCaps.setDepthBits(16); - fixedCaps.setSampleBuffers(true); - fixedCaps.setNumSamples(4); + if( !capsChosen.isOnscreen() ) { + fixedCaps.setOnscreen(false); + fixedCaps.setPBuffer(capsChosen.isPBuffer()); + fixedCaps.setFBO(capsChosen.isFBO()); + } if(DEBUG) { System.err.println("trying fixed caps (3): "+fixedCaps); } - res = eglChooseConfig(eglDisplay, fixedCaps, capsReq, chooser, absScreen, nativeVisualID, false); + res = eglChooseConfig(eglDevice, fixedCaps, capsReq, chooser, absScreen, nativeVisualID, false); } if(null==res) { throw new GLException("Graphics configuration failed [direct caps, eglGetConfig/chooser and fixed-caps(1-3)]"); } if(ownEGLDisplay) { - ((EGLGLCapabilities) res.getChosenCapabilities()).setEGLConfig(0); // eglDisplay: EOL - EGLDisplayUtil.eglTerminate(eglDisplay); + ((EGLGLCapabilities) res.getChosenCapabilities()).setEGLConfig(0); // eglDisplay: EOL + eglDevice.close(); } return res; } - static EGLGraphicsConfiguration eglChooseConfig(long eglDisplay, + + static EGLGraphicsConfiguration eglChooseConfig(EGLGraphicsDevice device, GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID, boolean forceTransparentFlag) { + final long eglDisplay = device.getHandle(); final GLProfile glp = capsChosen.getGLProfile(); - final boolean onscreen = capsChosen.isOnscreen(); - final boolean usePBuffer = capsChosen.isPBuffer(); - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); - List/*<EGLGLCapabilities>*/ availableCaps = null; + final int winattrmask = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(capsChosen); + List<GLCapabilitiesImmutable> availableCaps = null; int recommendedIndex = -1; long recommendedEGLConfig = -1; IntBuffer numConfigs = Buffers.newDirectIntBuffer(1); @@ -327,35 +342,58 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact if(0 == numConfigs.get(0)) { throw new GLException("EGLGraphicsConfiguration.eglChooseConfig: Get maxConfigs (eglGetConfigs) no configs"); } + final int numEGLConfigs = numConfigs.get(0); if (DEBUG) { - System.err.println("EGLGraphicsConfiguration.eglChooseConfig: eglChooseConfig maxConfigs: "+numConfigs.get(0)); - System.err.println("EGLGraphicsConfiguration.eglChooseConfig: eglDisplay "+toHexString(eglDisplay)+", "+capsChosen+", nativeVisualID "+toHexString(nativeVisualID)); + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: eglChooseConfig eglDisplay "+toHexString(eglDisplay)+ + ", nativeVisualID "+toHexString(nativeVisualID)+ + ", capsChosen "+capsChosen+", winbits "+GLGraphicsConfigurationUtil.winAttributeBits2String(null, winattrmask).toString()+ + ", fboAvail "+GLContext.isFBOAvailable(device, glp)+ + ", device "+device+", "+device.getUniqueID()+ + ", numEGLConfigs "+numEGLConfigs); } - final IntBuffer attrs = Buffers.newDirectIntBuffer(EGLGraphicsConfiguration.GLCapabilities2AttribList(capsChosen)); + final IntBuffer attrs = EGLGraphicsConfiguration.GLCapabilities2AttribList(capsChosen); PointerBuffer configs = PointerBuffer.allocateDirect(numConfigs.get(0)); - // 1st choice: get GLCapabilities based on users GLCapabilities + // 1st choice: get GLCapabilities based on users GLCapabilities // setting recommendedIndex as preferred choice // skipped if nativeVisualID is given - if( VisualIDHolder.VID_UNDEFINED != nativeVisualID || !EGL.eglChooseConfig(eglDisplay, attrs, configs, configs.capacity(), numConfigs) ) { + final boolean hasEGLChosenCaps; + if( VisualIDHolder.VID_UNDEFINED == nativeVisualID ) { + if( !EGL.eglChooseConfig(eglDisplay, attrs, configs, configs.capacity(), numConfigs) ) { + if(DEBUG) { + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 eglChooseConfig: false"); + } + numConfigs.put(0, 0); + hasEGLChosenCaps = false; + } else { + hasEGLChosenCaps = numConfigs.get(0)>0; + } + } else { if(DEBUG) { - System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 eglChooseConfig: false"); + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: Skipped due to given visualID: "+toHexString(nativeVisualID)); } - } else if (numConfigs.get(0) > 0) { - availableCaps = eglConfigs2GLCaps(glp, eglDisplay, configs, numConfigs.get(0), winattrmask, forceTransparentFlag); + hasEGLChosenCaps = false; + } + final boolean useRecommendedIndex = hasEGLChosenCaps && !forceTransparentFlag && capsChosen.isBackgroundOpaque(); // only use recommended idx if not translucent + final boolean skipCapsChooser = null == chooser && useRecommendedIndex; // fast path: skip choosing if using recommended idx and null chooser is used + if( hasEGLChosenCaps ) { + availableCaps = eglConfigs2GLCaps(device, glp, configs, numConfigs.get(0), winattrmask, forceTransparentFlag, skipCapsChooser /* onlyFirsValid */); if(availableCaps.size() > 0) { recommendedEGLConfig = configs.get(0); recommendedIndex = 0; if (DEBUG) { System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 eglChooseConfig: recommended fbcfg " + toHexString(recommendedEGLConfig) + ", idx " + recommendedIndex); + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 useRecommendedIndex "+useRecommendedIndex+", skipCapsChooser "+skipCapsChooser); System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 fbcfg caps " + availableCaps.get(recommendedIndex)); } } else if (DEBUG) { System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 eglChooseConfig: no caps for recommended fbcfg " + toHexString(configs.get(0))); + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 useRecommendedIndex "+useRecommendedIndex+", skipCapsChooser "+skipCapsChooser); } } else if (DEBUG) { System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 eglChooseConfig: no configs"); + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #1 useRecommendedIndex "+useRecommendedIndex+", skipCapsChooser "+skipCapsChooser); } // 2nd choice: get all GLCapabilities available, no preferred recommendedIndex available @@ -368,7 +406,7 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact throw new GLException("EGLGraphicsConfiguration.eglChooseConfig: #2 Get all configs (eglGetConfigs) call failed, error "+toHexString(EGL.eglGetError())); } if (numConfigs.get(0) > 0) { - availableCaps = eglConfigs2GLCaps(glp, eglDisplay, configs, numConfigs.get(0), winattrmask, forceTransparentFlag); + availableCaps = eglConfigs2GLCaps(device, glp, configs, numConfigs.get(0), winattrmask, forceTransparentFlag, false /* onlyFirsValid */); } } @@ -376,10 +414,12 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact if(DEBUG) { // FIXME: this happens on a ATI PC Emulation .. System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #2 Graphics configuration 1st choice and 2nd choice failed - no configs"); + availableCaps = eglConfigs2GLCaps(device, glp, configs, numConfigs.get(0), GLGraphicsConfigurationUtil.ALL_BITS, forceTransparentFlag, false /* onlyFirsValid */); + printCaps("AllCaps", availableCaps, System.err); } return null; } - + if(DEBUG) { System.err.println("EGLGraphicsConfiguration.eglChooseConfig: got configs: "+availableCaps.size()); for(int i=0; i<availableCaps.size(); i++) { @@ -387,11 +427,16 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact } } - if( VisualIDHolder.VID_UNDEFINED != nativeVisualID ) { - List/*<EGLGLCapabilities>*/ removedCaps = new ArrayList(); + if( VisualIDHolder.VID_UNDEFINED != nativeVisualID ) { // implies !hasEGLChosenCaps + List<GLCapabilitiesImmutable> removedCaps = new ArrayList<GLCapabilitiesImmutable>(); for(int i=0; i<availableCaps.size(); ) { - VisualIDHolder vidh = (VisualIDHolder) availableCaps.get(i); - if(vidh.getVisualID(VIDType.NATIVE) != nativeVisualID) { + final GLCapabilitiesImmutable aCap = availableCaps.get(i); + if(aCap.getVisualID(VIDType.NATIVE) != nativeVisualID) { + if(DEBUG) { System.err.println("Remove["+i+"] (mismatch VisualID): "+aCap); } + removedCaps.add(availableCaps.remove(i)); + } else if( 0 == aCap.getDepthBits() && 0 < capsChosen.getDepthBits() ) { + // Hack for HiSilicon/Vivante/Immersion.16 Renderer .. + if(DEBUG) { System.err.println("Remove["+i+"] (mismatch depth-bits): "+aCap); } removedCaps.add(availableCaps.remove(i)); } else { i++; @@ -404,10 +449,18 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact } } else if(DEBUG) { System.err.println("EGLGraphicsConfiguration.eglChooseConfig: post filter nativeVisualID "+toHexString(nativeVisualID)+" got configs: "+availableCaps.size()); + for(int i=0; i<availableCaps.size(); i++) { + System.err.println(i+": "+availableCaps.get(i)); + } } } - final int chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); + final int chosenIndex; + if( skipCapsChooser && 0 <= recommendedIndex ) { + chosenIndex = recommendedIndex; + } else { + chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); + } if ( 0 > chosenIndex ) { if (DEBUG) { System.err.println("EGLGraphicsConfiguration.eglChooseConfig: #2 chooseCapabilities failed"); @@ -415,46 +468,32 @@ public class EGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFact return null; } final EGLGLCapabilities chosenCaps = (EGLGLCapabilities) availableCaps.get(chosenIndex); + final EGLGraphicsConfiguration res = new EGLGraphicsConfiguration(absScreen, chosenCaps, capsRequested, chooser); if (DEBUG) { - System.err.println("EGLGraphicsConfiguration.eglChooseConfig: X chosen :"+chosenIndex+", eglConfig: "+toHexString(chosenCaps.getEGLConfig())+", "+chosenCaps); + System.err.println("EGLGraphicsConfiguration.eglChooseConfig: X chosen :"+chosenIndex+", eglConfig: "+toHexString(chosenCaps.getEGLConfig())+": "+res); } - return new EGLGraphicsConfiguration(absScreen, chosenCaps, capsRequested, chooser); + return res; } - static List/*<GLCapabilitiesImmutable>*/ eglConfigs2GLCaps(GLProfile glp, long eglDisplay, PointerBuffer configs, int num, int winattrmask, boolean forceTransparentFlag) { - ArrayList caps = new ArrayList(num); + static List<GLCapabilitiesImmutable> eglConfigs2GLCaps(EGLGraphicsDevice device, GLProfile glp, PointerBuffer configs, int num, int winattrmask, boolean forceTransparentFlag, boolean onlyFirstValid) { + final GLRendererQuirks defaultQuirks = GLRendererQuirks.getStickyDeviceQuirks( GLDrawableFactory.getEGLFactory().getDefaultDevice() ); + List<GLCapabilitiesImmutable> bucket = new ArrayList<GLCapabilitiesImmutable>(num); for(int i=0; i<num; i++) { - EGLGraphicsConfiguration.EGLConfig2Capabilities(caps, glp, eglDisplay, configs.get(i), winattrmask, forceTransparentFlag); + final GLCapabilitiesImmutable caps = EGLGraphicsConfiguration.EGLConfig2Capabilities(defaultQuirks, device, glp, configs.get(i), winattrmask, forceTransparentFlag); + if(null != caps) { + bucket.add(caps); + if(onlyFirstValid) { + break; + } + } } - return caps; + return bucket; } - static void printCaps(String prefix, List/*GLCapabilitiesImmutable*/ caps, PrintStream out) { + static void printCaps(String prefix, List<GLCapabilitiesImmutable> caps, PrintStream out) { for(int i=0; i<caps.size(); i++) { out.println(prefix+"["+i+"] "+caps.get(i)); } } - - static EGLGraphicsConfiguration createOffscreenGraphicsConfiguration(AbstractGraphicsDevice device, GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser) { - if(capsChosen.isOnscreen()) { - throw new GLException("Error: Onscreen set: "+capsChosen); - } - - if(capsChosen.getDoubleBuffered()) { - // OFFSCREEN !DOUBLE_BUFFER // FIXME DBLBUFOFFSCRN - GLCapabilities caps2 = (GLCapabilities) capsChosen.cloneMutable(); - caps2.setDoubleBuffered(false); - capsChosen = caps2; - } - - DefaultGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); - EGLGraphicsConfiguration eglConfig = chooseGraphicsConfigurationStatic(capsChosen, capsReq, chooser, screen, VisualIDHolder.VID_UNDEFINED, false); - if (null == eglConfig) { - throw new GLException("Couldn't create EGLGraphicsConfiguration from "+screen); - } else if(DEBUG) { - System.err.println("Chosen eglConfig: "+eglConfig); - } - return eglConfig; - } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenContext.java b/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenContext.java deleted file mode 100644 index dd0a3db3a..000000000 --- a/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenContext.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.egl; - -import javax.media.nativewindow.*; -import javax.media.opengl.*; -import jogamp.opengl.*; -import com.jogamp.gluegen.runtime.ProcAddressTable; -import java.nio.*; -import java.util.*; - -public class EGLOnscreenContext extends EGLContext { - public EGLOnscreenContext(EGLOnscreenDrawable drawable, GLContext shareWith) { - super(drawable, shareWith); - } - - public void bindPbufferToTexture() { - throw new GLException("Should not call this"); - } - - public void releasePbufferFromTexture() { - throw new GLException("Should not call this"); - } - -} - diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenDrawable.java b/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenDrawable.java index 42f067b29..065f80dcb 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenDrawable.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLOnscreenDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -48,12 +48,14 @@ public class EGLOnscreenDrawable extends EGLDrawable { super(factory, component); } + @Override public GLContext createContext(GLContext shareWith) { - return new EGLOnscreenContext(this, shareWith); + return new EGLContext(this, shareWith); } - protected long createSurface(long eglDpy, long eglNativeCfg, long surfaceHandle) { - return EGL.eglCreateWindowSurface(eglDpy, eglNativeCfg, surfaceHandle, null); + @Override + protected long createSurface(EGLGraphicsConfiguration config, int width, int height, long nativeSurfaceHandle) { + return EGL.eglCreateWindowSurface(config.getScreen().getDevice().getHandle(), config.getNativeConfig(), nativeSurfaceHandle, null); } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLPbufferContext.java b/src/jogl/classes/jogamp/opengl/egl/EGLPbufferContext.java deleted file mode 100644 index 2cad7daac..000000000 --- a/src/jogl/classes/jogamp/opengl/egl/EGLPbufferContext.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.egl; - -import javax.media.nativewindow.*; -import javax.media.opengl.*; -import jogamp.opengl.*; -import com.jogamp.gluegen.runtime.ProcAddressTable; -import java.nio.*; -import java.util.*; - -public class EGLPbufferContext extends EGLContext { - public EGLPbufferContext(EGLPbufferDrawable drawable, GLContext shareWith) { - super(drawable, shareWith); - } - - public int getFloatingPointMode() { - return 0; // FIXME ?? - } - - public void bindPbufferToTexture() { - throw new GLException("Not yet implemented"); - } - - public void releasePbufferFromTexture() { - throw new GLException("Not yet implemented"); - } -} - diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLPbufferDrawable.java b/src/jogl/classes/jogamp/opengl/egl/EGLPbufferDrawable.java index 28a23d294..45e39f5d1 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLPbufferDrawable.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLPbufferDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,64 +29,35 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.egl; -import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.SurfaceChangeable; -import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; -import javax.media.opengl.GLException; public class EGLPbufferDrawable extends EGLDrawable { - private int texFormat; protected static final boolean useTexture = false; // No yet .. protected EGLPbufferDrawable(EGLDrawableFactory factory, NativeSurface target) { super(factory, target); } - protected void destroyImpl() { - setRealized(false); - } - - protected long createSurface(long eglDpy, long eglNativeCfg, long surfaceHandle) { - final AbstractGraphicsConfiguration config = getNativeSurface().getGraphicsConfiguration(); - final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - - if(useTexture) { - texFormat = caps.getAlphaBits() > 0 ? EGL.EGL_TEXTURE_RGBA : EGL.EGL_TEXTURE_RGB ; - } else { - texFormat = EGL.EGL_NO_TEXTURE; - } - - if (DEBUG) { - System.out.println("Pbuffer config: " + config); - } - - NativeSurface nw = getNativeSurface(); - int[] attrs = EGLGraphicsConfiguration.CreatePBufferSurfaceAttribList(nw.getWidth(), nw.getHeight(), texFormat); - long surf = EGL.eglCreatePbufferSurface(eglDpy, eglNativeCfg, attrs, 0); - if (EGL.EGL_NO_SURFACE==surf) { - throw new GLException("Creation of window surface (eglCreatePbufferSurface) failed, dim "+nw.getWidth()+"x"+nw.getHeight()+", error 0x"+Integer.toHexString(EGL.eglGetError())); - } else if(DEBUG) { - System.err.println("PBuffer setSurface result: eglSurface 0x"+Long.toHexString(surf)); - } - ((SurfaceChangeable)nw).setSurfaceHandle(surf); - return surf; + @Override + protected long createSurface(EGLGraphicsConfiguration config, int width, int height, long nativeSurfaceHandle) { + return EGLDrawableFactory.createPBufferSurfaceImpl(config, width, height, false); } + @Override public GLContext createContext(GLContext shareWith) { - return new EGLPbufferContext(this, shareWith); + return new EGLContext(this, shareWith); } } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java b/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java new file mode 100644 index 000000000..5b911576e --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/egl/EGLUpstreamSurfaceHook.java @@ -0,0 +1,217 @@ +package jogamp.opengl.egl; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.DefaultGraphicsScreen; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.VisualIDHolder.VIDType; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLException; + +import com.jogamp.nativewindow.egl.EGLGraphicsDevice; + +/** + * <pre> + * EGLWrappedSurface [ is_a -> WrappedSurface -> ProxySurfaceImpl -> ProxySurface -> MutableSurface -> NativeSurface] has_a + * EGLUpstreamSurfaceHook [ is_a -> UpstreamSurfaceHook.MutableSize -> UpstreamSurfaceHook ] has_a + * NativeSurface (i.e. native X11 surface) + * </pre> + */ +public class EGLUpstreamSurfaceHook implements UpstreamSurfaceHook.MutableSize { + protected static final boolean DEBUG = EGLDrawableFactory.DEBUG; + private final NativeSurface upstreamSurface; + private final UpstreamSurfaceHook.MutableSize upstreamSurfaceHookMutableSize; + + public EGLUpstreamSurfaceHook(NativeSurface upstream) { + upstreamSurface = upstream; + if(upstreamSurface instanceof ProxySurface) { + final UpstreamSurfaceHook ush = ((ProxySurface)upstreamSurface).getUpstreamSurfaceHook(); + if(ush instanceof UpstreamSurfaceHook.MutableSize) { + // offscreen NativeSurface w/ MutableSize (default) + upstreamSurfaceHookMutableSize = (UpstreamSurfaceHook.MutableSize) ush; + } else { + upstreamSurfaceHookMutableSize = null; + } + } else { + upstreamSurfaceHookMutableSize = null; + } + } + + public final NativeSurface getUpstreamSurface() { return upstreamSurface; } + + static String getThreadName() { return Thread.currentThread().getName(); } + + @Override + public final void setSize(int width, int height) { + if(null != upstreamSurfaceHookMutableSize) { + upstreamSurfaceHookMutableSize.setSize(width, height); + } + } + + @Override + public final void create(ProxySurface surface) { + final String dbgPrefix; + if(DEBUG) { + dbgPrefix = getThreadName() + ": EGLUpstreamSurfaceHook.create( up "+upstreamSurface.getClass().getSimpleName()+" -> this "+surface.getClass().getSimpleName()+" ): "; + System.err.println(dbgPrefix+this); + } else { + dbgPrefix = null; + } + + if(upstreamSurface instanceof ProxySurface) { + // propagate createNotify(..) so upstreamSurface will be created + ((ProxySurface)upstreamSurface).createNotify(); + } + + // lock upstreamSurface, so it can be used in case EGLDisplay is derived from it! + if(NativeSurface.LOCK_SURFACE_NOT_READY >= upstreamSurface.lockSurface()) { + throw new GLException("Could not lock: "+upstreamSurface); + } + try { + evalUpstreamSurface(dbgPrefix, surface); + } finally { + upstreamSurface.unlockSurface(); + } + } + + private final void evalUpstreamSurface(String dbgPrefix, ProxySurface surface) { + // + // evaluate nature of upstreamSurface, may create EGL instances if required + // + + boolean isEGLSurfaceValid = true; // assume yes + + final EGLGraphicsDevice eglDevice; + final AbstractGraphicsConfiguration aConfig; + { + final AbstractGraphicsConfiguration surfaceConfig = surface.getGraphicsConfiguration(); + final AbstractGraphicsDevice surfaceDevice = null != surfaceConfig ? surfaceConfig.getScreen().getDevice() : null; + if(DEBUG) { + System.err.println(dbgPrefix+"SurfaceDevice: "+surfaceDevice.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(surfaceDevice.hashCode())+", "+surfaceDevice); + System.err.println(dbgPrefix+"SurfaceConfig: "+surfaceConfig.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(surfaceConfig.hashCode())+", "+surfaceConfig); + } + + final AbstractGraphicsConfiguration upstreamConfig = upstreamSurface.getGraphicsConfiguration(); + final AbstractGraphicsDevice upstreamDevice = upstreamConfig.getScreen().getDevice(); + if(DEBUG) { + System.err.println(dbgPrefix+"UpstreamDevice: "+upstreamDevice.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(upstreamDevice.hashCode())+", "+upstreamDevice); + System.err.println(dbgPrefix+"UpstreamConfig: "+upstreamConfig.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(upstreamConfig.hashCode())+", "+upstreamConfig); + } + + if( surfaceDevice instanceof EGLGraphicsDevice ) { + eglDevice = (EGLGraphicsDevice) surfaceDevice; + aConfig = surfaceConfig; + if(DEBUG) { + System.err.println(dbgPrefix+"Reusing this eglDevice: "+eglDevice+", using this config "+aConfig.getClass().getSimpleName()+" "+aConfig); + } + if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { + eglDevice.open(); + isEGLSurfaceValid = false; + surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + } else if( upstreamDevice instanceof EGLGraphicsDevice ) { + eglDevice = (EGLGraphicsDevice) upstreamDevice; + aConfig = upstreamConfig; + if(DEBUG) { + System.err.println(dbgPrefix+"Reusing upstream eglDevice: "+eglDevice+", using upstream config "+aConfig.getClass().getSimpleName()+" "+aConfig); + } + if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { + eglDevice.open(); + isEGLSurfaceValid = false; + surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + } else { + eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(upstreamSurface); + eglDevice.open(); + aConfig = upstreamConfig; + isEGLSurfaceValid = false; + surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + } + + final GLCapabilitiesImmutable capsRequested = (GLCapabilitiesImmutable) aConfig.getRequestedCapabilities(); + final EGLGraphicsConfiguration eglConfig; + if( aConfig instanceof EGLGraphicsConfiguration ) { + // Config is already in EGL type - reuse .. + final EGLGLCapabilities capsChosen = (EGLGLCapabilities) aConfig.getChosenCapabilities(); + if( !isEGLSurfaceValid || !EGLGraphicsConfiguration.isEGLConfigValid(eglDevice.getHandle(), capsChosen.getEGLConfig()) ) { + // 'refresh' the native EGLConfig handle + capsChosen.setEGLConfig(EGLGraphicsConfiguration.EGLConfigId2EGLConfig(eglDevice.getHandle(), capsChosen.getEGLConfigID())); + if( 0 == capsChosen.getEGLConfig() ) { + throw new GLException("Refreshing native EGLConfig handle failed with error "+EGLContext.toHexString(EGL.eglGetError())+": "+eglDevice+", "+capsChosen+" of "+aConfig); + } + final AbstractGraphicsScreen eglScreen = new DefaultGraphicsScreen(eglDevice, aConfig.getScreen().getIndex()); + eglConfig = new EGLGraphicsConfiguration(eglScreen, capsChosen, capsRequested, null); + if(DEBUG) { + System.err.println(dbgPrefix+"Refreshing eglConfig: "+eglConfig); + } + isEGLSurfaceValid = false; + } else { + eglConfig = (EGLGraphicsConfiguration) aConfig; + if(DEBUG) { + System.err.println(dbgPrefix+"Reusing eglConfig: "+eglConfig); + } + } + } else { + final AbstractGraphicsScreen eglScreen = new DefaultGraphicsScreen(eglDevice, aConfig.getScreen().getIndex()); + eglConfig = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( + capsRequested, capsRequested, null, eglScreen, aConfig.getVisualID(VIDType.NATIVE), false /* forceTransparencyFlag */); + + if (null == eglConfig) { + throw new GLException("Couldn't create EGLGraphicsConfiguration from "+eglScreen); + } else if(DEBUG) { + System.err.println(dbgPrefix+"Chosen eglConfig: "+eglConfig); + } + isEGLSurfaceValid = false; + } + surface.setGraphicsConfiguration(eglConfig); + + if(isEGLSurfaceValid) { + isEGLSurfaceValid = EGLDrawable.isValidEGLSurface(eglDevice.getHandle(), upstreamSurface.getSurfaceHandle()); + } + if(isEGLSurfaceValid) { + surface.setSurfaceHandle(upstreamSurface.getSurfaceHandle()); + surface.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + if(DEBUG) { + System.err.println(dbgPrefix+"Fin: Already valid EGL surface - use as-is: "+upstreamSurface); + } + } else { + surface.setSurfaceHandle(EGL.EGL_NO_SURFACE); + surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); // create/destroy in EGLDrawable + if(DEBUG) { + System.err.println(dbgPrefix+"Fin: EGL surface n/a - TBD: "+upstreamSurface); + } + } + } + + @Override + public final void destroy(ProxySurface surface) { + if(EGLDrawableFactory.DEBUG) { + System.err.println("EGLUpstreamSurfaceHook.destroy("+surface.getClass().getSimpleName()+"): "+this); + } + surface.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + if(upstreamSurface instanceof ProxySurface) { + ((ProxySurface)upstreamSurface).destroyNotify(); + } + } + + @Override + public final int getWidth(ProxySurface s) { + return upstreamSurface.getWidth(); + } + + @Override + public final int getHeight(ProxySurface s) { + return upstreamSurface.getHeight(); + } + + @Override + public String toString() { + final String us_s = null != upstreamSurface ? ( upstreamSurface.getClass().getName() + ": 0x" + Long.toHexString(upstreamSurface.getSurfaceHandle()) ) : "nil"; + return "EGLUpstreamSurfaceHook[ "+ upstreamSurface.getWidth() + "x" + upstreamSurface.getHeight() + ", " + us_s+ "]"; + } + +} diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLWrappedSurface.java b/src/jogl/classes/jogamp/opengl/egl/EGLWrappedSurface.java new file mode 100644 index 000000000..e6d43d957 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/egl/EGLWrappedSurface.java @@ -0,0 +1,34 @@ +package jogamp.opengl.egl; + +import javax.media.nativewindow.NativeSurface; + +import jogamp.nativewindow.WrappedSurface; + +/** + * <pre> + * EGLWrappedSurface [ is_a -> WrappedSurface -> ProxySurfaceImpl -> ProxySurface -> MutableSurface -> NativeSurface] has_a + * EGLUpstreamSurfaceHook [ is_a -> UpstreamSurfaceHook.MutableSize -> UpstreamSurfaceHook ] has_a + * NativeSurface (i.e. native X11 surface) + * </pre> + */ +public class EGLWrappedSurface extends WrappedSurface { + + public static EGLWrappedSurface get(NativeSurface surface) { + if(surface instanceof EGLWrappedSurface) { + return (EGLWrappedSurface)surface; + } + return new EGLWrappedSurface(surface); + } + + public EGLWrappedSurface(NativeSurface surface) { + super(surface.getGraphicsConfiguration(), EGL.EGL_NO_SURFACE, new EGLUpstreamSurfaceHook(surface), false /* tbd in UpstreamSurfaceHook */); + if(EGLDrawableFactory.DEBUG) { + System.err.println("EGLWrappedSurface.ctor(): "+this); + } + } + + @Override + public final NativeSurface getUpstreamSurface() { + return ((EGLUpstreamSurfaceHook)super.getUpstreamSurfaceHook()).getUpstreamSurface(); + } +} diff --git a/src/jogl/classes/jogamp/opengl/gl2/ProjectDouble.java b/src/jogl/classes/jogamp/opengl/gl2/ProjectDouble.java index 9165dbc4b..a4aa1c7c5 100644 --- a/src/jogl/classes/jogamp/opengl/gl2/ProjectDouble.java +++ b/src/jogl/classes/jogamp/opengl/gl2/ProjectDouble.java @@ -6,9 +6,9 @@ ** this file except in compliance with the License. You may obtain a copy ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: -** +** ** http://oss.sgi.com/projects/FreeB -** +** ** Note that, as provided in the License, the Software is distributed on an ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND @@ -24,13 +24,13 @@ ** except that Section 2.2 and 11 are omitted. Any differences between ** the Alternative License and the SGI License are offered solely by Sun ** and not by SGI. -** +** ** Original Code. The Original Code is: OpenGL Sample Implementation, ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. ** Copyright in any portions created by third parties is as indicated ** elsewhere herein. All Rights Reserved. -** +** ** Additional Notice Provisions: The application programming interfaces ** established by SGI in conjunction with the Original Code are The ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -45,56 +45,56 @@ ** $Header$ */ -/* +/* * Copyright (c) 2002-2004 LWJGL Project * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are + * modification, are permitted provided that the following conditions are * met: - * - * * Redistributions of source code must retain the above copyright + * + * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * * Neither the name of 'LWJGL' nor the names of - * its contributors may be used to endorse or promote products derived + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -107,7 +107,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -125,7 +125,7 @@ import com.jogamp.common.nio.Buffers; * <p/> * <p/> * Created 11-jan-2004 - * + * * @author Erik Duijs * @author Kenneth Russell */ @@ -155,7 +155,7 @@ public class ProjectDouble { private final double[] forward = new double[3]; private final double[] side = new double[3]; private final double[] up = new double[3]; - + // Buffer-based implementation private DoubleBuffer locbuf; private final DoubleBuffer matrixBuf; @@ -227,7 +227,7 @@ public class ProjectDouble { /** * Method __gluMultMatrixVecd - * + * * @param matrix * @param in * @param out @@ -244,7 +244,7 @@ public class ProjectDouble { /** * Method __gluMultMatrixVecd - * + * * @param matrix * @param in * @param out @@ -265,7 +265,7 @@ public class ProjectDouble { /** * @param src * @param inverse - * + * * @return */ private boolean __gluInvertMatrixd(double[] src, double[] inverse) { @@ -335,7 +335,7 @@ public class ProjectDouble { /** * @param src * @param inverse - * + * * @return */ private boolean __gluInvertMatrixd(DoubleBuffer src, DoubleBuffer inverse) { @@ -525,7 +525,7 @@ public class ProjectDouble { /** * Method gluOrtho2D. - * + * * @param left * @param right * @param bottom @@ -537,7 +537,7 @@ public class ProjectDouble { /** * Method gluPerspective. - * + * * @param fovy * @param aspect * @param zNear @@ -570,7 +570,7 @@ public class ProjectDouble { /** * Method gluLookAt - * + * * @param eyex * @param eyey * @param eyez @@ -631,7 +631,7 @@ public class ProjectDouble { /** * Method gluProject - * + * * @param objx * @param objy * @param objz @@ -639,7 +639,7 @@ public class ProjectDouble { * @param projMatrix * @param viewport * @param win_pos - * + * * @return */ public boolean gluProject(double objx, @@ -685,7 +685,7 @@ public class ProjectDouble { /** * Method gluProject - * + * * @param objx * @param objy * @param objz @@ -693,7 +693,7 @@ public class ProjectDouble { * @param projMatrix * @param viewport * @param win_pos - * + * * @return */ public boolean gluProject(double objx, @@ -738,7 +738,7 @@ public class ProjectDouble { /** * Method gluUnproject - * + * * @param winx * @param winy * @param winz @@ -746,7 +746,7 @@ public class ProjectDouble { * @param projMatrix * @param viewport * @param obj_pos - * + * * @return */ public boolean gluUnProject(double winx, @@ -799,7 +799,7 @@ public class ProjectDouble { /** * Method gluUnproject - * + * * @param winx * @param winy * @param winz @@ -807,7 +807,7 @@ public class ProjectDouble { * @param projMatrix * @param viewport * @param obj_pos - * + * * @return */ public boolean gluUnProject(double winx, @@ -858,7 +858,7 @@ public class ProjectDouble { /** * Method gluUnproject4 - * + * * @param winx * @param winy * @param winz @@ -869,7 +869,7 @@ public class ProjectDouble { * @param near * @param far * @param obj_pos - * + * * @return */ public boolean gluUnProject4(double winx, @@ -923,7 +923,7 @@ public class ProjectDouble { /** * Method gluUnproject4 - * + * * @param winx * @param winy * @param winz @@ -934,7 +934,7 @@ public class ProjectDouble { * @param near * @param far * @param obj_pos - * + * * @return */ public boolean gluUnProject4(double winx, @@ -987,7 +987,7 @@ public class ProjectDouble { /** * Method gluPickMatrix - * + * * @param x * @param y * @param deltaX @@ -1014,7 +1014,7 @@ public class ProjectDouble { /** * Method gluPickMatrix - * + * * @param x * @param y * @param deltaX diff --git a/src/jogl/classes/jogamp/opengl/glu/GLUquadricImpl.java b/src/jogl/classes/jogamp/opengl/glu/GLUquadricImpl.java index 3f8a76535..717b1255c 100644 --- a/src/jogl/classes/jogamp/opengl/glu/GLUquadricImpl.java +++ b/src/jogl/classes/jogamp/opengl/glu/GLUquadricImpl.java @@ -6,15 +6,15 @@ ** this file except in compliance with the License. You may obtain a copy ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: -** +** ** http://oss.sgi.com/projects/FreeB -** +** ** Note that, as provided in the License, the Software is distributed on an ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. -** +** ** NOTE: The Original Code (as defined below) has been licensed to Sun ** Microsystems, Inc. ("Sun") under the SGI Free Software License B ** (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. ** Copyright in any portions created by third parties is as indicated ** elsewhere herein. All Rights Reserved. -** +** ** Additional Notice Provisions: The application programming interfaces ** established by SGI in conjunction with the Original Code are The ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -45,56 +45,56 @@ ** $Header$ */ -/* +/* * Copyright (c) 2002-2004 LWJGL Project * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are + * modification, are permitted provided that the following conditions are * met: - * - * * Redistributions of source code must retain the above copyright + * + * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * * Neither the name of 'LWJGL' nor the names of - * its contributors may be used to endorse or promote products derived + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -107,7 +107,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -115,15 +115,17 @@ package jogamp.opengl.glu; -import javax.media.opengl.*; -import javax.media.opengl.glu.*; +import javax.media.opengl.GL; +import javax.media.opengl.glu.GLU; +import javax.media.opengl.glu.GLUquadric; + import com.jogamp.opengl.util.ImmModeSink; -import java.nio.*; +import com.jogamp.opengl.util.glsl.ShaderState; /** * GLUquadricImpl.java - * - * + * + * * Created 22-dec-2003 (originally Quadric.java) * @author Erik Duijs * @author Kenneth Russell, Sven Gothel @@ -139,25 +141,30 @@ public class GLUquadricImpl implements GLUquadric { private boolean immModeSinkImmediate; public int normalType; public GL gl; + public ShaderState shaderState; + public int shaderProgram; public static final boolean USE_NORM = true; public static final boolean USE_TEXT = false; private ImmModeSink immModeSink=null; - public GLUquadricImpl(GL gl, boolean useGLSL) { + public GLUquadricImpl(GL gl, boolean useGLSL, ShaderState st, int shaderProgram) { this.gl=gl; this.useGLSL = useGLSL; - drawStyle = GLU.GLU_FILL; - orientation = GLU.GLU_OUTSIDE; - textureFlag = false; - normals = GLU.GLU_SMOOTH; - normalType = gl.isGLES1()?GL.GL_BYTE:GL.GL_FLOAT; - immModeSinkImmediate=true; - immModeSinkEnabled=!gl.isGL2(); + this.drawStyle = GLU.GLU_FILL; + this.orientation = GLU.GLU_OUTSIDE; + this.textureFlag = false; + this.normals = GLU.GLU_SMOOTH; + this.normalType = gl.isGLES1()?GL.GL_BYTE:GL.GL_FLOAT; + this.immModeSinkImmediate=true; + this.immModeSinkEnabled=!gl.isGL2(); + this.shaderState = st; + this.shaderProgram = shaderProgram; replaceImmModeSink(); } + @Override public void enableImmModeSink(boolean val) { if(gl.isGL2()) { immModeSinkEnabled=val; @@ -169,10 +176,12 @@ public class GLUquadricImpl implements GLUquadric { } } + @Override public boolean isImmModeSinkEnabled() { return immModeSinkEnabled; } + @Override public void setImmMode(boolean val) { if(immModeSinkEnabled) { immModeSinkImmediate=val; @@ -181,30 +190,44 @@ public class GLUquadricImpl implements GLUquadric { } } + @Override public boolean getImmMode() { return immModeSinkImmediate; } + @Override public ImmModeSink replaceImmModeSink() { if(!immModeSinkEnabled) return null; ImmModeSink res = immModeSink; if(useGLSL) { - immModeSink = ImmModeSink.createGLSL (gl, GL.GL_STATIC_DRAW, 32, - 3, GL.GL_FLOAT, // vertex - 0, GL.GL_FLOAT, // color - USE_NORM?3:0, normalType,// normal - USE_TEXT?2:0, GL.GL_FLOAT); // texture + if(null != shaderState) { + immModeSink = ImmModeSink.createGLSL (32, + 3, GL.GL_FLOAT, // vertex + 0, GL.GL_FLOAT, // color + USE_NORM?3:0, normalType, // normal + USE_TEXT?2:0, GL.GL_FLOAT, // texCoords + GL.GL_STATIC_DRAW, shaderState); + } else { + immModeSink = ImmModeSink.createGLSL (32, + 3, GL.GL_FLOAT, // vertex + 0, GL.GL_FLOAT, // color + USE_NORM?3:0, normalType, // normal + USE_TEXT?2:0, GL.GL_FLOAT, // texCoords + GL.GL_STATIC_DRAW, shaderProgram); + } } else { - immModeSink = ImmModeSink.createFixed(gl, GL.GL_STATIC_DRAW, 32, - 3, GL.GL_FLOAT, // vertex - 0, GL.GL_FLOAT, // color - USE_NORM?3:0, normalType,// normal - USE_TEXT?2:0, GL.GL_FLOAT); // texture + immModeSink = ImmModeSink.createFixed(32, + 3, GL.GL_FLOAT, // vertex + 0, GL.GL_FLOAT, // color + USE_NORM?3:0, normalType, // normal + USE_TEXT?2:0, GL.GL_FLOAT, // texCoords + GL.GL_STATIC_DRAW); } return res; } + @Override public void resetImmModeSink(GL gl) { if(immModeSinkEnabled) { immModeSink.reset(gl); @@ -212,7 +235,7 @@ public class GLUquadricImpl implements GLUquadric { } /** - * specifies the draw style for quadrics. + * specifies the draw style for quadrics. * * The legal values are as follows: * @@ -226,7 +249,7 @@ public class GLUquadricImpl implements GLUquadric { * separating coplanar faces will not be drawn. * * GLU.POINT: Quadrics are rendered as a set of points. - * + * * @param drawStyle The drawStyle to set */ public void setDrawStyle(int drawStyle) { @@ -243,7 +266,7 @@ public class GLUquadricImpl implements GLUquadric { * * GLU.SMOOTH: One normal is generated for every vertex of a quadric. This * is the default. - * + * * @param normals The normals to set */ public void setNormals(int normals) { @@ -260,7 +283,7 @@ public class GLUquadricImpl implements GLUquadric { * * Note that the interpretation of outward and inward depends on the quadric * being drawn. - * + * * @param orientation The orientation to set */ public void setOrientation(int orientation) { @@ -275,7 +298,7 @@ public class GLUquadricImpl implements GLUquadric { * * The manner in which texture coordinates are generated depends upon the * specific quadric rendered. - * + * * @param textureFlag The textureFlag to set */ public void setTextureFlag(boolean textureFlag) { @@ -430,7 +453,7 @@ public class GLUquadricImpl implements GLUquadric { r = baseRadius; for (j = 0; j < stacks; j++) { float s = 0.0f; - glBegin(gl, immModeSink.GL_QUAD_STRIP); + glBegin(gl, ImmModeSink.GL_QUAD_STRIP); for (i = 0; i <= slices; i++) { if (i == slices) { x = sin(0.0f); @@ -495,10 +518,10 @@ public class GLUquadricImpl implements GLUquadric { glNormal3f(gl, 0.0f, 0.0f, -1.0f); } } - + da = 2.0f * PI / slices; dr = (outerRadius - innerRadius) / loops; - + switch (drawStyle) { case GLU.GLU_FILL: { @@ -514,7 +537,7 @@ public class GLUquadricImpl implements GLUquadric { float r2 = r1 + dr; if (orientation == GLU.GLU_OUTSIDE) { int s; - glBegin(gl, immModeSink.GL_QUAD_STRIP); + glBegin(gl, ImmModeSink.GL_QUAD_STRIP); for (s = 0; s <= slices; s++) { float a; if (s == slices) @@ -532,7 +555,7 @@ public class GLUquadricImpl implements GLUquadric { } else { int s; - glBegin(gl, immModeSink.GL_QUAD_STRIP); + glBegin(gl, ImmModeSink.GL_QUAD_STRIP); for (s = slices; s >= 0; s--) { float a; if (s == slices) @@ -631,18 +654,18 @@ public class GLUquadricImpl implements GLUquadric { * through startAngle + sweepAngle is included (where 0 degrees is along * the +y axis, 90 degrees along the +x axis, 180 along the -y axis, and * 270 along the -x axis). - * + * * The partial disk has a radius of outerRadius, and contains a concentric * circular hole with a radius of innerRadius. If innerRadius is zero, then * no hole is generated. The partial disk is subdivided around the z axis * into slices (like pizza slices), and also about the z axis into rings * (as specified by slices and loops, respectively). - * + * * With respect to orientation, the +z side of the partial disk is * considered to be outside (see gluQuadricOrientation). This means that if * the orientation is set to GLU.GLU_OUTSIDE, then any normals generated point * along the +z axis. Otherwise, they point along the -z axis. - * + * * If texturing is turned on (with gluQuadricTexture), texture coordinates * are generated linearly such that where r=outerRadius, the value at (r, 0, 0) * is (1, 0.5), at (0, r, 0) it is (0.5, 1), at (-r, 0, 0) it is (0, 0.5), @@ -655,11 +678,10 @@ public class GLUquadricImpl implements GLUquadric { int loops, float startAngle, float sweepAngle) { - int i, j, max; + int i, j; float[] sinCache = new float[CACHE_SIZE]; float[] cosCache = new float[CACHE_SIZE]; float angle; - float x, y; float sintemp, costemp; float deltaRadius; float radiusLow, radiusHigh; @@ -770,7 +792,7 @@ public class GLUquadricImpl implements GLUquadric { texHigh = radiusHigh / outerRadius / 2; } - glBegin(gl, immModeSink.GL_QUAD_STRIP); + glBegin(gl, ImmModeSink.GL_QUAD_STRIP); for (i = 0; i <= slices; i++) { if (orientation == GLU.GLU_OUTSIDE) { if (textureFlag) { @@ -984,7 +1006,7 @@ public class GLUquadricImpl implements GLUquadric { // draw intermediate stacks as quad strips for (i = imin; i < imax; i++) { rho = i * drho; - glBegin(gl, immModeSink.GL_QUAD_STRIP); + glBegin(gl, ImmModeSink.GL_QUAD_STRIP); s = 0.0f; for (j = 0; j <= slices; j++) { theta = (j == slices) ? 0.0f : j * dtheta; @@ -1188,7 +1210,7 @@ public class GLUquadricImpl implements GLUquadric { */ private void normal3f(GL gl, float x, float y, float z) { float mag; - + mag = (float)Math.sqrt(x * x + y * y + z * z); if (mag > 0.00001F) { x /= mag; diff --git a/src/jogl/classes/jogamp/opengl/glu/Glue.java b/src/jogl/classes/jogamp/opengl/glu/Glue.java index 636d17f29..2ad3d8c89 100644 --- a/src/jogl/classes/jogamp/opengl/glu/Glue.java +++ b/src/jogl/classes/jogamp/opengl/glu/Glue.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -89,15 +89,15 @@ public class Glue { "null control point reference", "duplicate point on piecewise linear trimming curve" } ; - + /** Creates a new instance of Glue */ public Glue() { } - + public static String __gluNURBSErrorString( int errno ) { return( __gluNurbsErrors[ errno ] ); } - + private static String[] __gluTessErrors = { " ", "gluTessBeginPolygon() must precede a gluTessEndPolygon", @@ -107,7 +107,7 @@ public class Glue { "a coordinate is too large", "need combine callback" }; - + public static String __gluTessErrorString( int errno ) { return( __gluTessErrors[ errno ] ); } diff --git a/src/jogl/classes/jogamp/opengl/glu/error/Error.java b/src/jogl/classes/jogamp/opengl/glu/error/Error.java index 2f49db9a4..ffb8d9471 100644 --- a/src/jogl/classes/jogamp/opengl/glu/error/Error.java +++ b/src/jogl/classes/jogamp/opengl/glu/error/Error.java @@ -6,9 +6,9 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND @@ -24,13 +24,13 @@ * except that Section 2.2 and 11 are omitted. Any differences between * the Alternative License and the SGI License are offered solely by Sun * and not by SGI. - * + * * Original Code. The Original Code is: OpenGL Sample Implementation, * Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -53,7 +53,7 @@ import jogamp.opengl.glu.Glue; * @author Administrator */ public class Error { - + private static String[] glErrorStrings = { "invalid enumerant", "invalid value", @@ -63,7 +63,7 @@ public class Error { "out of memory", "invalid framebuffer operation" }; - + private static String[] gluErrorStrings = { "invalid enumerant", "invalid value", @@ -71,11 +71,11 @@ public class Error { "", "invalid operation" }; - + /** Creates a new instance of Error */ public Error() { } - + public static String gluErrorString( int errorCode ) { if( errorCode == 0 ) { return( "no error" ); diff --git a/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2CurveEvaluator.java b/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2CurveEvaluator.java index 2ef4468e5..4213dfd46 100644 --- a/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2CurveEvaluator.java +++ b/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2CurveEvaluator.java @@ -48,7 +48,7 @@ import javax.media.opengl.glu.gl2.GLUgl2; class GL2CurveEvaluator implements CurveEvaluator { /** - * Output triangles (for callback) or render curve + * Output triangles (for callback) or render curve */ private boolean output_triangles; @@ -92,6 +92,7 @@ class GL2CurveEvaluator implements CurveEvaluator { /** * Pushes eval bit */ + @Override public void bgnmap1f() { // DONE if (output_triangles) { @@ -108,6 +109,7 @@ class GL2CurveEvaluator implements CurveEvaluator { /** * Pops all OpenGL attributes */ + @Override public void endmap1f() { // DONE if (output_triangles) { @@ -127,6 +129,7 @@ class GL2CurveEvaluator implements CurveEvaluator { * @param order curve order * @param ps control points */ + @Override public void map1f(int type, float ulo, float uhi, int stride, int order, CArrayOfFloats ps) { if (output_triangles) { @@ -153,6 +156,7 @@ class GL2CurveEvaluator implements CurveEvaluator { * Calls opengl enable * @param type what to enable */ + @Override public void enable(int type) { // DONE gl.glEnable(type); @@ -164,6 +168,7 @@ class GL2CurveEvaluator implements CurveEvaluator { * @param u1 low u * @param u2 high u */ + @Override public void mapgrid1f(int nu, float u1, float u2) { if (output_triangles) { // System.out.println("TODO curveevaluator.mapgrid1f"); @@ -179,6 +184,7 @@ class GL2CurveEvaluator implements CurveEvaluator { * @param from lowest param * @param to highest param */ + @Override public void mapmesh1f(int style, int from, int to) { /* //DEBUG drawing control points this.poradi++; diff --git a/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2SurfaceEvaluator.java b/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2SurfaceEvaluator.java index 155c4f9a9..e9c9fca3f 100644 --- a/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2SurfaceEvaluator.java +++ b/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GL2SurfaceEvaluator.java @@ -72,6 +72,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { /** * Pushes eval bit */ + @Override public void bgnmap2f() { if (output_triangles) { @@ -88,6 +89,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { * Sets glPolygonMode * @param style polygon mode (N_MESHFILL/N_MESHLINE/N_MESHPOINT) */ + @Override public void polymode(int style) { if (!output_triangles) { switch (style) { @@ -109,6 +111,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { /** * Pops all attributes */ + @Override public void endmap2f() { // TODO Auto-generated method stub if (output_triangles) { @@ -126,6 +129,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { * @param vlo * @param vhi */ + @Override public void domain2f(float ulo, float uhi, float vlo, float vhi) { // DONE } @@ -139,6 +143,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { * @param v0 lowest v * @param v1 highest v */ + @Override public void mapgrid2f(int nu, float u0, float u1, int nv, float v0, float v1) { if (output_triangles) { @@ -157,6 +162,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { * @param vmin minimum V * @param vmax maximum V */ + @Override public void mapmesh2f(int style, int umin, int umax, int vmin, int vmax) { if (output_triangles) { // System.out.println("TODO openglsurfaceavaluator.mapmesh2f output_triangles"); @@ -195,6 +201,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { * @param vorder surface order in v direction * @param pts control points */ + @Override public void map2f(int type, float ulo, float uhi, int ustride, int uorder, float vlo, float vhi, int vstride, int vorder, CArrayOfFloats pts) { // TODO Auto-generated method stub @@ -210,6 +217,7 @@ class GL2SurfaceEvaluator implements SurfaceEvaluator { * Calls opengl enable * @param type what to enable */ + @Override public void enable(int type) { //DONE gl.glEnable(type); diff --git a/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GLUgl2nurbsImpl.java b/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GLUgl2nurbsImpl.java index 58b565484..f83b3a805 100644 --- a/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GLUgl2nurbsImpl.java +++ b/src/jogl/classes/jogamp/opengl/glu/gl2/nurbs/GLUgl2nurbsImpl.java @@ -43,9 +43,9 @@ import javax.media.opengl.glu.GLUnurbs; /** * Base object for working with NURBS curves and surfaces - * + * * @author Tomas Hrasky - * + * */ public class GLUgl2nurbsImpl implements GLUnurbs { @@ -272,7 +272,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Sets domain distance for dom.dist. sampling in u direction - * + * * @param d * distance */ @@ -283,7 +283,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Sets domain distance for dom.dist. sampling in v direction - * + * * @param d * distance */ @@ -303,7 +303,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Calls a method with given name and passes argumet - * + * * @param name * name of a method to be called * @param arg @@ -329,7 +329,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Calls a method with given name - * + * * @param name * name of a method to be called */ @@ -349,7 +349,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Begins a NURBS curve - * + * * @param o_curve * curve object */ @@ -381,7 +381,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Begins new surface - * + * * @param o_surface * surface object */ @@ -503,7 +503,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Method for handling error codes - * + * * @param i * error code */ @@ -539,7 +539,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Make a NURBS curve - * + * * @param nknots * number of knots in knot vector * @param knot @@ -587,7 +587,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Check knot vector specification - * + * * @param knots * knot vector * @param msg @@ -607,7 +607,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Draw a curve - * + * * @param o_nurbscurve * NURBS curve object */ @@ -660,7 +660,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Draw NURBS surface - * + * * @param o_nurbssurface * NURBS surface object */ @@ -704,7 +704,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Define a map of given properties - * + * * @param type * map type * @param rational @@ -719,7 +719,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Set NURBS property - * + * * @param type * property type * @param tag @@ -744,7 +744,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Set parameters of existing property - * + * * @param prop * property */ @@ -755,7 +755,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Set given property to rendering hints - * + * * @param prop * property to be set */ @@ -767,7 +767,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Sets wheteher we use domain distance sampling - * + * * @param i * domain distance sampling flag */ @@ -805,7 +805,7 @@ public class GLUgl2nurbsImpl implements GLUnurbs { /** * Make NURBS surface - * + * * @param sknot_count * number of knots in s direction * @param sknot diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/BuildMipmap.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/BuildMipmap.java index f5fe17a7b..81a99beab 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/BuildMipmap.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/BuildMipmap.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -64,7 +64,7 @@ public class BuildMipmap { /** Creates a new instance of BuildMipmap */ public BuildMipmap() { } - + public static int gluBuild1DMipmapLevelsCore( GL gl, int target, int internalFormat, int width, int widthPowerOf2, int format, int type, int userLevel, int baseLevel, int maxLevel, ByteBuffer data ) { @@ -78,34 +78,34 @@ public class BuildMipmap { int maxsize; int cmpts; PixelStorageModes psm = new PixelStorageModes(); - + assert( Mipmap.checkMipmapArgs( internalFormat, format, type ) == 0 ); assert( width >= 1 ); - + newwidth = widthPowerOf2; levels = Mipmap.computeLog( newwidth ); - + levels += userLevel; - + Mipmap.retrieveStoreModes( gl, psm ); try { - newImage = Buffers.newDirectByteBuffer( Mipmap.image_size( width, 1, format, + newImage = Buffers.newDirectByteBuffer( Mipmap.image_size( width, 1, format, GL2.GL_UNSIGNED_SHORT ) ).asShortBuffer(); } catch( OutOfMemoryError ome ) { return( GLU.GLU_OUT_OF_MEMORY ); } newImage_width = width; - + Image.fill_image( psm, width, 1, format, type, Mipmap.is_index( format ), data, newImage ); cmpts = Mipmap.elements_per_group( format, type ); gl.glPixelStorei( GL2.GL_UNPACK_ALIGNMENT, 2 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_ROWS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, 0 ); - + // if swap_bytes was set, swapping occurred in fill_image gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, GL2.GL_FALSE ); - + for( level = userLevel; level <= levels; level++ ) { if( newImage_width == newwidth ) { // user newimage for this level @@ -132,10 +132,10 @@ public class BuildMipmap { imageTemp = otherImage; otherImage = newImage; newImage = imageTemp; - + newImage_width = newwidth; if( baseLevel <= level && level <= maxLevel ) { - gl.getGL2().glTexImage1D( target, level, internalFormat, newImage_width, 0, + gl.getGL2().glTexImage1D( target, level, internalFormat, newImage_width, 0, format, GL2.GL_UNSIGNED_SHORT, newImage ); } } @@ -148,10 +148,10 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, psm.getUnpackSkipPixels() ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, psm.getUnpackRowLength() ); gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, (psm.getUnpackSwapBytes() ? 1 : 0) ); - + return( 0 ); } - + public static int bitmapBuild2DMipmaps( GL gl, int target, int internalFormat, int width, int height, int format, int type, ByteBuffer data ) { int newwidth[] = new int[1]; @@ -166,37 +166,37 @@ public class BuildMipmap { int maxsize; int cmpts; PixelStorageModes psm = new PixelStorageModes(); - + Mipmap.retrieveStoreModes( gl, psm ); - + Mipmap.closestFit( gl, target, width, height, internalFormat, format, type, newwidth, newheight ); - + levels = Mipmap.computeLog( newwidth[0] ); level = Mipmap.computeLog( newheight[0] ); if( level > levels ) { levels = level; } - + try { - newImage = Buffers.newDirectByteBuffer( Mipmap.image_size( width, height, + newImage = Buffers.newDirectByteBuffer( Mipmap.image_size( width, height, format, GL2.GL_UNSIGNED_SHORT ) ).asShortBuffer(); } catch( OutOfMemoryError ome ) { return( GLU.GLU_OUT_OF_MEMORY ); } newImage_width = width; newImage_height = height; - + Image.fill_image( psm, width, height, format, type, Mipmap.is_index( format ), data, newImage ); - + cmpts = Mipmap.elements_per_group( format, type ); gl.glPixelStorei( GL2.GL_UNPACK_ALIGNMENT, 2 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_ROWS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, 0 ); - + // if swap_bytes is set, swapping occurred in fill_image gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, GL2.GL_FALSE ); - + for( level = 0; level < levels; level++ ) { if( newImage_width == newwidth[0] && newImage_height == newheight[0] ) { newImage.rewind(); @@ -216,13 +216,13 @@ public class BuildMipmap { return( GLU.GLU_OUT_OF_MEMORY ); } } - ScaleInternal.scale_internal( cmpts, newImage_width, newImage_height, + ScaleInternal.scale_internal( cmpts, newImage_width, newImage_height, newImage, newwidth[0], newheight[0], otherImage ); // swap newImage and otherImage tempImage = otherImage; otherImage = newImage; newImage = tempImage; - + newImage_width = newwidth[0]; newImage_height = newheight[0]; newImage.rewind(); @@ -241,10 +241,10 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, psm.getUnpackSkipPixels() ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, psm.getUnpackRowLength() ); gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, (psm.getUnpackSwapBytes() ? 1 : 0) ); - + return( 0 ); } - + public static int gluBuild2DMipmapLevelsCore( GL gl, int target, int internalFormat, int width, int height, int widthPowerOf2, int heightPowerOf2, int format, int type, int userLevel, int baseLevel, int maxLevel, @@ -263,19 +263,19 @@ public class BuildMipmap { int maxsize; int cmpts; int mark=-1; - + boolean myswap_bytes; int groups_per_line, element_size, group_size; int rowsize, padding; PixelStorageModes psm = new PixelStorageModes(); - + assert( Mipmap.checkMipmapArgs( internalFormat, format, type ) == 0 ); assert( width >= 1 && height >= 1 ); - + if( type == GL2.GL_BITMAP ) { return( bitmapBuild2DMipmaps( gl, target, internalFormat, width, height, format, type, data ) ); } - + newwidth = widthPowerOf2; newheight = heightPowerOf2; levels = Mipmap.computeLog( newwidth ); @@ -283,9 +283,9 @@ public class BuildMipmap { if( level > levels ) { levels = level; } - + levels += userLevel; - + Mipmap.retrieveStoreModes( gl, psm ); myswap_bytes = psm.getUnpackSwapBytes(); cmpts = Mipmap.elements_per_group( format, type ); @@ -294,28 +294,28 @@ public class BuildMipmap { } else { groups_per_line = width; } - + element_size = Mipmap.bytes_per_element( type ); group_size = element_size * cmpts; if( element_size == 1 ) { myswap_bytes = false; } - + rowsize = groups_per_line * group_size; padding = ( rowsize % psm.getUnpackAlignment() ); if( padding != 0 ) { rowsize += psm.getUnpackAlignment() - padding; } - + mark = psm.getUnpackSkipRows() * rowsize + psm.getUnpackSkipPixels() * group_size; data.position( mark ); - + gl.glPixelStorei( GL2.GL_UNPACK_SKIP_ROWS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, 0 ); - + level = userLevel; - + // already power of two square if( width == newwidth && height == newheight ) { // use usersImage for level userLevel @@ -333,7 +333,7 @@ public class BuildMipmap { } int nextWidth = newwidth / 2; int nextHeight = newheight / 2; - + // clamp to 1 if( nextWidth < 1 ) { nextWidth = 1; @@ -342,7 +342,7 @@ public class BuildMipmap { nextHeight = 1; } memReq = Mipmap.image_size( nextWidth, nextHeight, format, type ); - + try { switch( type ) { case( GL2.GL_UNSIGNED_BYTE ): @@ -452,7 +452,7 @@ public class BuildMipmap { if( newheight < 1 ) { newheight = 1; } - + myswap_bytes = false; rowsize = newwidth * group_size; memReq = Mipmap.image_size( newwidth, newheight, format, type ); @@ -498,7 +498,7 @@ public class BuildMipmap { level = userLevel + 1; } else { // user's image is not nice powerof2 size square memReq = Mipmap.image_size( newwidth, newheight, format, type ); - try { + try { switch( type ) { case( GL2.GL_UNSIGNED_BYTE ): case( GL2.GL_BYTE ): @@ -535,15 +535,15 @@ public class BuildMipmap { data.position( mark ); switch( type ) { case( GL2.GL_UNSIGNED_BYTE ): - ScaleInternal.scale_internal_ubyte( cmpts, width, height, data, + ScaleInternal.scale_internal_ubyte( cmpts, width, height, data, newwidth, newheight, dstImage, element_size, rowsize, group_size ); break; case( GL2.GL_BYTE ): - ScaleInternal.scale_internal_byte( cmpts, width, height, data, newwidth, + ScaleInternal.scale_internal_byte( cmpts, width, height, data, newwidth, newheight, dstImage, element_size, rowsize, group_size ); break; case( GL2.GL_UNSIGNED_SHORT ): - ScaleInternal.scale_internal_ushort( cmpts, width, height, data, newwidth, + ScaleInternal.scale_internal_ushort( cmpts, width, height, data, newwidth, newheight, dstImage.asShortBuffer(), element_size, rowsize, group_size, myswap_bytes ); break; case( GL2.GL_SHORT ): @@ -620,7 +620,7 @@ public class BuildMipmap { tempImage = srcImage; srcImage = dstImage; dstImage = tempImage; - + if( levels != 0 ) { // use as little memory as possible int nextWidth = newwidth / 2; int nextHeight = newheight / 2; @@ -630,7 +630,7 @@ public class BuildMipmap { if( nextHeight < 1 ) { nextHeight = 1; } - + memReq = Mipmap.image_size( nextWidth, nextHeight, format, type ); try { switch( type ) { @@ -670,7 +670,7 @@ public class BuildMipmap { // level userLevel is in srcImage; nothing saved yet level = userLevel; } - + gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, GL2.GL_FALSE ); if( baseLevel <= level && level <= maxLevel ) { srcImage.rewind(); @@ -685,7 +685,7 @@ public class BuildMipmap { } } } - + level++; // update current level for the loop for( ; level <= levels; level++ ) { srcImage.rewind(); @@ -754,12 +754,12 @@ public class BuildMipmap { assert( false ); break; } - + // swap dstImage and srcImage tempImage = srcImage; srcImage = dstImage; dstImage = tempImage; - + if( newwidth > 1 ) { newwidth /= 2; rowsize /= 2; @@ -769,7 +769,7 @@ public class BuildMipmap { } // compute amount to pad per row if any int rowPad = rowsize % psm.getUnpackAlignment(); - + // should row be padded if( rowPad == 0 ) { // call teximage with srcImage untouched since its not padded @@ -792,7 +792,7 @@ public class BuildMipmap { int ii, jj; int dstTrav; int srcTrav; - + // allocate new image for mipmap of size newRowLength x newheight ByteBuffer newMipmapImage = null; try { @@ -813,7 +813,7 @@ public class BuildMipmap { newMipmapImage.put( srcImage.get() ); } } - + // and use this new image for mipmapping instead if( baseLevel <= level && level <= maxLevel ) { newMipmapImage.rewind(); @@ -833,10 +833,10 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, psm.getUnpackSkipPixels() ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, psm.getUnpackRowLength() ); gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, (psm.getUnpackSwapBytes() ? 1 : 0) ); - + return( 0 ); } - + public static int fastBuild2DMipmaps( GL gl, PixelStorageModes psm, int target, int components, int width, int height, int format, int type, ByteBuffer data ) { int[] newwidth = new int[1]; @@ -850,22 +850,22 @@ public class BuildMipmap { int memReq; int maxsize; int cmpts; - - Mipmap.closestFit( gl, target, width, height, components, format, type, newwidth, + + Mipmap.closestFit( gl, target, width, height, components, format, type, newwidth, newheight ); - + levels = Mipmap.computeLog( newwidth[0] ); level = Mipmap.computeLog( newheight[0] ); if( level > levels ) { levels = level; } - + cmpts = Mipmap.elements_per_group( format, type ); - + otherImage = null; // No need to copy the user data if its packed correctly. // Make sure that later routines don't change that data. - + if( psm.getUnpackSkipRows() == 0 && psm.getUnpackSkipPixels() == 0 ) { newImage = data; newImage_width = width; @@ -878,7 +878,7 @@ public class BuildMipmap { int iter; int iter2; int i, j; - + try { newImage = Buffers.newDirectByteBuffer( Mipmap.image_size(width, height, format, GL2.GL_UNSIGNED_BYTE ) ); } catch( OutOfMemoryError err ) { @@ -896,7 +896,7 @@ public class BuildMipmap { rowsize = group_per_line * cmpts; elements_per_line = width * cmpts; start = psm.getUnpackSkipRows() * rowsize + psm.getUnpackSkipPixels() * cmpts; - + for( i = 0; i < height; i++ ) { iter = start; data.position( iter ); @@ -906,13 +906,13 @@ public class BuildMipmap { start += rowsize; } } - + gl.glPixelStorei( GL2.GL_UNPACK_ALIGNMENT, 1 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_ROWS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, GL2.GL_FALSE ); - + for( level = 0; level <= levels; level++ ) { if( newImage_width == newwidth[0] && newImage_height == newheight[0] ) { // use newImage for this level @@ -937,7 +937,7 @@ public class BuildMipmap { imageTemp = otherImage; otherImage = newImage; newImage = imageTemp; - + newImage_width = newwidth[0]; newImage_height = newheight[0]; newImage.rewind(); @@ -956,10 +956,10 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, psm.getUnpackSkipPixels() ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, psm.getUnpackRowLength() ); gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, ( psm.getUnpackSwapBytes() ? 1 : 0 ) ) ; - + return( 0 ); } - + public static int gluBuild3DMipmapLevelsCore( GL gl, int target, int internalFormat, int width, int height, int depth, int widthPowerOf2, int heightPowerOf2, int depthPowerOf2, int format, int type, int userLevel, int baseLevel, @@ -977,19 +977,19 @@ public class BuildMipmap { int maxSize; int cmpts; int mark=-1; - + boolean myswapBytes; int groupsPerLine, elementSize, groupSize; int rowsPerImage, imageSize; int rowSize, padding; PixelStorageModes psm = new PixelStorageModes(); - + assert( Mipmap.checkMipmapArgs( internalFormat, format, type ) == 0 ); assert( width >= 1 && height >= 1 && depth >= 1 ); assert( type != GL2.GL_BITMAP ); - + srcImage = dstImage = null; - + newWidth = widthPowerOf2; newHeight = heightPowerOf2; newDepth = depthPowerOf2; @@ -1002,9 +1002,9 @@ public class BuildMipmap { if( level > levels ) { levels = level; } - + levels += userLevel; - + Mipmap.retrieveStoreModes3D( gl, psm ); myswapBytes = psm.getUnpackSwapBytes(); cmpts = Mipmap.elements_per_group( format, type ); @@ -1013,42 +1013,42 @@ public class BuildMipmap { } else { groupsPerLine = width; } - + elementSize = Mipmap.bytes_per_element( type ); groupSize = elementSize * cmpts; if( elementSize == 1 ) { myswapBytes = false; } - + // 3dstuff if( psm.getUnpackImageHeight() > 0 ) { rowsPerImage = psm.getUnpackImageHeight(); } else { rowsPerImage = height; } - + rowSize = groupsPerLine * groupSize; padding = ( rowSize % psm.getUnpackAlignment() ); if( padding != 0 ) { rowSize += psm.getUnpackAlignment() - padding; } - + imageSize = rowsPerImage * rowSize; - + usersImage = ByteBuffer.wrap(data.array()); mark = psm.getUnpackSkipRows() * rowSize + psm.getUnpackSkipPixels() * groupSize + psm.getUnpackSkipImages() * imageSize; usersImage.position( mark ); - + gl.glPixelStorei( GL2.GL_UNPACK_SKIP_ROWS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_PIXELS, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_ROW_LENGTH, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_SKIP_IMAGES, 0 ); gl.glPixelStorei( GL2.GL_UNPACK_IMAGE_HEIGHT, 0 ); - + level = userLevel; - + if( width == newWidth && height == newHeight && depth == newDepth ) { // use usersImage for level userlevel if( baseLevel <= level && level <= maxLevel ) { @@ -1068,7 +1068,7 @@ public class BuildMipmap { int nextWidth = newWidth / 2; int nextHeight = newHeight / 2; int nextDepth = newDepth / 2; - + // clamp to one if( nextWidth < 1 ) { nextWidth = 1; @@ -1116,13 +1116,13 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_IMAGE_HEIGHT, psm.getUnpackImageHeight() ); return( GLU.GLU_OUT_OF_MEMORY ); } - + if( dstImage != null ) { switch( type ) { case( GL2.GL_UNSIGNED_BYTE ): if( depth > 1 ) { HalveImage.halveImage3D( cmpts, new ExtractUByte(), width, height, depth, - usersImage, dstImage, elementSize, + usersImage, dstImage, elementSize, groupSize, rowSize, imageSize, myswapBytes ); } else { HalveImage.halveImage_ubyte( cmpts, width, height, usersImage, @@ -1145,7 +1145,7 @@ public class BuildMipmap { usersImage, dstImage, elementSize, groupSize, rowSize, imageSize, myswapBytes ); } else { - HalveImage.halveImage_ushort( cmpts, width, height, usersImage, + HalveImage.halveImage_ushort( cmpts, width, height, usersImage, dstImage.asShortBuffer(), elementSize, rowSize, groupSize, myswapBytes ); } break; @@ -1257,7 +1257,7 @@ public class BuildMipmap { if( newDepth < 1 ) { newDepth = 1; } - + myswapBytes = false; rowSize = newWidth * groupSize; imageSize = rowSize * newHeight; @@ -1302,7 +1302,7 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_IMAGE_HEIGHT, psm.getUnpackImageHeight() ); return( GLU.GLU_OUT_OF_MEMORY ); } - + // level userLevel + 1 is in srcImage; level userLevel already saved level = userLevel + 1; } else { @@ -1343,10 +1343,10 @@ public class BuildMipmap { gl.glPixelStorei( GL2.GL_UNPACK_IMAGE_HEIGHT, psm.getUnpackImageHeight() ); return( GLU.GLU_OUT_OF_MEMORY ); } - + ScaleInternal.gluScaleImage3D( gl, format, width, height, depth, type, usersImage, newWidth, newHeight, newDepth, type, dstImage ); - + myswapBytes = false; rowSize = newWidth * groupSize; imageSize = rowSize * newHeight; @@ -1354,7 +1354,7 @@ public class BuildMipmap { tempImage = srcImage; srcImage = dstImage; dstImage = tempImage; - + if( levels != 0 ) { int nextWidth = newWidth / 2; int nextHeight = newHeight / 2; @@ -1409,7 +1409,7 @@ public class BuildMipmap { // level userLevel is in srcImage; nothing saved yet level = userLevel; } - + gl.glPixelStorei( GL2.GL_UNPACK_SWAP_BYTES, GL2.GL_FALSE ); if( baseLevel <= level && level <= maxLevel ) { usersImage.position( mark ); @@ -1541,11 +1541,11 @@ public class BuildMipmap { assert( false ); break; } - + tempImage = srcImage; srcImage = dstImage; dstImage = tempImage; - + if( newWidth > 1 ) { newWidth /= 2; rowSize /= 2; diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract.java index a564269fb..0eee9bf32 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1010102.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1010102.java index 10ea1d729..0c155ff96 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1010102.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1010102.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.ByteBuffer; * @author Administrator */ public class Extract1010102 implements Extract { - + /** Creates a new instance of Extract1010102 */ public Extract1010102() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { long uint = 0; - + if( isSwap ) { uint = 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( packedPixel.getInt() ); } else { uint = 0x00000000FFFFFFFF & packedPixel.getInt(); } - + // 11111111,11000000,00000000,00000000 == 0xFFC00000 // 00000000,00111111,11110000,00000000 == 0x003F0000 // 00000000,00000000,00001111,11111100 == 0x00000FFC // 00000000,00000000,00000000,00000011 == 0x00000003 - + extractComponents[0] = (float)( ( uint & 0xFFC00000 ) >> 22 ) / 1023.0f; extractComponents[1] = (float)( ( uint & 0x003FF000 ) >> 12 ) / 1023.0f; extractComponents[2] = (float)( ( uint & 0x00000FFC ) >> 2 ) / 1023.0f; extractComponents[3] = (float)( ( uint & 0x00000003 ) ) / 3.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving long uint = (((int)((shoveComponents[0] * 1023) + 0.5f) << 22) & 0xFFC00000 ); uint |= (((int)((shoveComponents[1] * 1023) + 0.5f) << 12) & 0x003FF000 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1555rev.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1555rev.java index 1234da5f8..5208ea537 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1555rev.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract1555rev.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.ByteBuffer; * @author Administrator */ public class Extract1555rev implements Extract { - + /** Creates a new instance of Extract1555rev */ public Extract1555rev() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { int ushort = 0; - + if( isSwap ) { ushort = 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( packedPixel.getShort() ); } else { ushort = 0x0000FFFF & packedPixel.getShort(); } - + // 00000000,00011111 == 0x001F // 00000011,11100000 == 0x03E0 // 01111100,00000000 == 0x7C00 // 10000000,00000000 == 0x8000 - + extractComponents[0] = (float)( ( ushort & 0x001F ) ) / 31.0f; extractComponents[1] = (float)( ( ushort & 0x003E ) >> 5 ) / 31.0f; extractComponents[2] = (float)( ( ushort & 0x7C00 ) >> 10) / 31.0f; extractComponents[3] = (float)( ( ushort & 0x8000 ) >> 15); } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 00000000,00011111 == 0x001F // 00000011,11100000 == 0x03E0 // 01111100,00000000 == 0x7C00 // 10000000,00000000 == 0x8000 - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving int ushort = (((int)((shoveComponents[0] * 31) + 0.5f) ) & 0x0000001F ); ushort |= (((int)((shoveComponents[1] * 31) + 0.5f) << 5) & 0x000003E0 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract2101010rev.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract2101010rev.java index 226254f99..1bf8abcc3 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract2101010rev.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract2101010rev.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.ByteBuffer; * @author Administrator */ public class Extract2101010rev implements Extract { - + /** Creates a new instance of Extract2101010 */ public Extract2101010rev() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { long uint = 0; - + if( isSwap ) { uint = 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( packedPixel.getInt() ); } else { uint = 0x00000000FFFFFFFF & packedPixel.getInt(); } - + // 11111111,11000000,00000000,00000000 == 0xFFC00000 // 00000000,00111111,11110000,00000000 == 0x003F0000 // 00000000,00000000,00001111,11111100 == 0x00000FFC // 00000000,00000000,00000000,00000011 == 0x00000003 - + extractComponents[0] = (float)( ( uint & 0x000003FF ) ) / 1023.0f; extractComponents[1] = (float)( ( uint & 0x000FFC00 ) >> 10 ) / 1023.0f; extractComponents[2] = (float)( ( uint & 0x3FF00000 ) >> 20 ) / 1023.0f; extractComponents[3] = (float)( ( uint & 0xC0000000 ) >> 30 ) / 3.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving long uint = (((int)((shoveComponents[0] * 1023) + 0.5f) ) & 0x000003FF ); uint |= (((int)((shoveComponents[1] * 1023) + 0.5f) << 10) & 0x000FFC00 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract233rev.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract233rev.java index 9fa2a3a54..c86b39e63 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract233rev.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract233rev.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.ByteBuffer; * @author Administrator */ public class Extract233rev implements Extract { - + /** Creates a new instance of Extract223rev */ public Extract233rev() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { // 11100000 == 0xe0 // 00011100 == 0x1c @@ -65,16 +66,17 @@ public class Extract233rev implements Extract { extractComponents[1] = (float)((ubyte & 0x38) >> 3) / 7.0f; extractComponents[2] = (float)((ubyte & 0xC0) >> 6) / 3.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11100000 == 0xE0 // 00011100 == 0x1C // 00000011 == 0x03 - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); - + // due to limited precision, need to round before shoving byte b = (byte)( ( (int)( ( shoveComponents[0] * 7 ) + 0.5f ) ) & 0x07 ); b |= (byte)( ( (int)( ( shoveComponents[1] * 7 ) + 0.5f ) << 3 ) & 0x38 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract332.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract332.java index 92d141be5..706f0c3f2 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract332.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract332.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.ByteBuffer; * @author Administrator */ public class Extract332 implements Extract { - + /** Creates a new instance of Extract332 */ public Extract332() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { // 11100000 == 0xe0 // 00011100 == 0x1c @@ -65,16 +66,17 @@ public class Extract332 implements Extract { extractComponents[1] = (float)((ubyte & 0x1c) >> 2) / 7.0f; extractComponents[2] = (float)((ubyte & 0x03)) / 3.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11100000 == 0xE0 // 00011100 == 0x1C // 00000011 == 0x03 - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); - + // due to limited precision, need to round before shoving byte b = (byte)( ( (int)( ( shoveComponents[0] * 7 ) + 0.5f ) << 5 ) & 0xE0 ); b |= (byte)( ( (int)( ( shoveComponents[1] * 7 ) + 0.5f ) << 2 ) & 0x1C ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444.java index af99d154c..182d66ce2 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,41 +51,43 @@ import java.nio.*; * @author Administrator */ public class Extract4444 implements Extract { - + /** Creates a new instance of Extract4444 */ public Extract4444() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { int ushort = 0; - + if( isSwap ) { ushort = 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( packedPixel.getShort() ); } else { ushort = 0x0000FFFF & packedPixel.getShort(); } - + // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + extractComponents[0] = (float)( ( ushort & 0xF000 ) >> 12 ) / 15.0f; extractComponents[1] = (float)( ( ushort & 0x0F00 ) >> 8 ) / 15.0f; extractComponents[2] = (float)( ( ushort & 0x00F0 ) >> 4 ) / 15.0f; extractComponents[3] = (float)( ( ushort & 0x000F ) ) / 15.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); - + // due to limited precision, need to round before shoving int ushort = (((int)((shoveComponents[0] * 15) + 0.5f) << 12) & 0x0000F000 ); ushort |= (((int)((shoveComponents[1] * 15) + 0.5f) << 8) & 0x00000F00 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444rev.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444rev.java index e5bce60d8..52ecdc17c 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444rev.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract4444rev.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.*; * @author Administrator */ public class Extract4444rev implements Extract { - + /** Creates a new instance of Extract4444rev */ public Extract4444rev() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { int ushort = 0; - + if( isSwap ) { ushort = 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( packedPixel.getShort() ); } else { ushort = 0x0000FFFF & packedPixel.getShort(); } - + // 00000000,00001111 == 0x000F // 00000000,11110000 == 0x00F0 // 00001111,00000000 == 0x0F00 // 11110000,00000000 == 0xF000 - + extractComponents[0] = (float)( ( ushort & 0x000F ) ) / 15.0f; extractComponents[1] = (float)( ( ushort & 0x00F0 ) >> 4 ) / 15.0f; extractComponents[2] = (float)( ( ushort & 0x0F00 ) >> 8 ) / 15.0f; extractComponents[3] = (float)( ( ushort & 0xF000 ) >> 12 ) / 15.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving int ushort = (((int)((shoveComponents[0] * 15) + 0.5f) ) & 0x0000000F ); ushort |= (((int)((shoveComponents[1] * 15) + 0.5f) << 4) & 0x000000F0 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract5551.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract5551.java index 5c383103e..21f53aa1d 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract5551.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract5551.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.*; * @author Administrator */ public class Extract5551 implements Extract { - + /** Creates a new instance of Extract5551 */ public Extract5551() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { int ushort = 0; - + if( isSwap ) { ushort = 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( packedPixel.getShort() ); } else { ushort = 0x0000FFFF & packedPixel.getShort(); } - + // 11111000,00000000 == 0xF800 // 00000111,11000000 == 0x07C0 // 00000000,00111110 == 0x003E // 00000000,00000001 == 0x0001 - + extractComponents[0] = (float)( ( ushort & 0xF800 ) >> 11 ) / 31.0f; extractComponents[1] = (float)( ( ushort & 0x00F0 ) >> 6 ) / 31.0f; extractComponents[2] = (float)( ( ushort & 0x0F00 ) >> 1 ) / 31.0f; extractComponents[3] = (float)( ( ushort & 0xF000 ) ); } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving int ushort = (((int)((shoveComponents[0] * 31) + 0.5f) << 11) & 0x0000F800 ); ushort |= (((int)((shoveComponents[1] * 31) + 0.5f) << 6) & 0x000007C0 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565.java index f6193dd2d..7408c45b2 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,38 +51,40 @@ import java.nio.*; * @author Administrator */ public class Extract565 implements Extract { - + /** Creates a new instance of Extract565 */ public Extract565() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { int ushort = 0; - + if( isSwap ) { ushort = 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( packedPixel.getShort() ); } else { ushort = 0x0000FFFF & packedPixel.getShort(); } - + // 11111000,00000000 == 0xF800 // 00000111,11100000 == 0x07E0 // 00000000,00111111 == 0x001F - + extractComponents[0] = (float)( ( ushort & 0xF800 ) >> 11 ) / 31.0f; extractComponents[1] = (float)( ( ushort & 0x07E0 ) >> 5 ) / 63.0f; extractComponents[2] = (float)( ( ushort & 0x001F ) ) / 31.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11111000,00000000 == 0xF800 // 00000111,11100000 == 0x07E0 // 00000000,00111111 == 0x001F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); - + // due to limited precision, need to round before shoving int ushort = (((int)((shoveComponents[0] * 31) + 0.5f) << 11) & 0x0000F800 ); ushort |= (((int)((shoveComponents[1] * 63) + 0.5f) << 5) & 0x000007E0 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565rev.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565rev.java index 2e455adfa..adaafa7ea 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565rev.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract565rev.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,38 +51,40 @@ import java.nio.*; * @author Administrator */ public class Extract565rev implements Extract { - + /** Creates a new instance of Extract565rev */ public Extract565rev() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { int ushort = 0; - + if( isSwap ) { ushort = 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( packedPixel.getShort() ); } else { ushort = 0x0000FFFF & packedPixel.getShort(); } - + // 00000000,00011111 == 0x001F // 00000111,11100000 == 0x07E0 // 11111000,00000000 == 0xF800 - + extractComponents[0] = (float)( ( ushort & 0x001F ) ) / 31.0f; extractComponents[1] = (float)( ( ushort & 0x07E0 ) >> 5 ) / 63.0f; extractComponents[2] = (float)( ( ushort & 0xF800 ) >> 11 ) / 31.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 00000000,00111111 == 0x001F // 00000111,11100000 == 0x07E0 // 11111000,00000000 == 0xF800 - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); - + // due to limited precision, need to round before shoving int ushort = (((int)((shoveComponents[0] * 31) + 0.5f) ) & 0x0000001F ); ushort |= (((int)((shoveComponents[1] * 63) + 0.5f) << 5) & 0x000007E0 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888.java index 137fa3c21..be155d578 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.*; * @author Administrator */ public class Extract8888 implements Extract { - + /** Creates a new instance of Extract8888 */ public Extract8888() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { long uint = 0; - + if( isSwap ) { uint = 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( packedPixel.getInt() ); } else { uint = 0x00000000FFFFFFFF & packedPixel.getInt(); } - + // 11111000,00000000 == 0xF800 // 00000111,11000000 == 0x07C0 // 00000000,00111110 == 0x003E // 00000000,00000001 == 0x0001 - + extractComponents[0] = (float)( ( uint & 0xFF000000 ) >> 24 ) / 255.0f; extractComponents[1] = (float)( ( uint & 0x00FF0000 ) >> 16 ) / 255.0f; extractComponents[2] = (float)( ( uint & 0x0000FF00 ) >> 8 ) / 255.0f; extractComponents[3] = (float)( ( uint & 0x000000FF ) ) / 255.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving long uint = (((int)((shoveComponents[0] * 255) + 0.5f) << 24) & 0xFF000000 ); uint |= (((int)((shoveComponents[1] * 255) + 0.5f) << 16) & 0x00FF0000 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888rev.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888rev.java index 2ac942c84..294e60e12 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888rev.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Extract8888rev.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,42 +51,44 @@ import java.nio.*; * @author Administrator */ public class Extract8888rev implements Extract { - + /** Creates a new instance of Extract8888rev */ public Extract8888rev() { } - + + @Override public void extract( boolean isSwap, ByteBuffer packedPixel, float[] extractComponents ) { long uint = 0; - + if( isSwap ) { uint = 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( packedPixel.getInt() ); } else { uint = 0x00000000FFFFFFFF & packedPixel.getInt(); } - + // 11111000,00000000 == 0xF800 // 00000111,11000000 == 0x07C0 // 00000000,00111110 == 0x003E // 00000000,00000001 == 0x0001 - + extractComponents[0] = (float)( ( uint & 0x000000FF ) ) / 255.0f; extractComponents[1] = (float)( ( uint & 0x0000FF00 ) >> 8 ) / 255.0f; extractComponents[2] = (float)( ( uint & 0x00FF0000 ) >> 16 ) / 255.0f; extractComponents[3] = (float)( ( uint & 0xFF000000 ) >> 24 ) / 255.0f; } - + + @Override public void shove( float[] shoveComponents, int index, ByteBuffer packedPixel ) { // 11110000,00000000 == 0xF000 // 00001111,00000000 == 0x0F00 // 00000000,11110000 == 0x00F0 // 00000000,00001111 == 0x000F - + assert( 0.0f <= shoveComponents[0] && shoveComponents[0] <= 1.0f ); assert( 0.0f <= shoveComponents[1] && shoveComponents[1] <= 1.0f ); assert( 0.0f <= shoveComponents[2] && shoveComponents[2] <= 1.0f ); assert( 0.0f <= shoveComponents[3] && shoveComponents[3] <= 1.0f ); - + // due to limited precision, need to round before shoving long uint = (((int)((shoveComponents[0] * 255) + 0.5f) ) & 0x000000FF ); uint |= (((int)((shoveComponents[1] * 255) + 0.5f) << 8) & 0x0000FF00 ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractFloat.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractFloat.java index 52c2191b9..1dd8bff8a 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractFloat.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractFloat.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.*; * @author Administrator */ public class ExtractFloat implements ExtractPrimitive { - + /** Creates a new instance of ExtractFloat */ public ExtractFloat() { } - + + @Override public double extract( boolean isSwap, ByteBuffer data ) { float f = 0; if( isSwap ) { @@ -66,7 +67,8 @@ public class ExtractFloat implements ExtractPrimitive { assert( f <= 1.0f ); return( f ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { assert(0.0 <= value && value < 1.0); data.asFloatBuffer().put( index, (float)value ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractPrimitive.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractPrimitive.java index 926096649..a44fb9508 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractPrimitive.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractPrimitive.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSByte.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSByte.java index 2e1a9a0a6..dcbe52a40 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSByte.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSByte.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,17 +51,19 @@ import java.nio.ByteBuffer; * @author Administrator */ public class ExtractSByte implements ExtractPrimitive { - + /** Creates a new instance of ExtractUByte */ public ExtractSByte() { } - + + @Override public double extract( boolean isSwap, ByteBuffer sbyte ) { byte b = sbyte.get(); assert( b <= 127 ); return( b ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { data.position( index ); data.put( (byte)value ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSInt.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSInt.java index ca80747c4..547bd944a 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSInt.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSInt.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.*; * @author Administrator */ public class ExtractSInt implements ExtractPrimitive { - + /** Creates a new instance of ExtractSInt */ public ExtractSInt() { } - + + @Override public double extract( boolean isSwap, ByteBuffer uint ) { int i = 0; if( isSwap ) { @@ -66,7 +67,8 @@ public class ExtractSInt implements ExtractPrimitive { assert( i <= 0x7FFFFFFF ); return( i ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { assert(0.0 <= value && value < Integer.MAX_VALUE); IntBuffer ib = data.asIntBuffer(); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSShort.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSShort.java index 979c3b449..7dc172976 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSShort.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractSShort.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.*; * @author Administrator */ public class ExtractSShort implements ExtractPrimitive { - + /** Creates a new instance of ExtractSShort */ public ExtractSShort() { } - + + @Override public double extract( boolean isSwap, ByteBuffer ushort ) { short s = 0; if( isSwap ) { @@ -66,7 +67,8 @@ public class ExtractSShort implements ExtractPrimitive { assert( s <= 32767 ); return( s ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { assert(0.0 <= value && value < 32768.0); ShortBuffer sb = data.asShortBuffer(); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUByte.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUByte.java index 4d14212ab..3e933811c 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUByte.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUByte.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,17 +51,19 @@ import java.nio.ByteBuffer; * @author Administrator */ public class ExtractUByte implements ExtractPrimitive { - + /** Creates a new instance of ExtractUByte */ public ExtractUByte() { } - + + @Override public double extract( boolean isSwap, ByteBuffer ubyte ) { int i = 0x000000FF & ubyte.get(); assert( i <= 255 ); return( i ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { assert(0.0 <= value && value < 256.0); data.position( index ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUInt.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUInt.java index c088ca301..1c34828b3 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUInt.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUInt.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.*; * @author Administrator */ public class ExtractUInt implements ExtractPrimitive { - + /** Creates a new instance of ExtractUInt */ public ExtractUInt() { } - + + @Override public double extract( boolean isSwap, ByteBuffer uint ) { long i = 0; if( isSwap ) { @@ -66,7 +67,8 @@ public class ExtractUInt implements ExtractPrimitive { assert( i <= 0xFFFFFFFF ); return( i ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { assert(0.0 <= value && value < 0xFFFFFFFF); IntBuffer ib = data.asIntBuffer(); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUShort.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUShort.java index 81db60f0f..8e0d25c42 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUShort.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ExtractUShort.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,12 @@ import java.nio.*; * @author Administrator */ public class ExtractUShort implements ExtractPrimitive { - + /** Creates a new instance of ExtracUShort */ public ExtractUShort() { } - + + @Override public double extract( boolean isSwap, ByteBuffer ushort ) { int i = 0; if( isSwap ) { @@ -66,7 +67,8 @@ public class ExtractUShort implements ExtractPrimitive { assert( i <= 65535 ); return( i ); } - + + @Override public void shove( double value, int index, ByteBuffer data ) { assert(0.0 <= value && value < 65536.0); ShortBuffer sb = data.asShortBuffer(); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/HalveImage.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/HalveImage.java index 7549044ba..184c5fda8 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/HalveImage.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/HalveImage.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -52,11 +52,11 @@ import java.nio.*; * @author Administrator */ public class HalveImage { - + private static final int BOX2 = 2; private static final int BOX4 = 4; private static final int BOX8 = 8; - + public static void halveImage( int components, int width, int height, ShortBuffer datain, ShortBuffer dataout ) { int i, j, k; @@ -64,11 +64,11 @@ public class HalveImage { int delta; int t = 0; short temp = 0; - + newwidth = width / 2; newheight = height /2; delta = width * components; - + // Piece of cake for( i = 0; i < newheight; i++ ) { for( j = 0; j < newwidth; j++ ) { @@ -91,7 +91,7 @@ public class HalveImage { t += delta; } } - + public static void halveImage_ubyte( int components, int width, int height, ByteBuffer datain, ByteBuffer dataout, int element_size, int ysize, int group_size ) { @@ -99,19 +99,19 @@ public class HalveImage { int newwidth, newheight; int s; int t; - + // Handle case where there is only 1 column/row if( width == 1 || height == 1 ) { assert( !( width == 1 && height == 1 ) ); // can't be 1x1 halve1Dimage_ubyte( components, width, height, datain, dataout, element_size, ysize, group_size ); return; } - + newwidth = width / 2; newheight = height / 2; s = 0; t = 0; - + int temp = 0; // piece of cake for( i = 0; i < newheight; i++ ) { @@ -133,9 +133,9 @@ public class HalveImage { t += ysize; } } - + public static void halve1Dimage_ubyte( int components, int width, int height, - ByteBuffer datain, ByteBuffer dataout, + ByteBuffer datain, ByteBuffer dataout, int element_size, int ysize, int group_size ) { int halfWidth = width / 2; int halfHeight = height / 2; @@ -143,14 +143,14 @@ public class HalveImage { int dest = 0; int jj; int temp = 0; - + assert( width == 1 || height == 1 ); // Must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // widthxheight can't be 1x1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -161,7 +161,7 @@ public class HalveImage { temp /= 2; dataout.put( (byte)temp ); /* - dataout.setByte( (byte)(((0x000000FF & datain.setIndexInBytes(src).getByte()) + + dataout.setByte( (byte)(((0x000000FF & datain.setIndexInBytes(src).getByte()) + (0x000000FF & datain.setIndexInBytes( src + group_size ).getByte())) / 2 ) ); */ src += element_size; @@ -188,7 +188,7 @@ public class HalveImage { temp /= 2; dataout.put( (byte)temp ); /* - dataout.setByte( (byte)(((0x000000FF & datain.setIndexInBytes(src).getByte()) + + dataout.setByte( (byte)(((0x000000FF & datain.setIndexInBytes(src).getByte()) + (0x000000FF & datain.setIndexInBytes(src + ysize).getByte()) ) / 2 ) ); */ src += element_size; @@ -202,7 +202,7 @@ public class HalveImage { assert( src == ysize * height ); assert( dest == components * element_size * halfWidth * halfHeight ); } - + public static void halveImage_byte( int components, int width, int height, ByteBuffer datain, ByteBuffer dataout, int element_size, int ysize, int group_size ) { @@ -211,7 +211,7 @@ public class HalveImage { int s = 0; int t = 0; byte temp = (byte)0; - + // handle case where there is only 1 column if( width == 1 || height == 1 ) { assert( !( width == 1 && height == 1 ) ); @@ -219,10 +219,10 @@ public class HalveImage { ysize, group_size ); return; } - + newwidth = width / 2; newheight = height / 2; - + for( i = 0; i < newheight; i++ ) { for( j = 0; j < newwidth; j++ ) { for( k = 0; k < components; k++ ) { @@ -244,7 +244,7 @@ public class HalveImage { t += ysize; } } - + public static void halve1Dimage_byte( int components, int width, int height, ByteBuffer datain, ByteBuffer dataout, int element_size, int ysize, int group_size ) { @@ -254,14 +254,14 @@ public class HalveImage { int dest = 0; int jj; byte temp = (byte)0; - + assert( width == 1 || height == 1 ); // must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // widthxheight can't be 1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -284,7 +284,7 @@ public class HalveImage { halfWidth = 1; // one vertical column with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -303,7 +303,7 @@ public class HalveImage { } assert( dest == components * element_size * halfWidth * halfHeight ); } - + public static void halveImage_ushort( int components, int width, int height, ByteBuffer datain, ShortBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -319,10 +319,10 @@ public class HalveImage { ysize, group_size, myswap_bytes ); return; } - + newwidth = width / 2; newheight = height / 2; - + // Piece of cake if( !myswap_bytes ) { for( i = 0; i < newheight; i++ ) { @@ -364,7 +364,7 @@ public class HalveImage { } } } - + public static void halve1Dimage_ushort( int components, int width, int height, ByteBuffer datain, ShortBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -373,14 +373,14 @@ public class HalveImage { int src = 0; int dest = 0; int jj; - + assert( width == 1 || height == 1 ); // must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // widthxheight can't be 1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < halfHeight; kk++ ) { @@ -410,7 +410,7 @@ public class HalveImage { halfWidth = 1; // one vertical column with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -437,7 +437,7 @@ public class HalveImage { } assert( dest == components * element_size * halfWidth * halfHeight ); } - + public static void halveImage_short( int components, int width, int height, ByteBuffer datain, ShortBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -453,10 +453,10 @@ public class HalveImage { ysize, group_size, myswap_bytes ); return; } - + newwidth = width / 2; newheight = height / 2; - + // Piece of cake if( !myswap_bytes ) { for( i = 0; i < newheight; i++ ) { @@ -504,7 +504,7 @@ public class HalveImage { } } } - + public static void halve1Dimage_short( int components, int width, int height, ByteBuffer datain, ShortBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -513,14 +513,14 @@ public class HalveImage { int src = 0; int dest = 0; int jj; - + assert( width == 1 || height == 1 ); // must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // can't be 1x1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -550,7 +550,7 @@ public class HalveImage { halfWidth = 1; // one vertical column with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -577,7 +577,7 @@ public class HalveImage { } assert( dest == ( components * element_size * halfWidth * halfHeight ) ); } - + public static void halveImage_uint( int components, int width, int height, ByteBuffer datain, IntBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -586,7 +586,7 @@ public class HalveImage { int s = 0; int t = 0; double temp = 0; - + // handle case where there is only 1 column/row if( width == 1 || height == 1 ) { assert( !( width == 1 && height == 1 ) ); // can't be 1x1 @@ -594,10 +594,10 @@ public class HalveImage { ysize, group_size, myswap_bytes ); return; } - + newwidth = width / 2; newheight = height / 2; - + // Piece of cake if( !myswap_bytes ) { for( i = 0; i < newheight; i++ ) { @@ -643,7 +643,7 @@ public class HalveImage { } } } - + public static void halve1Dimage_uint( int components, int width, int height, ByteBuffer datain, IntBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -652,14 +652,14 @@ public class HalveImage { int src = 0; int dest = 0; int jj; - + assert( width == 1 || height == 1 ); // must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // widthxheight can't be 1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < halfHeight; kk++ ) { @@ -689,7 +689,7 @@ public class HalveImage { halfWidth = 1; // one vertical column with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -716,7 +716,7 @@ public class HalveImage { } assert( dest == components * element_size * halfWidth * halfHeight ); } - + public static void halveImage_int( int components, int width, int height, ByteBuffer datain, IntBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -725,7 +725,7 @@ public class HalveImage { int s = 0; int t = 0; int temp = 0; - + // handle case where there is only 1 column/row if( width == 1 || height == 1 ) { assert( !( width == 1 && height == 1 ) ); // can't be 1x1 @@ -733,10 +733,10 @@ public class HalveImage { ysize, group_size, myswap_bytes ); return; } - + newwidth = width / 2; newheight = height / 2; - + // Piece of cake if( !myswap_bytes ) { for( i = 0; i < newheight; i++ ) { @@ -785,7 +785,7 @@ public class HalveImage { } } } - + public static void halve1Dimage_int( int components, int width, int height, ByteBuffer datain, IntBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -794,14 +794,14 @@ public class HalveImage { int src = 0; int dest = 0; int jj; - + assert( width == 1 || height == 1 ); // must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // can't be 1x1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -831,7 +831,7 @@ public class HalveImage { halfWidth = 1; // one vertical column with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -858,7 +858,7 @@ public class HalveImage { } assert( dest == ( components * element_size * halfWidth * halfHeight ) ); } - + public static void halveImage_float( int components, int width, int height, ByteBuffer datain, FloatBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -874,10 +874,10 @@ public class HalveImage { ysize, group_size, myswap_bytes ); return; } - + newwidth = width / 2; newheight = height / 2; - + // Piece of cake if( !myswap_bytes ) { for( i = 0; i < newheight; i++ ) { @@ -920,7 +920,7 @@ public class HalveImage { } } } - + public static void halve1Dimage_float( int components, int width, int height, ByteBuffer datain, FloatBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { @@ -929,14 +929,14 @@ public class HalveImage { int src = 0; int dest = 0; int jj; - + assert( width == 1 || height == 1 ); // must be 1D assert( width != height ); // can't be square - + if( height == 1 ) { // 1 row assert( width != 1 ); // can't be 1x1 halfHeight = 1; - + for( jj = 0; jj < halfWidth; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -966,7 +966,7 @@ public class HalveImage { halfWidth = 1; // one vertical column with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { int kk; for( kk = 0; kk < components; kk++ ) { @@ -993,9 +993,9 @@ public class HalveImage { } assert( dest == ( components * element_size * halfWidth * halfHeight ) ); } - - public static void halveImagePackedPixel( int components, Extract extract, int width, - int height, ByteBuffer datain, ByteBuffer dataout, + + public static void halveImagePackedPixel( int components, Extract extract, int width, + int height, ByteBuffer datain, ByteBuffer dataout, int pixelSizeInBytes, int rowSizeInBytes, boolean isSwap ) { if( width == 1 || height == 1 ) { assert( !( width == 1 && height == 1 ) ); @@ -1004,19 +1004,19 @@ public class HalveImage { return; } int ii, jj; - + int halfWidth = width / 2; int halfHeight = height / 2; int src = 0; int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes ); int outIndex = 0; - + for( ii = 0; ii < halfHeight; ii++ ) { for( jj = 0; jj < halfWidth; jj++ ) { float totals[] = new float[4]; float extractTotals[][] = new float[BOX4][4]; int cc; - + datain.position( src ); extract.extract( isSwap, datain, extractTotals[0] ); datain.position( src + pixelSizeInBytes ); @@ -1045,7 +1045,7 @@ public class HalveImage { assert( src == rowSizeInBytes * height ); assert( outIndex == halfWidth * halfHeight ); } - + public static void halve1DimagePackedPixel( int components, Extract extract, int width, int height, ByteBuffer datain, ByteBuffer dataout, int pixelSizeInBytes, int rowSizeInBytes, boolean isSwap ) { @@ -1053,23 +1053,23 @@ public class HalveImage { int halfHeight = height / 2; int src = 0; int jj; - + assert( width == 1 || height == 1 ); assert( width != height ); - + if( height == 1 ) { int outIndex = 0; - + assert( width != 1 ); halfHeight = 1; - + // one horizontal row with possible pad bytes - + for( jj = 0; jj < halfWidth; jj++ ) { float[] totals = new float[4]; float[][] extractTotals = new float[BOX2][4]; int cc; - + datain.position( src ); extract.extract( isSwap, datain, extractTotals[0] ); datain.position( src + pixelSizeInBytes ); @@ -1090,17 +1090,17 @@ public class HalveImage { } int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes ); src += padBytes; - + assert( src == rowSizeInBytes ); assert( outIndex == halfWidth * halfHeight ); } else if( width == 1 ) { int outIndex = 0; - + assert( height != 1 ); halfWidth = 1; // one vertical volumn with possible pad bytes per row // average two at a time - + for( jj = 0; jj < halfHeight; jj++ ) { float[] totals = new float[4]; float[][] extractTotals = new float[BOX2][4]; @@ -1128,7 +1128,7 @@ public class HalveImage { assert( outIndex == halfWidth * halfHeight ); } } - + public static void halveImagePackedPixelSlice( int components, Extract extract, int width, int height, int depth, ByteBuffer dataIn, ByteBuffer dataOut, int pixelSizeInBytes, int rowSizeInBytes, @@ -1140,26 +1140,26 @@ public class HalveImage { int src = 0; int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes ); int outIndex = 0; - + assert( (width == 1 || height == 1) && depth >= 2 ); - + if( width == height ) { assert( width == 1 && height == 1 ); assert( depth >= 2 ); - + for( ii = 0; ii < halfDepth; ii++ ) { float totals[] = new float[4]; float extractTotals[][] = new float[BOX2][4]; int cc; - + dataIn.position( src ); extract.extract( isSwap, dataIn, extractTotals[0] ); dataIn.position( src + imageSizeInBytes ); extract.extract( isSwap, dataIn, extractTotals[1] ); - + for( cc = 0; cc < components; cc++ ) { int kk; - + // average only 2 pixels since a column totals[cc]= 0.0f; for( kk = 0; kk < BOX2; kk++ ) { @@ -1167,7 +1167,7 @@ public class HalveImage { } totals[cc] /= BOX2; } // for cc - + extract.shove( totals, outIndex, dataOut ); outIndex++; // skip over to next group of 2 @@ -1175,13 +1175,13 @@ public class HalveImage { } // for ii } else if( height == 1 ) { assert( width != 1 ); - + for( ii = 0; ii < halfDepth; ii++ ) { for( jj = 0; jj < halfWidth; jj++ ) { float totals[] = new float[4]; float extractTotals[][] = new float[BOX4][4]; int cc; - + dataIn.position( src ); extract.extract( isSwap, dataIn, extractTotals[0] ); dataIn.position( src + pixelSizeInBytes ); @@ -1190,10 +1190,10 @@ public class HalveImage { extract.extract( isSwap, dataIn, extractTotals[2] ); dataIn.position( src + pixelSizeInBytes + imageSizeInBytes ); extract.extract( isSwap, dataIn, extractTotals[3] ); - + for( cc = 0; cc < components; cc++ ) { int kk; - + // grab 4 pixels to average totals[cc] = 0.0f; for( kk = 0; kk < BOX4; kk++ ) { @@ -1209,13 +1209,13 @@ public class HalveImage { } } else if( width == 1 ) { assert( height != 1 ); - + for( ii = 0; ii < halfDepth; ii++ ) { for( jj = 0; jj < halfWidth; jj++ ) { float totals[] = new float[4]; float extractTotals[][] = new float[BOX4][4]; int cc; - + dataIn.position( src ); extract.extract( isSwap, dataIn, extractTotals[0] ); dataIn.position( src + rowSizeInBytes ); @@ -1224,10 +1224,10 @@ public class HalveImage { extract.extract( isSwap, dataIn, extractTotals[2] ); dataIn.position( src + rowSizeInBytes + imageSizeInBytes ); extract.extract( isSwap, dataIn, extractTotals[3] ); - + for( cc = 0; cc < components; cc++ ) { int kk; - + // grab 4 pixels to average totals[cc] = 0.0f; for( kk = 0; kk < BOX4; kk++ ) { @@ -1243,7 +1243,7 @@ public class HalveImage { } } } - + public static void halveImageSlice( int components, ExtractPrimitive extract, int width, int height, int depth, ByteBuffer dataIn, ByteBuffer dataOut, int elementSizeInBytes, int groupSizeInBytes, int rowSizeInBytes, @@ -1255,25 +1255,25 @@ public class HalveImage { int src = 0; int padBytes = rowSizeInBytes - ( width * groupSizeInBytes ); int outIndex = 0; - + assert( (width == 1 || height == 1) && depth >= 2 ); - + if( width == height ) { assert( width == 1 && height == 1 ); assert( depth >= 2 ); - + for( ii = 0; ii < halfDepth; ii++ ) { int cc; for( cc = 0; cc < components; cc++ ) { double[] totals = new double[4]; double[][] extractTotals = new double[BOX2][4]; int kk; - + dataIn.position( src ); extractTotals[0][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + imageSizeInBytes ); extractTotals[1][cc] = extract.extract( isSwap, dataIn ); - + // average 2 pixels since only a column totals[cc] = 0.0f; // totals[red] = extractTotals[0][red] + extractTotals[1][red]; @@ -1282,7 +1282,7 @@ public class HalveImage { totals[cc] += extractTotals[kk][cc]; } totals[cc] /= (double)BOX2; - + extract.shove( totals[cc], outIndex, dataOut ); outIndex++; src += elementSizeInBytes; @@ -1290,12 +1290,12 @@ public class HalveImage { // skip over next group of 2 src += rowSizeInBytes; } // for ii - + assert( src == rowSizeInBytes * height * depth ); assert( outIndex == halfDepth * components ); } else if( height == 1 ) { assert( width != 1 ); - + for( ii = 0; ii < halfDepth; ii++ ) { for( jj = 0; jj < halfWidth; jj++ ) { int cc; @@ -1303,7 +1303,7 @@ public class HalveImage { int kk; double totals[] = new double[4]; double extractTotals[][] = new double[BOX4][4]; - + dataIn.position( src ); extractTotals[0][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + groupSizeInBytes ); @@ -1312,7 +1312,7 @@ public class HalveImage { extractTotals[2][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + imageSizeInBytes + groupSizeInBytes ); extractTotals[3][cc] = extract.extract( isSwap, dataIn ); - + // grab 4 pixels to average totals[cc] = 0.0f; // totals[red] = extractTotals[0][red] + extractTotals[1][red] + @@ -1322,7 +1322,7 @@ public class HalveImage { totals[cc] += extractTotals[kk][cc]; } totals[cc] /= (double)BOX4; - + extract.shove( totals[cc], outIndex, dataOut ); outIndex++; src += elementSizeInBytes; @@ -1337,7 +1337,7 @@ public class HalveImage { assert( outIndex == halfWidth * halfDepth * components ); } else if( width == 1 ) { assert( height != 1 ); - + for( ii = 0; ii < halfDepth; ii++ ) { for( jj = 0; jj < halfHeight; jj++ ) { int cc; @@ -1345,7 +1345,7 @@ public class HalveImage { int kk; double totals[] = new double[4]; double extractTotals[][] = new double[BOX4][4]; - + dataIn.position( src ); extractTotals[0][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + rowSizeInBytes ); @@ -1354,8 +1354,8 @@ public class HalveImage { extractTotals[2][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + imageSizeInBytes + groupSizeInBytes ); extractTotals[3][cc] = extract.extract( isSwap, dataIn ); - - + + // grab 4 pixels to average totals[cc] = 0.0f; // totals[red] = extractTotals[0][red] + extractTotals[1][red] + @@ -1365,7 +1365,7 @@ public class HalveImage { totals[cc] += extractTotals[kk][cc]; } totals[cc] /= (double)BOX4; - + extract.shove( totals[cc], outIndex, dataOut ); outIndex++; src += elementSizeInBytes; @@ -1380,32 +1380,32 @@ public class HalveImage { assert( outIndex == halfWidth * halfDepth * components ); } } - + public static void halveImage3D( int components, ExtractPrimitive extract, int width, int height, int depth, ByteBuffer dataIn, ByteBuffer dataOut, int elementSizeInBytes, int groupSizeInBytes, int rowSizeInBytes, int imageSizeInBytes, boolean isSwap ) { assert( depth > 1 ); - + // horizontal/vertical/onecolumn slice viewed from top if( width == 1 || height == 1 ) { assert( 1 <= depth ); - + halveImageSlice( components, extract, width, height, depth, dataIn, dataOut, elementSizeInBytes, groupSizeInBytes, rowSizeInBytes, imageSizeInBytes, isSwap ); return; } - + int ii, jj, dd; - + int halfWidth = width / 2; int halfHeight = height / 2; int halfDepth = depth / 2; int src = 0; int padBytes = rowSizeInBytes - ( width * groupSizeInBytes ); int outIndex = 0; - + for( dd = 0; dd < halfDepth; dd++ ) { for( ii = 0; ii < halfHeight; ii++ ) { for( jj = 0; jj < halfWidth; jj++ ) { @@ -1414,7 +1414,7 @@ public class HalveImage { int kk; double totals[] = new double[4]; double extractTotals[][] = new double[BOX8][4]; - + dataIn.position( src ); extractTotals[0][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + groupSizeInBytes ); @@ -1431,17 +1431,17 @@ public class HalveImage { extractTotals[6][cc] = extract.extract( isSwap, dataIn ); dataIn.position( src + rowSizeInBytes + imageSizeInBytes + groupSizeInBytes ); extractTotals[7][cc] = extract.extract( isSwap, dataIn ); - + totals[cc] = 0.0f; - + for( kk = 0; kk < BOX8; kk++ ) { totals[cc] += extractTotals[kk][cc]; } totals[cc] /= (double)BOX8; - + extract.shove( totals[cc], outIndex, dataOut ); outIndex++; - + src += elementSizeInBytes; } // for cc // skip over to next square of 4 @@ -1456,40 +1456,40 @@ public class HalveImage { assert( src == rowSizeInBytes * height * depth ); assert( outIndex == halfWidth * halfHeight * halfDepth * components ); } - + public static void halveImagePackedPixel3D( int components, Extract extract, - int width, int height, int depth, ByteBuffer dataIn, + int width, int height, int depth, ByteBuffer dataIn, ByteBuffer dataOut, int pixelSizeInBytes, int rowSizeInBytes, int imageSizeInBytes, boolean isSwap ) { if( depth == 1 ) { assert( 1 <= width && 1 <= height ); - + halveImagePackedPixel( components, extract, width, height, dataIn, dataOut, pixelSizeInBytes, rowSizeInBytes, isSwap ); return; } else if( width == 1 || height == 1 ) { // a horizontal or vertical slice viewed from top assert( 1 <= depth ); - + halveImagePackedPixelSlice( components, extract, width, height, depth, dataIn, dataOut, pixelSizeInBytes, rowSizeInBytes, imageSizeInBytes, isSwap ); return; } int ii, jj, dd; - + int halfWidth = width / 2; int halfHeight = height / 2; int halfDepth = depth / 2; int src = 0; int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes ); int outIndex = 0; - + for( dd = 0; dd < halfDepth; dd++ ) { for( ii = 0; ii < halfHeight; ii++ ) { for( jj = 0; jj < halfWidth; jj++ ) { float totals[] = new float[4]; // 4 is max components float extractTotals[][] = new float[BOX8][4]; int cc; - + dataIn.position( src ); extract.extract( isSwap, dataIn, extractTotals[0] ); dataIn.position( src + pixelSizeInBytes ); @@ -1506,7 +1506,7 @@ public class HalveImage { extract.extract( isSwap, dataIn, extractTotals[6] ); dataIn.position( src + rowSizeInBytes + pixelSizeInBytes + imageSizeInBytes ); extract.extract( isSwap, dataIn, extractTotals[7] ); - + for( cc = 0; cc < components; cc++ ) { int kk; // grab 8 pixels to average diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Image.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Image.java index b610ce86b..18f814dde 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Image.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Image.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -53,18 +53,18 @@ import java.nio.*; * @author Administrator */ public class Image { - + /** Creates a new instance of Image */ public Image() { } - + public static short getShortFromByteArray( byte[] array, int index ) { short s; s = (short)(array[index] << 8 ); s |= (short)(0x00FF & array[index+1]); return( s ); } - + public static int getIntFromByteArray( byte[] array, int index ) { int i; i = ( array[index] << 24 ) & 0xFF000000; @@ -73,12 +73,12 @@ public class Image { i |= ( array[index+3] ) & 0x000000FF; return( i ); } - + public static float getFloatFromByteArray( byte[] array, int index ) { int i = getIntFromByteArray( array, index ); return( Float.intBitsToFloat(i) ); } - + /* * Extract array from user's data applying all pixel store modes. * The internal format used is an array of unsigned shorts. @@ -98,7 +98,7 @@ public class Image { int iter2; int i, j, k; boolean myswap_bytes; - + // Create a Extract interface object Extract extract = null; switch( type ) { @@ -139,7 +139,7 @@ public class Image { extract = new Extract2101010rev(); break; } - + myswap_bytes = psm.getUnpackSwapBytes(); components = Mipmap.elements_per_group( format, type ); if( psm.getUnpackRowLength() > 0 ) { @@ -147,12 +147,12 @@ public class Image { } else { groups_per_line = width; } - + // All formats except GL_BITMAP fall out trivially if( type == GL2.GL_BITMAP ) { int bit_offset; int current_bit; - + rowsize = ( groups_per_line * components + 7 ) / 8; padding = ( rowsize % psm.getUnpackAlignment() ); if( padding != 0 ) { @@ -200,7 +200,7 @@ public class Image { if( element_size == 1 ) { myswap_bytes = false; } - + rowsize = groups_per_line * group_size; padding = ( rowsize % psm.getUnpackAlignment() ); if( padding != 0 ) { @@ -208,7 +208,7 @@ public class Image { } start = psm.getUnpackSkipRows() * rowsize + psm.getUnpackSkipPixels() * group_size; elements_per_line = width * components; - + iter2 = 0; for( i = 0; i < height; i++ ) { iter = start; @@ -364,7 +364,7 @@ public class Image { // want iter pointing at start, not within, row for assertion purposes iter = start; } // for i - + // iterators should be one byte past end if( !Mipmap.isTypePackedPixel( type ) ) { assert( iter2 == ( width * height * components ) ); @@ -374,16 +374,16 @@ public class Image { assert( iter == ( rowsize * height + psm.getUnpackSkipRows() * rowsize + psm.getUnpackSkipPixels() * group_size ) ); } } - + /* * Insert array into user's data applying all pixel store modes. * Theinternal format is an array of unsigned shorts. * empty_image() because it is the opposet of fill_image(). */ - public static void empty_image( PixelStorageModes psm, int width, int height, - int format, int type, boolean index_format, + public static void empty_image( PixelStorageModes psm, int width, int height, + int format, int type, boolean index_format, ShortBuffer oldimage, ByteBuffer userdata ) { - + int components; int element_size; int rowsize; @@ -396,7 +396,7 @@ public class Image { int iter2; int i, j, k; boolean myswap_bytes; - + // Create a Extract interface object Extract extract = null; switch( type ) { @@ -437,7 +437,7 @@ public class Image { extract = new Extract2101010rev(); break; } - + myswap_bytes = psm.getPackSwapBytes(); components = Mipmap.elements_per_group( format, type ); if( psm.getPackRowLength() > 0 ) { @@ -445,12 +445,12 @@ public class Image { } else { groups_per_line = width; } - + // all formats except GL_BITMAP fall out trivially if( type == GL2.GL_BITMAP ) { int bit_offset; int current_bit; - + rowsize = ( groups_per_line * components + 7 ) / 8; padding = ( rowsize % psm.getPackAlignment() ); if( padding != 0 ) { @@ -472,7 +472,7 @@ public class Image { current_bit = 0; } } - + if( current_bit != 0 ) { if( psm.getPackLsbFirst() ) { userdata.put( iter, (byte)( ( userdata.get( iter ) | ( 1 << bit_offset ) ) ) ); @@ -488,7 +488,7 @@ public class Image { userdata.put( iter, (byte)( ( userdata.get( iter ) & ~( 7 - bit_offset ) ) ) ); } } - + bit_offset++; if( bit_offset == 8 ) { bit_offset = 0; @@ -500,13 +500,13 @@ public class Image { } } else { float shoveComponents[] = new float[4]; - + element_size = Mipmap.bytes_per_element( type ); group_size = element_size * components; if( element_size == 1 ) { myswap_bytes = false; } - + rowsize = groups_per_line * group_size; padding = ( rowsize % psm.getPackAlignment() ); if( padding != 0 ) { @@ -514,13 +514,13 @@ public class Image { } start = psm.getPackSkipRows() * rowsize + psm.getPackSkipPixels() * group_size; elements_per_line = width * components; - + iter2 = 0; for( i = 0; i < height; i++ ) { iter = start; for( j = 0; j < elements_per_line; j++ ) { Type_Widget widget = new Type_Widget(); - + switch( type ) { case( GL2.GL_UNSIGNED_BYTE_3_3_2 ): for( k = 0; k < 3; k++ ) { @@ -799,7 +799,7 @@ public class Image { assert( iter == rowsize * height + psm.getPackSkipRows() * rowsize + psm.getPackSkipPixels() * group_size ); } } - + public static void fillImage3D( PixelStorageModes psm, int width, int height, int depth, int format, int type, boolean indexFormat, ByteBuffer userImage, ShortBuffer newImage ) { @@ -819,7 +819,7 @@ public class Image { int ww, hh, dd, k; Type_Widget widget = new Type_Widget(); float extractComponents[] = new float[4]; - + // Create a Extract interface object Extract extract = null; switch( type ) { @@ -860,7 +860,7 @@ public class Image { extract = new Extract2101010rev(); break; } - + myswapBytes = psm.getUnpackSwapBytes(); components = Mipmap.elements_per_group( format, type ); if( psm.getUnpackRowLength() > 0 ) { @@ -873,7 +873,7 @@ public class Image { if( elementSize == 1 ) { myswapBytes = false; } - + // 3dstuff begin if( psm.getUnpackImageHeight() > 0 ) { rowsPerImage = psm.getUnpackImageHeight(); @@ -881,27 +881,27 @@ public class Image { rowsPerImage = height; } // 3dstuff end - + rowSize = groupsPerLine * groupSize; padding = rowSize % psm.getUnpackAlignment(); if( padding != 0 ) { rowSize += psm.getUnpackAlignment() - padding; } - + imageSize = rowsPerImage * rowSize; // 3dstuff - - start = psm.getUnpackSkipRows() * rowSize + - psm.getUnpackSkipPixels() * groupSize + + + start = psm.getUnpackSkipRows() * rowSize + + psm.getUnpackSkipPixels() * groupSize + psm.getUnpackSkipImages() * imageSize; elementsPerLine = width * components; - + iter2 = 0; for( dd = 0; dd < depth; dd++ ) { rowStart = start; for( hh = 0; hh < height; hh++ ) { iter = rowStart; for( ww = 0; ww < elementsPerLine; ww++ ) { - + switch( type ) { case( GL2.GL_UNSIGNED_BYTE ): if( indexFormat ) { @@ -1063,18 +1063,18 @@ public class Image { } // for hh start += imageSize; }// for dd - + // iterators should be one byte past end if( !Mipmap.isTypePackedPixel( type ) ) { assert( iter2 == width * height * depth * components ); } else { assert( iter2 == width * height * depth * Mipmap.elements_per_group( format, 0 ) ); } - assert( iter == rowSize * height * depth + psm.getUnpackSkipRows() * rowSize + + assert( iter == rowSize * height * depth + psm.getUnpackSkipRows() * rowSize + psm.getUnpackSkipPixels() * groupSize + psm.getUnpackSkipImages() * imageSize ); } - + public static void emptyImage3D( PixelStorageModes psm, int width, int height, int depth, int format, int type, boolean indexFormat, ShortBuffer oldImage, ByteBuffer userImage ) { boolean myswapBytes; @@ -1092,7 +1092,7 @@ public class Image { int imageSize; Type_Widget widget = new Type_Widget(); float[] shoveComponents = new float[4]; - + // Create a Extract interface object Extract extract = null; switch( type ) { @@ -1133,9 +1133,9 @@ public class Image { extract = new Extract2101010rev(); break; } - + iter = 0; - + myswapBytes = psm.getPackSwapBytes(); components = Mipmap.elements_per_group( format, type ); if( psm.getPackRowLength() > 0 ) { @@ -1143,44 +1143,44 @@ public class Image { } else { groupsPerLine = width; } - + elementSize = Mipmap.bytes_per_element( type ); groupSize = elementSize * components; if( elementSize == 1 ) { myswapBytes = false; } - + // 3dstuff begin if( psm.getPackImageHeight() > 0 ) { rowsPerImage = psm.getPackImageHeight(); } else { rowsPerImage = height; } - + // 3dstuff end - + rowSize = groupsPerLine * groupSize; padding = rowSize % psm.getPackAlignment(); if( padding != 0 ) { rowSize += psm.getPackAlignment() - padding; } - + imageSize = rowsPerImage * rowSize; - + start = psm.getPackSkipRows() * rowSize + psm.getPackSkipPixels() * groupSize + psm.getPackSkipImages() * imageSize; elementsPerLine = width * components; - + iter2 = 0; for( dd = 0; dd < depth; dd++ ) { rowStart = start; - + for( ii = 0; ii < height; ii++ ) { iter = rowStart; - + for( jj = 0; jj < elementsPerLine; jj++ ) { - + switch( type ) { case( GL2.GL_UNSIGNED_BYTE ): if( indexFormat ) { @@ -1392,20 +1392,20 @@ public class Image { default: assert( false ); } - + iter += elementSize; } // for jj rowStart += rowSize; } // for ii start += imageSize; } // for dd - + if( !Mipmap.isTypePackedPixel( type ) ) { assert( iter2 == width * height * depth * components ); } else { assert( iter2 == width * height * depth * Mipmap.elements_per_group( format, 0 ) ); } - assert( iter == rowSize * height * depth + + assert( iter == rowSize * height * depth + psm.getUnpackSkipRows() * rowSize + psm.getUnpackSkipPixels() * groupSize + psm.getUnpackSkipImages() * imageSize ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java index b74d0a6b8..938873ec5 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -57,11 +57,11 @@ import com.jogamp.common.nio.Buffers; * @author Administrator */ public class Mipmap { - + /** Creates a new instance of Mipmap */ public Mipmap() { } - + public static int computeLog( int value ) { int i = 0; // Error @@ -79,7 +79,7 @@ public class Mipmap { i++; } } - + /* Compute the nearest power of 2 number. This algorithm is a little strange * but it works quite well. */ @@ -99,7 +99,7 @@ public class Mipmap { i *= 2; } } - + public static short GLU_SWAP_2_BYTES( short s ) { byte b = 0; b = (byte)( s >>> 8 ); @@ -107,7 +107,7 @@ public class Mipmap { s = (short)( s | (0x00FF & b) ); return( s ); } - + public static int GLU_SWAP_4_BYTES( int i ) { int t = i << 24; t |= 0x00FF0000 & ( i << 8 ); @@ -115,13 +115,13 @@ public class Mipmap { t |= 0x000000FF & ( i >>> 24 ); return( t ); } - + public static float GLU_SWAP_4_BYTES( float f ) { int i = Float.floatToRawIntBits( f ); float temp = Float.intBitsToFloat( i ); return( temp ); } - + public static int checkMipmapArgs( int internalFormat, int format, int type ) { if( !legalFormat( format ) || !legalType( type ) ) { return( GLU.GLU_INVALID_ENUM ); @@ -134,7 +134,7 @@ public class Mipmap { } return( 0 ); } - + public static boolean legalFormat( int format ) { switch( format ) { case( GL2.GL_COLOR_INDEX ): @@ -155,7 +155,7 @@ public class Mipmap { return( false ); } } - + public static boolean legalType( int type ) { switch( type ) { case( GL2.GL_BITMAP ): @@ -183,10 +183,10 @@ public class Mipmap { return( false ); } } - + public static boolean isTypePackedPixel( int type ) { assert( legalType( type ) ); - + if( type == GL2GL3.GL_UNSIGNED_BYTE_3_3_2 || type == GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV || type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5 || @@ -203,20 +203,20 @@ public class Mipmap { } return( false ); } - + public static boolean isLegalFormatForPackedPixelType( int format, int type ) { // if not a packed pixel type then return true if( isTypePackedPixel( type ) ) { return( true ); } - + // 3_3_2/2_3_3_REV & 5_6_5/5_6_5_REV are only compatible with RGB if( (type == GL2GL3.GL_UNSIGNED_BYTE_3_3_2 || type == GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV || type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5 || type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV ) & format != GL2GL3.GL_RGB ) { return( false ); } - + // 4_4_4_4/4_4_4_4_REV & 5_5_5_1/1_5_5_5_REV & 8_8_8_8/8_8_8_8_REV & // 10_10_10_2/2_10_10_10_REV are only campatible with RGBA, BGRA & ARGB_EXT if( ( type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4 || @@ -232,7 +232,7 @@ public class Mipmap { } return( true ); } - + public static boolean isLegalLevels( int userLevel, int baseLevel, int maxLevel, int totalLevels ) { if( (baseLevel < 0) || (baseLevel < userLevel) || (maxLevel < baseLevel) || @@ -241,7 +241,7 @@ public class Mipmap { } return( true ); } - + /* Given user requested textures size, determine if it fits. If it doesn't then * halve both sides and make the determination again until it does fit ( for * IR only ). @@ -257,7 +257,7 @@ public class Mipmap { int heightPowerOf2 = nearestPower( height ); int[] proxyWidth = new int[1]; boolean noProxyTextures = false; - + // Some drivers (in particular, ATI's) seem to set a GL error // when proxy textures are used even though this is in violation // of the spec. Guard against this and interactions with the @@ -268,20 +268,20 @@ public class Mipmap { int widthAtLevelOne = ( ( width > 1 ) ? (widthPowerOf2 >> 1) : widthPowerOf2 ); int heightAtLevelOne = ( ( height > 1 ) ? (heightPowerOf2 >> 1) : heightPowerOf2 ); int proxyTarget; - + assert( widthAtLevelOne > 0 ); assert( heightAtLevelOne > 0 ); - + // does width x height at level 1 & all their mipmaps fit? if( target == GL2GL3.GL_TEXTURE_2D || target == GL2GL3.GL_PROXY_TEXTURE_2D ) { proxyTarget = GL2GL3.GL_PROXY_TEXTURE_2D; gl.glTexImage2D( proxyTarget, 1, internalFormat, widthAtLevelOne, heightAtLevelOne, 0, format, type, null ); - } else if( (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_X) || - (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_X) || - (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_Y) || - (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) || - (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_Z) || + } else if( (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_X) || + (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_X) || + (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_Y) || + (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) || + (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_Z) || (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ) { proxyTarget = GL2GL3.GL_PROXY_TEXTURE_CUBE_MAP; gl.glTexImage2D( proxyTarget, 1, internalFormat, widthAtLevelOne, @@ -289,7 +289,7 @@ public class Mipmap { } else { assert( target == GL2GL3.GL_TEXTURE_1D || target == GL2GL3.GL_PROXY_TEXTURE_1D ); proxyTarget = GL2GL3.GL_PROXY_TEXTURE_1D; - gl.getGL2GL3().glTexImage1D( proxyTarget, 1, internalFormat, widthAtLevelOne, + gl.getGL2GL3().glTexImage1D( proxyTarget, 1, internalFormat, widthAtLevelOne, 0, format, type, null ); } if(gl.isGL2GL3()) { @@ -336,15 +336,15 @@ public class Mipmap { newHeight[0] = maxsize[0]; } } - - public static void closestFit3D( GL gl, int target, int width, int height, int depth, + + public static void closestFit3D( GL gl, int target, int width, int height, int depth, int internalFormat, int format, int type, int[] newWidth, int[] newHeight, int[] newDepth ) { int widthPowerOf2 = nearestPower( width ); int heightPowerOf2 = nearestPower( height ); int depthPowerOf2 = nearestPower( depth ); int[] proxyWidth = new int[1]; - + do { // compute level 1 width & height & depth, clamping each at 1 int widthAtLevelOne = (widthPowerOf2 > 1) ? widthPowerOf2 >> 1 : widthPowerOf2; @@ -354,7 +354,7 @@ public class Mipmap { assert( widthAtLevelOne > 0 ); assert( heightAtLevelOne > 0 ); assert( depthAtLevelOne > 0 ); - + // does width x height x depth at level 1 & all their mipmaps fit? if( target == GL2GL3.GL_TEXTURE_3D || target == GL2GL3.GL_PROXY_TEXTURE_3D ) { proxyTarget = GL2GL3.GL_PROXY_TEXTURE_3D; @@ -378,16 +378,16 @@ public class Mipmap { } } while( proxyWidth[0] == 0 ); // loop must terminate - + // return the width & height at level 0 that fits newWidth[0] = widthPowerOf2; newHeight[0] = heightPowerOf2; newDepth[0] = depthPowerOf2; } - + public static int elements_per_group( int format, int type ) { // Return the number of elements per grtoup of a specified gromat - + // If the type is packedpixels then answer is 1 if( type == GL2GL3.GL_UNSIGNED_BYTE_3_3_2 || type == GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV || @@ -403,7 +403,7 @@ public class Mipmap { type == GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV ) { return( 1 ); } - + // Types are not packed pixels so get elements per group switch( format ) { case( GL2GL3.GL_RGB ): @@ -418,10 +418,10 @@ public class Mipmap { return( 1 ); } } - + public static int bytes_per_element( int type ) { // return the number of bytes per element, based on the element type - + switch( type ) { case( GL2.GL_BITMAP ): case( GL2GL3.GL_BYTE ): @@ -450,17 +450,17 @@ public class Mipmap { return( 4 ); } } - + public static boolean is_index( int format ) { return( format == GL2.GL_COLOR_INDEX || format == GL2GL3.GL_STENCIL_INDEX ); } - + /* Compute memory required for internal packed array of data of given type and format. */ - + public static int image_size( int width, int height, int format, int type ) { int bytes_per_row; int components; - + assert( width > 0 ); assert( height > 0 ); components = elements_per_group( format, type ); @@ -471,17 +471,17 @@ public class Mipmap { } return( bytes_per_row * height * components ); } - + public static int imageSize3D( int width, int height, int depth, int format, int type ) { int components = elements_per_group( format, type ); int bytes_per_row = bytes_per_element( type ) * width; - + assert( width > 0 && height > 0 && depth > 0 ); assert( type != GL2.GL_BITMAP ); - + return( bytes_per_row * height * depth * components ); } - + public static void retrieveStoreModes( GL gl, PixelStorageModes psm ) { int[] a = new int[1]; gl.glGetIntegerv( GL2GL3.GL_UNPACK_ALIGNMENT, a, 0); @@ -496,7 +496,7 @@ public class Mipmap { psm.setUnpackLsbFirst( ( a[0] == 1 ) ); gl.glGetIntegerv( GL2GL3.GL_UNPACK_SWAP_BYTES, a, 0); psm.setUnpackSwapBytes( ( a[0] == 1 ) ); - + gl.glGetIntegerv( GL2GL3.GL_PACK_ALIGNMENT, a, 0); psm.setPackAlignment( a[0] ); gl.glGetIntegerv( GL2GL3.GL_PACK_ROW_LENGTH, a, 0); @@ -510,7 +510,7 @@ public class Mipmap { gl.glGetIntegerv( GL2GL3.GL_PACK_SWAP_BYTES, a, 0); psm.setPackSwapBytes( ( a[0] == 1 ) ); } - + public static void retrieveStoreModes3D( GL gl, PixelStorageModes psm ) { int[] a = new int[1]; gl.glGetIntegerv( GL2GL3.GL_UNPACK_ALIGNMENT, a, 0); @@ -529,7 +529,7 @@ public class Mipmap { psm.setUnpackSkipImages( a[0] ); gl.glGetIntegerv( GL2GL3.GL_UNPACK_IMAGE_HEIGHT, a, 0); psm.setUnpackImageHeight( a[0] ); - + gl.glGetIntegerv( GL2GL3.GL_PACK_ALIGNMENT, a, 0); psm.setPackAlignment( a[0] ); gl.glGetIntegerv( GL2GL3.GL_PACK_ROW_LENGTH, a, 0); @@ -547,9 +547,9 @@ public class Mipmap { gl.glGetIntegerv( GL2GL3.GL_PACK_IMAGE_HEIGHT, a, 0 ); psm.setPackImageHeight( a[0] ); } - - public static int gluScaleImage( GL gl, int format, int widthin, int heightin, - int typein, ByteBuffer datain, int widthout, int heightout, + + public static int gluScaleImage( GL gl, int format, int widthin, int heightin, + int typein, ByteBuffer datain, int widthout, int heightout, int typeout, ByteBuffer dataout ) { int datainPos = datain.position(); int dataoutPos = dataout.position(); @@ -559,7 +559,7 @@ public class Mipmap { ByteBuffer beforeimage; ByteBuffer afterimage; PixelStorageModes psm = new PixelStorageModes(); - + if( (widthin == 0) || (heightin == 0) || (widthout == 0) || (heightout == 0) ) { return( 0 ); } @@ -580,20 +580,20 @@ public class Mipmap { if( beforeimage == null || afterimage == null ) { return( GLU.GLU_OUT_OF_MEMORY ); } - + retrieveStoreModes( gl, psm ); Image.fill_image( psm, widthin, heightin, format, typein, is_index( format ), datain, beforeimage.asShortBuffer() ); components = elements_per_group( format, 0 ); ScaleInternal.scale_internal( components, widthin, heightin, beforeimage.asShortBuffer(), widthout, heightout, afterimage.asShortBuffer() ); Image.empty_image( psm, widthout, heightout, format, typeout, is_index( format ), afterimage.asShortBuffer(), dataout ); - + return( 0 ); } finally { datain.position(datainPos); dataout.position(dataoutPos); } } - + public static int gluBuild1DMipmapLevels( GL gl, int target, int internalFormat, int width, int format, int type, int userLevel, int baseLevel, int maxLevel, ByteBuffer data ) { @@ -601,30 +601,30 @@ public class Mipmap { try { int levels; - + int rc = checkMipmapArgs( internalFormat, format, type ); if( rc != 0 ) { return( rc ); } - + if( width < 1 ) { return( GLU.GLU_INVALID_VALUE ); } - + levels = computeLog( width ); - + levels += userLevel; if( !isLegalLevels( userLevel, baseLevel, maxLevel, levels ) ) { return( GLU.GLU_INVALID_VALUE ); } - + return( BuildMipmap.gluBuild1DMipmapLevelsCore( gl, target, internalFormat, width, width, format, type, userLevel, baseLevel, maxLevel, data ) ); } finally { data.position(dataPos); } } - + public static int gluBuild1DMipmaps( GL gl, int target, int internalFormat, int width, int format, int type, ByteBuffer data ) { int dataPos = data.position(); @@ -633,54 +633,54 @@ public class Mipmap { int[] widthPowerOf2 = new int[1]; int levels; int[] dummy = new int[1]; - + int rc = checkMipmapArgs( internalFormat, format, type ); if( rc != 0 ) { return( rc ); } - + if( width < 1 ) { return( GLU.GLU_INVALID_VALUE ); } - + closestFit( gl, target, width, 1, internalFormat, format, type, widthPowerOf2, dummy ); levels = computeLog( widthPowerOf2[0] ); - - return( BuildMipmap.gluBuild1DMipmapLevelsCore( gl, target, internalFormat, + + return( BuildMipmap.gluBuild1DMipmapLevelsCore( gl, target, internalFormat, width, widthPowerOf2[0], format, type, 0, 0, levels, data ) ); } finally { data.position(dataPos); } } - + public static int gluBuild2DMipmapLevels( GL gl, int target, int internalFormat, int width, int height, int format, int type, int userLevel, int baseLevel, int maxLevel, Object data ) { int dataPos = 0; int level, levels; - + int rc = checkMipmapArgs( internalFormat, format, type ); if( rc != 0 ) { return( rc ); } - + if( width < 1 || height < 1 ) { return( GLU.GLU_INVALID_VALUE ); } - + levels = computeLog( width ); level = computeLog( height ); if( level > levels ) { levels = level; } - + levels += userLevel; if( !isLegalLevels( userLevel, baseLevel, maxLevel, levels ) ) { return( GLU.GLU_INVALID_VALUE ); } - + //PointerWrapper pointer = PointerWrapperFactory.getPointerWrapper( data ); ByteBuffer buffer = null; if( data instanceof ByteBuffer ) { @@ -706,7 +706,7 @@ public class Mipmap { FloatBuffer fb = buffer.asFloatBuffer(); fb.put( array ); } - + try { return( BuildMipmap.gluBuild2DMipmapLevelsCore( gl, target, internalFormat, width, height, width, height, format, type, userLevel, baseLevel, @@ -716,7 +716,7 @@ public class Mipmap { } } - + public static int gluBuild2DMipmaps( GL gl, int target, int internalFormat, int width, int height, int format, int type, Object data ) { int dataPos = 0; @@ -724,25 +724,25 @@ public class Mipmap { int[] widthPowerOf2 = new int[1]; int[] heightPowerOf2 = new int[1]; int level, levels; - + int rc = checkMipmapArgs( internalFormat, format, type ); if( rc != 0 ) { return( rc ); } - + if( width < 1 || height < 1 ) { return( GLU.GLU_INVALID_VALUE ); } - - closestFit( gl, target, width, height, internalFormat, format, type, + + closestFit( gl, target, width, height, internalFormat, format, type, widthPowerOf2, heightPowerOf2 ); - + levels = computeLog( widthPowerOf2[0] ); level = computeLog( heightPowerOf2[0] ); if( level > levels ) { levels = level; } - + //PointerWrapper pointer = PointerWrapperFactory.getPointerWrapper( data ); ByteBuffer buffer = null; if( data instanceof ByteBuffer ) { @@ -768,17 +768,17 @@ public class Mipmap { FloatBuffer fb = buffer.asFloatBuffer(); fb.put( array ); } - + try { - return( BuildMipmap.gluBuild2DMipmapLevelsCore( gl, target, internalFormat, - width, height, widthPowerOf2[0], heightPowerOf2[0], format, type, 0, + return( BuildMipmap.gluBuild2DMipmapLevelsCore( gl, target, internalFormat, + width, height, widthPowerOf2[0], heightPowerOf2[0], format, type, 0, 0, levels, buffer ) ); } finally { buffer.position(dataPos); } } - - + + public static int gluBuild3DMipmaps( GL gl, int target, int internalFormat, int width, int height, int depth, int format, int type, ByteBuffer data ) { int dataPos = data.position(); @@ -788,23 +788,23 @@ public class Mipmap { int[] heightPowerOf2 = new int[1]; int[] depthPowerOf2 = new int[1]; int level, levels; - + int rc = checkMipmapArgs( internalFormat, format, type ); if( rc != 0 ) { return( rc ); } - + if( width < 1 || height < 1 || depth < 1 ) { return( GLU.GLU_INVALID_VALUE ); } - + if( type == GL2.GL_BITMAP ) { return( GLU.GLU_INVALID_ENUM ); } - + closestFit3D( gl, target, width, height, depth, internalFormat, format, type, widthPowerOf2, heightPowerOf2, depthPowerOf2 ); - + levels = computeLog( widthPowerOf2[0] ); level = computeLog( heightPowerOf2[0] ); if( level > levels ) { @@ -814,7 +814,7 @@ public class Mipmap { if( level > levels ) { levels = level; } - + return( BuildMipmap.gluBuild3DMipmapLevelsCore( gl, target, internalFormat, width, height, depth, widthPowerOf2[0], heightPowerOf2[0], depthPowerOf2[0], format, type, 0, 0, levels, data ) ); @@ -822,27 +822,27 @@ public class Mipmap { data.position(dataPos); } } - + public static int gluBuild3DMipmapLevels( GL gl, int target, int internalFormat, int width, int height, int depth, int format, int type, int userLevel, int baseLevel, int maxLevel, ByteBuffer data ) { int dataPos = data.position(); try { int level, levels; - + int rc = checkMipmapArgs( internalFormat, format, type ); if( rc != 0 ) { return( rc ); } - + if( width < 1 || height < 1 || depth < 1 ) { return( GLU.GLU_INVALID_VALUE ); } - + if( type == GL2.GL_BITMAP ) { return( GLU.GLU_INVALID_ENUM ); } - + levels = computeLog( width ); level = computeLog( height ); if( level > levels ) { @@ -852,12 +852,12 @@ public class Mipmap { if( level > levels ) { levels = level; } - + levels += userLevel; if( !isLegalLevels( userLevel, baseLevel, maxLevel, levels ) ) { return( GLU.GLU_INVALID_VALUE ); } - + return( BuildMipmap.gluBuild3DMipmapLevelsCore( gl, target, internalFormat, width, height, depth, width, height, depth, format, type, userLevel, baseLevel, maxLevel, data ) ); diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/PixelStorageModes.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/PixelStorageModes.java index 0b1af8323..7eb98db35 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/PixelStorageModes.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/PixelStorageModes.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -129,7 +129,7 @@ public class PixelStorageModes { * Holds value of property unpackImageHeight. */ private int unpackImageHeight; - + /** Creates a new instance of PixelStorageModes */ public PixelStorageModes() { } @@ -421,6 +421,6 @@ public class PixelStorageModes { this.unpackImageHeight = unpackImageHeight; } - - + + } diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/ScaleInternal.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/ScaleInternal.java index 5f086ceff..9aca1fb03 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/ScaleInternal.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/ScaleInternal.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -55,9 +55,9 @@ import com.jogamp.common.nio.Buffers; * @author Administrator */ public class ScaleInternal { - + public static final float UINT_MAX = (float)(0x00000000FFFFFFFF); - + public static void scale_internal( int components, int widthin, int heightin, ShortBuffer datain, int widthout, int heightout, ShortBuffer dataout ) { float x, lowx, highx, convx, halfconvx; @@ -69,7 +69,7 @@ public class ScaleInternal { float area; int i, j, k, yint, xint, xindex, yindex; int temp; - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { HalveImage.halveImage( components, widthin, heightin, datain, dataout ); return; @@ -101,7 +101,7 @@ public class ScaleInternal { // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; area = 0.0f; - + y = lowy; yint = (int)Math.floor( y ); while( y < highy ) { @@ -111,10 +111,10 @@ public class ScaleInternal { } else { ypercent = yint + 1 - y; } - + x = lowx; xint = (int)Math.floor( x ); - + while( x < highx ) { xindex = ( xint + widthin ) % widthin; if( highx < xint + 1 ) { @@ -122,21 +122,21 @@ public class ScaleInternal { } else { xpercent = xint + 1 - x; } - + percent = xpercent * ypercent; area += percent; temp = ( xindex + ( yindex * widthin) ) * components; for( k = 0; k < components; k++ ) { - totals[k] += datain.get( temp + k ) * percent; + totals[k] += datain.get( temp + k ) * percent; } - + xint++; x = xint; } yint++; y = yint; } - + temp = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { // totals[] should be rounded in the case of enlarging an RGB @@ -146,9 +146,9 @@ public class ScaleInternal { } } } - + public static void scale_internal_ubyte( int components, int widthin, int heightin, - ByteBuffer datain, int widthout, int heightout, + ByteBuffer datain, int widthout, int heightout, ByteBuffer dataout, int element_size, int ysize, int group_size ) { float x, convx; float y, convy; @@ -157,11 +157,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -169,9 +169,9 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_ubyte( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_ubyte( components, widthin, heightin, datain, dataout, element_size, ysize, group_size ); return; } @@ -181,35 +181,42 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -232,7 +239,7 @@ public class ScaleInternal { datain.position( temp_index ); totals[k] += ( 0x000000FF & datain.get() ) * percent; } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -254,7 +261,7 @@ public class ScaleInternal { datain.position( temp_index ); totals[k] += ( 0x000000FF & datain.get() ) * percent; } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -332,7 +339,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { dataout.position( outindex + k ); @@ -364,9 +371,9 @@ public class ScaleInternal { } } } - + public static void scale_internal_byte( int components, int widthin, int heightin, - ByteBuffer datain, int widthout, int heightout, + ByteBuffer datain, int widthout, int heightout, ByteBuffer dataout, int element_size, int ysize, int group_size ) { float x, convx; @@ -376,11 +383,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -388,9 +395,9 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_byte( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_byte( components, widthin, heightin, datain, dataout, element_size, ysize, group_size ); return; } @@ -400,34 +407,41 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -450,7 +464,7 @@ public class ScaleInternal { datain.position( temp_index ); totals[k] += datain.get() * percent; } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -472,7 +486,7 @@ public class ScaleInternal { datain.position( temp_index ); totals[k] += datain.get() * percent; } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -536,7 +550,7 @@ public class ScaleInternal { totals[k] += datain.get() * percent; } } - + // this is for the pixels in the body temp0 = xindex + group_size + ( lowy_int + 1 ) * ysize; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -550,7 +564,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { dataout.position( outindex + k ); @@ -582,10 +596,10 @@ public class ScaleInternal { } } } - + public static void scale_internal_ushort( int components, int widthin, int heightin, - ByteBuffer datain, int widthout, int heightout, - ShortBuffer dataout, int element_size, int ysize, + ByteBuffer datain, int widthout, int heightout, + ShortBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { float x, convx; float y, convy; @@ -594,11 +608,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -606,9 +620,9 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_ushort( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_ushort( components, widthin, heightin, datain, dataout, element_size, ysize, group_size, myswap_bytes ); return; } @@ -618,34 +632,41 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -680,7 +701,7 @@ public class ScaleInternal { totals[k] += ( 0x0000FFFF & datain.getShort()) * percent; } } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -714,7 +735,7 @@ public class ScaleInternal { totals[k] += ( 0x0000FFFF & datain.getShort()) * percent; } } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -813,7 +834,7 @@ public class ScaleInternal { } } } - + // this is for the pixels in the body temp0 = xindex + group_size + ( lowy_int + 1 ) * ysize; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -831,7 +852,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { dataout.position( outindex + k ); @@ -863,10 +884,10 @@ public class ScaleInternal { } } } - + public static void scale_internal_short( int components, int widthin, int heightin, ByteBuffer datain, int widthout, int heightout, - ShortBuffer dataout, int element_size, int ysize, + ShortBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { float x, convx; float y, convy; @@ -875,11 +896,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -887,11 +908,11 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + int swapbuf; // unsigned buffer - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_short( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_short( components, widthin, heightin, datain, dataout, element_size, ysize, group_size, myswap_bytes ); return; } @@ -901,34 +922,41 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -966,7 +994,7 @@ public class ScaleInternal { totals[k] += datain.getShort() * percent; } } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -1003,7 +1031,7 @@ public class ScaleInternal { totals[k] += datain.getShort() * percent; } } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -1109,7 +1137,7 @@ public class ScaleInternal { } } } - + // this is for the pixels in the body temp0 = xindex + group_size + ( lowy_int + 1 ) * ysize; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -1128,7 +1156,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { dataout.position( outindex + k ); @@ -1160,10 +1188,10 @@ public class ScaleInternal { } } } - + public static void scale_internal_uint( int components, int widthin, int heightin, - ByteBuffer datain, int widthout, int heightout, - IntBuffer dataout, int element_size, int ysize, + ByteBuffer datain, int widthout, int heightout, + IntBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { float x, convx; float y, convy; @@ -1172,11 +1200,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -1184,9 +1212,9 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_uint( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_uint( components, widthin, heightin, datain, dataout, element_size, ysize, group_size, myswap_bytes ); return; } @@ -1196,34 +1224,41 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -1258,7 +1293,7 @@ public class ScaleInternal { totals[k] += (0x00000000FFFFFFFF & datain.getInt()) * percent; } } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -1292,7 +1327,7 @@ public class ScaleInternal { totals[k] += (0x00000000FFFFFFFF & datain.getInt()) * percent; } } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -1392,7 +1427,7 @@ public class ScaleInternal { } } } - + // this is for the pixels in the body temp0 = xindex + group_size + ( lowy_int + 1 ) * ysize; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -1410,7 +1445,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; float value = 0.0f; for( k = 0; k < components; k++ ) { @@ -1448,10 +1483,10 @@ public class ScaleInternal { } } } - + public static void scale_internal_int( int components, int widthin, int heightin, - ByteBuffer datain, int widthout, int heightout, - IntBuffer dataout, int element_size, int ysize, + ByteBuffer datain, int widthout, int heightout, + IntBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { float x, convx; float y, convy; @@ -1460,11 +1495,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -1472,11 +1507,11 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + long swapbuf; // unsigned buffer - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_int( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_int( components, widthin, heightin, datain, dataout, element_size, ysize, group_size, myswap_bytes ); return; } @@ -1486,34 +1521,41 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -1551,7 +1593,7 @@ public class ScaleInternal { totals[k] += datain.getInt() * percent; } } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -1588,7 +1630,7 @@ public class ScaleInternal { totals[k] += datain.getInt() * percent; } } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -1694,7 +1736,7 @@ public class ScaleInternal { } } } - + // this is for the pixels in the body temp0 = xindex + group_size + ( lowy_int + 1 ) * ysize; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -1713,7 +1755,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { dataout.position( outindex + k ); @@ -1745,10 +1787,10 @@ public class ScaleInternal { } } } - + public static void scale_internal_float( int components, int widthin, int heightin, - ByteBuffer datain, int widthout, int heightout, - FloatBuffer dataout, int element_size, int ysize, + ByteBuffer datain, int widthout, int heightout, + FloatBuffer dataout, int element_size, int ysize, int group_size, boolean myswap_bytes ) { float x, convx; float y, convy; @@ -1757,11 +1799,11 @@ public class ScaleInternal { float[] totals = new float[4]; float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outindex; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -1769,11 +1811,11 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + float swapbuf; // unsigned buffer - + if( (widthin == (widthout * 2)) && (heightin == (heightout * 2)) ) { - HalveImage.halveImage_float( components, widthin, heightin, datain, dataout, + HalveImage.halveImage_float( components, widthin, heightin, datain, dataout, element_size, ysize, group_size, myswap_bytes ); return; } @@ -1783,34 +1825,41 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convy_float; - + for( i = 0; i < heightout; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightin) highy_int = heightin - 1; lowx_int = 0; lowx_float = 0.0f; - highx_int = convx_int; - highx_float = convx_float; - + // If we have a single column, fix the max width values + // to prevent buffer overflow + if (widthin == 1 && widthout == 1) { + highx_int = 0; + highx_float = 0.0f; + } else { + highx_int = convx_int; + highx_float = convx_float; + } + for( j = 0; j < widthout; j++ ) { - + // Ok, now apply box filter to box that goes from (lowx, lowy) // to (highx, highy) on input data into this pixel on output // data. totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // caulate the value for pixels in the 1st row xindex = lowx_int * group_size; if( ( highy_int > lowy_int ) && ( highx_int > lowx_int ) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * ysize; percent = y_percent * ( 1 - lowx_float ); @@ -1848,7 +1897,7 @@ public class ScaleInternal { totals[k] += datain.getFloat() * percent; } } - + // calculate the value for pixels in the last row y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); @@ -1885,7 +1934,7 @@ public class ScaleInternal { totals[k] += datain.getFloat() * percent; } } - + // calculate the value for the pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += ysize; @@ -1991,7 +2040,7 @@ public class ScaleInternal { } } } - + // this is for the pixels in the body temp0 = xindex + group_size + ( lowy_int + 1 ) * ysize; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -2010,7 +2059,7 @@ public class ScaleInternal { } temp0 += ysize; } - + outindex = ( j + ( i * widthout ) ) * components; for( k = 0; k < components; k++ ) { dataout.position( outindex + k ); @@ -2042,28 +2091,28 @@ public class ScaleInternal { } } } - - public static void scaleInternalPackedPixel( int components, Extract extract, + + public static void scaleInternalPackedPixel( int components, Extract extract, int widthIn, int heightIn, ByteBuffer dataIn, int widthOut, int heightOut, ByteBuffer dataOut, int pixelSizeInBytes, int rowSizeInBytes, boolean isSwap ) { float x, convx; float y, convy; float percent; - + // max components in a format is 4, so float[] totals = new float[4]; float[] extractTotals = new float[4]; float[] extractMoreTotals = new float[4]; float[] shoveTotals = new float[4]; - + float area; int i, j, k, xindex; - + int temp, temp0; int temp_index; int outIndex = 0; - + int lowx_int, highx_int, lowy_int, highy_int; float x_percent, y_percent; float lowx_float, highx_float, lowy_float, highy_float; @@ -2071,7 +2120,7 @@ public class ScaleInternal { int convy_int, convx_int; int l, m; int left, right; - + if( widthIn == widthOut * 2 && heightIn == heightOut * 2 ) { HalveImage.halveImagePackedPixel( components, extract, widthIn, heightIn, dataIn, dataOut, pixelSizeInBytes, rowSizeInBytes, isSwap ); @@ -2083,14 +2132,14 @@ public class ScaleInternal { convy_float = convy - convy_int; convx_int = (int)Math.floor( convx ); convx_float = convx - convx_int; - + area = convx * convy; - + lowy_int = 0; lowy_float = 0.0f; highy_int = convy_int; highy_float = convx_float; - + for( i = 0; i < heightOut; i++ ) { // Clamp here to be sure we don't read beyond input buffer. if (highy_int >= heightIn) @@ -2099,16 +2148,16 @@ public class ScaleInternal { lowx_float = 0.0f; highx_int = convx_int; highx_float = convx_float; - + for( j = 0; j < widthOut; j++ ) { // ok now apply box filter to box that goes from( lowx, lowy ) // to ( highx, highy ) on input data into this pixel on output data totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; - + // calculate that value for pixels in the 1st row xindex = lowx_int * pixelSizeInBytes; if( (highy_int > lowy_int) && (highx_int > lowx_int) ) { - + y_percent = 1 - lowy_float; temp = xindex + lowy_int * rowSizeInBytes; percent = y_percent * ( 1 - lowx_float ); @@ -2135,7 +2184,7 @@ public class ScaleInternal { totals[k] += extractTotals[k] * percent; } // calculate the value for pixels in the last row - + y_percent = highy_float; percent = y_percent * ( 1 - lowx_float ); temp = xindex + highy_int * rowSizeInBytes; @@ -2158,7 +2207,7 @@ public class ScaleInternal { for( k = 0; k < components; k++ ) { totals[k] += extractTotals[k] * percent; } - + // calculate the value for pixels in the 1st and last column for( m = lowy_int + 1; m < highy_int; m++ ) { left += rowSizeInBytes; @@ -2228,7 +2277,7 @@ public class ScaleInternal { totals[k] += extractTotals[k] * percent; } } - + // this is for the pixels in the body temp0 = xindex + pixelSizeInBytes + ( lowy_int + 1 ) * rowSizeInBytes; for( m = lowy_int + 1; m < highy_int; m++ ) { @@ -2243,7 +2292,7 @@ public class ScaleInternal { } temp0 += rowSizeInBytes; } - + outIndex = ( j + ( i * widthOut ) ); for( k = 0; k < components; k++ ) { shoveTotals[k] = totals[k] / area; @@ -2276,7 +2325,7 @@ public class ScaleInternal { } assert( outIndex == ( widthOut * heightOut - 1) ); } - + public static void scaleInternal3D( int components, int widthIn, int heightIn, int depthIn, ShortBuffer dataIn, int widthOut, int heightOut, int depthOut, ShortBuffer dataOut ) { @@ -2290,9 +2339,9 @@ public class ScaleInternal { float volume; int i, j, d, k, zint, yint, xint, xindex, yindex, zindex; int temp; - + lowy = highy = lowx = highx = 0.0f; - + convz = (float)depthIn / depthOut; convy = (float)heightIn / heightOut; convx = (float)widthIn / widthOut; @@ -2326,13 +2375,13 @@ public class ScaleInternal { highz = x + 0.5f; lowz = x - 0.5f; } - + // Ok, now apply box filter to box that goes from ( lowx, lowy, lowz ) // to ( highx, highy, highz ) on input data into this pixel on output data - + totals[0] = totals[1] = totals[2] = totals[3] = 0.0f; volume = 0.0f; - + z = lowz; zint = (int)(Math.floor( z ) ); while( z < highz ) { @@ -2342,7 +2391,7 @@ public class ScaleInternal { } else { zpercent = zint + 1 - z; } - + y = lowy; yint = (int)(Math.floor( y ) ); while( y < highy ) { @@ -2352,10 +2401,10 @@ public class ScaleInternal { } else { ypercent = yint + 1 - y; } - + x = lowx; xint = (int)(Math.floor( x ) ); - + while( x < highx ) { xindex = (xint + widthIn ) % widthIn; if( highx < xint + 1 ) { @@ -2363,10 +2412,10 @@ public class ScaleInternal { } else { xpercent = xint + 1 - x; } - + percent = xpercent * ypercent * zpercent; volume += percent; - + temp = (xindex + ( yindex *widthIn) + (zindex * widthIn *heightIn)) * components; for( k = 0; k < components; k++ ) { assert( 0 <= (temp+k) && (temp+k) < (widthIn * heightIn * depthIn * components) ); @@ -2381,7 +2430,7 @@ public class ScaleInternal { zint++; z = zint; } // while z - + temp = ( j + ( i * widthOut ) + (d * widthOut * heightOut ) ) * components; for( k = 0; k < components; k++ ) { // totals should be rounded in the case of enlarging an rgb ramp when the type is 332 or 4444 @@ -2392,48 +2441,48 @@ public class ScaleInternal { } } } - - public static int gluScaleImage3D( GL gl, int format, int widthIn, int heightIn, - int depthIn, int typeIn, ByteBuffer dataIn, int widthOut, int heightOut, + + public static int gluScaleImage3D( GL gl, int format, int widthIn, int heightIn, + int depthIn, int typeIn, ByteBuffer dataIn, int widthOut, int heightOut, int depthOut, int typeOut, ByteBuffer dataOut ) { int components; ShortBuffer beforeImage, afterImage; PixelStorageModes psm = new PixelStorageModes(); - + if( widthIn == 0 || heightIn == 0 || depthIn == 0 || widthOut == 0 || heightOut == 0 || depthOut == 0 ) { return( 0 ); } - + if( widthIn < 0 || heightIn < 0 || depthIn < 0 || widthOut < 0 || heightOut < 0 || depthOut < 0 ) { return( GLU.GLU_INVALID_VALUE ); } - - if( !Mipmap.legalFormat(format) || !Mipmap.legalType(typeIn) || + + if( !Mipmap.legalFormat(format) || !Mipmap.legalType(typeIn) || !Mipmap.legalType(typeOut) || typeIn == GL2.GL_BITMAP || typeOut == GL2.GL_BITMAP ) { return( GLU.GLU_INVALID_ENUM ); } - + if( !Mipmap.isLegalFormatForPackedPixelType( format, typeIn ) ) { return( GLU.GLU_INVALID_OPERATION ); } - + if( !Mipmap.isLegalFormatForPackedPixelType( format, typeOut ) ) { return( GLU.GLU_INVALID_OPERATION ); } - + try { - beforeImage = Buffers.newDirectByteBuffer( Mipmap.imageSize3D( widthIn, + beforeImage = Buffers.newDirectByteBuffer( Mipmap.imageSize3D( widthIn, heightIn, depthIn, format, GL2.GL_UNSIGNED_SHORT ) ).asShortBuffer(); - afterImage = Buffers.newDirectByteBuffer( Mipmap.imageSize3D( widthIn, + afterImage = Buffers.newDirectByteBuffer( Mipmap.imageSize3D( widthIn, heightIn, depthIn, format, GL2.GL_UNSIGNED_SHORT ) ).asShortBuffer(); } catch( OutOfMemoryError err ) { return( GLU.GLU_OUT_OF_MEMORY ); } Mipmap.retrieveStoreModes3D( gl, psm ); - + Image.fillImage3D( psm, widthIn, heightIn, depthIn, format, typeIn, Mipmap.is_index( format ), dataIn, beforeImage ); components = Mipmap.elements_per_group( format, 0 ); @@ -2441,7 +2490,7 @@ public class ScaleInternal { beforeImage, widthOut, heightOut, depthOut, afterImage ); Image.emptyImage3D( psm, widthOut, heightOut, depthOut, format, typeOut, Mipmap.is_index( format ), afterImage, dataOut ); - + return( 0 ); } } diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Type_Widget.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Type_Widget.java index 38113f601..dc401880d 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Type_Widget.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Type_Widget.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,176 +51,176 @@ import java.nio.*; * @author Administrator */ public class Type_Widget { - + ByteBuffer buffer; - + /** Creates a new instance of Type_Widget */ public Type_Widget() { // can't make this direct, because JVM doesn't allocate small direct buffers efficiently // see https://jogamp.org/bugzilla/show_bug.cgi?id=463 for details buffer = ByteBuffer.allocate( 4 ); } - + public void setUB0( byte b ) { buffer.position( 0 ); buffer.put( b ); } - + public byte getUB0() { buffer.position( 0 ); return( buffer.get() ); } - + public void setUB1( byte b ) { buffer.position( 1 ); buffer.put( b ); } - + public byte getUB1() { buffer.position( 1 ); return( buffer.get() ); } - + public void setUB2( byte b ) { buffer.position( 2 ); buffer.put( b ); } - + public byte getUB2() { buffer.position( 2 ); return( buffer.get() ); } - + public void setUB3( byte b ) { buffer.position( 3 ); buffer.put( b ); } - + public byte getUB3() { buffer.position( 3 ); return( buffer.get() ); } - + public void setUS0( short s ) { buffer.position( 0 ); buffer.putShort( s ); } - + public short getUS0() { buffer.position( 0 ); return( buffer.getShort() ); } - + public void setUS1( short s ) { buffer.position( 2 ); buffer.putShort( s ); } - + public short getUS1() { buffer.position( 2 ); return( buffer.getShort() ); } - + public void setUI( int i ) { buffer.position( 0 ); buffer.putInt( i ); } - + public int getUI() { buffer.position( 0 ); return( buffer.getInt() ); } - + public void setB0( byte b ) { buffer.position( 0 ); buffer.put( b ); } - + public byte getB0() { buffer.position( 0 ); return( buffer.get() ); } - + public void setB1( byte b ) { buffer.position( 1 ); buffer.put( b ); } - + public byte getB1() { buffer.position( 1 ); return( buffer.get() ); } - + public void setB2( byte b ) { buffer.position( 2 ); buffer.put( b ); } - + public byte getB2() { buffer.position( 2 ); return( buffer.get() ); } - + public void setB3( byte b ) { buffer.position( 3 ); buffer.put( b ); } - + public byte getB3() { buffer.position( 3 ); return( buffer.get() ); } - + public void setS0( short s ) { buffer.position( 0 ); buffer.putShort( s ); } - + public short getS0() { buffer.position( 0 ); return( buffer.getShort() ); } - + public void setS1( short s ) { buffer.position( 2 ); buffer.putShort( s ); } - + public short getS1() { buffer.position( 2 ); return( buffer.getShort() ); } - + public void setI( int i ) { buffer.position( 0 ); buffer.putInt( i ); } - + public int getI() { buffer.position( 0 ); return( buffer.getInt() ); } - + public void setF( float f ) { buffer.position( 0 ); buffer.putFloat( f ); } - + public float getF() { buffer.position( 0 ); return( buffer.getFloat() ); } - + public ByteBuffer getBuffer() { buffer.rewind(); return( buffer ); } - + public static void main( String args[] ) { Type_Widget t = new Type_Widget(); t.setI( 1000000 ); - + System.out.println("int: " + Integer.toHexString( t.getI() ) ); - + } } diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Arc.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Arc.java index 422f8d4df..df2b9a147 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Arc.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Arc.java @@ -72,7 +72,7 @@ public class Arc { /** * Makes new arc at specified side - * + * * @param side * which side doeas this arc form */ @@ -86,7 +86,7 @@ public class Arc { /** * Sets side the arc is at - * + * * @param side * arc side */ @@ -104,7 +104,7 @@ public class Arc { type &= ~(0x7 << 8); } - // this one replaces enum arc_side + // this one replaces enum arc_side /** * Side not specified */ @@ -147,7 +147,7 @@ public class Arc { /** * Appends arc to the list - * + * * @param jarc * arc to be append * @return this @@ -169,7 +169,7 @@ public class Arc { /** * Unused - * + * * @return true */ public boolean check() { @@ -187,7 +187,7 @@ public class Arc { /** * Returns tail of linked list coords - * + * * @return tail coords */ public float[] tail() { @@ -197,7 +197,7 @@ public class Arc { /** * Returns head of linked list coords - * + * * @return head coords */ public float[] head() { @@ -207,7 +207,7 @@ public class Arc { /** * Returns whether arc is marked with arc_tag - * + * * @return is arc marked with arc_tag */ public boolean ismarked() { @@ -241,7 +241,7 @@ public class Arc { /** * Returns whether arc is marked tail - * + * * @return is tail */ public boolean getitail() { diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcSdirSorter.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcSdirSorter.java index f4ad70193..c299b10af 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcSdirSorter.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcSdirSorter.java @@ -44,7 +44,7 @@ public class ArcSdirSorter { /** * Makes new ArcSdirSorter with Subdivider * @param subdivider subdivider - */ + */ public ArcSdirSorter(Subdivider subdivider) { //TODO // System.out.println("TODO arcsdirsorter.constructor"); diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcTdirSorter.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcTdirSorter.java index be72c53d2..1a584c396 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcTdirSorter.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/ArcTdirSorter.java @@ -43,7 +43,7 @@ public class ArcTdirSorter { /** * Makes new ArcSdirSorter with Subdivider * @param subdivider subdivider - */ + */ public ArcTdirSorter(Subdivider subdivider) { // TODO Auto-generated constructor stub // System.out.println("TODO arcTsorter.konstruktor"); diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Backend.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Backend.java index 610a19556..3e974247b 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Backend.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Backend.java @@ -67,7 +67,7 @@ public abstract class Backend { protected SurfaceEvaluator surfaceEvaluator; /** - * Makes new backend + * Makes new backend */ public Backend() { // curveEvaluator = new OpenGLCurveEvaluator(); diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Bin.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Bin.java index df8b16ab5..17437ef01 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Bin.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Bin.java @@ -143,7 +143,7 @@ public class Bin { /** * Returns next arc in linked list * @return next arc - * + * */ private Arc nextarc() { // DONE diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Breakpt.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Breakpt.java index f45571dac..f84640d28 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Breakpt.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Breakpt.java @@ -36,9 +36,9 @@ package jogamp.opengl.glu.nurbs; /** * Class holding break point parameters - * + * * @author Tomas Hrasky - * + * */ public class Breakpt { diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfArcs.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfArcs.java index aaa8cb5f2..b67764c30 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfArcs.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfArcs.java @@ -2,9 +2,9 @@ package jogamp.opengl.glu.nurbs; /** * Class replacing C language pointer - * + * * @author Tomas Hrasky - * + * */ public class CArrayOfArcs { /** @@ -24,7 +24,7 @@ public class CArrayOfArcs { /** * Makes new CArray - * + * * @param array * underlaying array * @param pointer @@ -38,7 +38,7 @@ public class CArrayOfArcs { /** * Makes new CArray from other CArray - * + * * @param carray * reference array */ @@ -50,7 +50,7 @@ public class CArrayOfArcs { /** * Makes new CArray with pointer set to 0 - * + * * @param ctlarray * underlaying array */ @@ -61,7 +61,7 @@ public class CArrayOfArcs { /** * Returns element at pointer - * + * * @return element at pointer */ public Arc get() { @@ -78,7 +78,7 @@ public class CArrayOfArcs { /** * Sets element at pointer - * + * * @param f * desired value */ @@ -89,7 +89,7 @@ public class CArrayOfArcs { /** * Returns array element at specified index - * + * * @param i * array index * @return element at index @@ -100,7 +100,7 @@ public class CArrayOfArcs { /** * Returns array element at specified index relatively to pointer - * + * * @param i * relative index * @return element at relative index @@ -111,7 +111,7 @@ public class CArrayOfArcs { /** * Sets value of element at specified index relatively to pointer - * + * * @param i * relative index * @param value @@ -123,7 +123,7 @@ public class CArrayOfArcs { /** * Lessens pointer by value - * + * * @param i * lessen by */ @@ -134,7 +134,7 @@ public class CArrayOfArcs { /** * Returns pointer value - * + * * @return pointer value */ public int getPointer() { @@ -143,7 +143,7 @@ public class CArrayOfArcs { /** * Sets ponter value - * + * * @param pointer * pointer value to be set */ @@ -156,7 +156,7 @@ public class CArrayOfArcs { /** * Raises pointer by value - * + * * @param i * raise by */ @@ -175,7 +175,7 @@ public class CArrayOfArcs { /** * Returns underlaying array - * + * * @return underlaying array */ public Arc[] getArray() { @@ -184,7 +184,7 @@ public class CArrayOfArcs { /** * Sets underlaying array - * + * * @param array * underlaying array */ diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfBreakpts.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfBreakpts.java index 5112b07fc..b5f588960 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfBreakpts.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfBreakpts.java @@ -2,9 +2,9 @@ package jogamp.opengl.glu.nurbs; /** * Class replacing C language pointer - * + * * @author Tomas Hrasky - * + * */ public class CArrayOfBreakpts { /** @@ -19,7 +19,7 @@ public class CArrayOfBreakpts { /** * Makes new CArray - * + * * @param array * underlaying array * @param pointer @@ -32,7 +32,7 @@ public class CArrayOfBreakpts { /** * Makes new CArray from other CArray - * + * * @param carray * reference array */ @@ -43,7 +43,7 @@ public class CArrayOfBreakpts { /** * Returns element at pointer - * + * * @return element at pointer */ public Breakpt get() { @@ -59,7 +59,7 @@ public class CArrayOfBreakpts { /** * Sets element at pointer - * + * * @param f * desired value */ @@ -70,7 +70,7 @@ public class CArrayOfBreakpts { /** * Returns array element at specified index - * + * * @param i * array index * @return element at index @@ -81,7 +81,7 @@ public class CArrayOfBreakpts { /** * Lessens pointer by value - * + * * @param i * lessen by */ @@ -92,7 +92,7 @@ public class CArrayOfBreakpts { /** * Returns pointer value - * + * * @return pointer value */ public int getPointer() { @@ -101,7 +101,7 @@ public class CArrayOfBreakpts { /** * Sets ponter value - * + * * @param pointer * pointer value to be set */ @@ -111,7 +111,7 @@ public class CArrayOfBreakpts { /** * Raises pointer by value - * + * * @param i * raise by */ diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfFloats.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfFloats.java index 39ef841ec..d9e4d0ff1 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfFloats.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfFloats.java @@ -2,9 +2,9 @@ package jogamp.opengl.glu.nurbs; /** * Class replacing C language pointer - * + * * @author Tomas Hrasky - * + * */ public class CArrayOfFloats { @@ -25,7 +25,7 @@ public class CArrayOfFloats { /** * Makes new CArray - * + * * @param array * underlaying array * @param pointer @@ -39,7 +39,7 @@ public class CArrayOfFloats { /** * Makes new CArray from other CArray - * + * * @param carray * reference array */ @@ -51,7 +51,7 @@ public class CArrayOfFloats { /** * Makes new CArray with pointer set to 0 - * + * * @param ctlarray * underlaying array */ @@ -62,7 +62,7 @@ public class CArrayOfFloats { /** * Returns element at pointer - * + * * @return element at pointer */ public float get() { @@ -79,7 +79,7 @@ public class CArrayOfFloats { /** * Sets element at pointer - * + * * @param f * desired value */ @@ -90,7 +90,7 @@ public class CArrayOfFloats { /** * Returns array element at specified index - * + * * @param i * array index * @return element at index @@ -101,7 +101,7 @@ public class CArrayOfFloats { /** * Returns array element at specified index relatively to pointer - * + * * @param i * relative index * @return element at relative index @@ -112,7 +112,7 @@ public class CArrayOfFloats { /** * Sets value of element at specified index relatively to pointer - * + * * @param i * relative index * @param value @@ -124,7 +124,7 @@ public class CArrayOfFloats { /** * Lessens pointer by value - * + * * @param i * lessen by */ @@ -135,7 +135,7 @@ public class CArrayOfFloats { /** * Returns pointer value - * + * * @return pointer value */ public int getPointer() { @@ -144,7 +144,7 @@ public class CArrayOfFloats { /** * Sets ponter value - * + * * @param pointer * pointer value to be set */ @@ -157,7 +157,7 @@ public class CArrayOfFloats { /** * Raises pointer by value - * + * * @param i * raise by */ @@ -176,7 +176,7 @@ public class CArrayOfFloats { /** * Returns underlaying array - * + * * @return underlaying array */ public float[] getArray() { @@ -185,7 +185,7 @@ public class CArrayOfFloats { /** * Sets underlaying array - * + * * @param array * underlaying array */ diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfQuiltspecs.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfQuiltspecs.java index 4b21f2d50..e7bbac16a 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfQuiltspecs.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/CArrayOfQuiltspecs.java @@ -2,9 +2,9 @@ package jogamp.opengl.glu.nurbs; /** * Class replacing C language pointer - * + * * @author Tomas Hrasky - * + * */ public class CArrayOfQuiltspecs { /** @@ -19,7 +19,7 @@ public class CArrayOfQuiltspecs { /** * Makes new CArray - * + * * @param array * underlaying array * @param pointer @@ -32,7 +32,7 @@ public class CArrayOfQuiltspecs { /** * Makes new CArray from other CArray - * + * * @param carray * reference array */ @@ -43,7 +43,7 @@ public class CArrayOfQuiltspecs { /** * Makes new CArray with pointer set to 0 - * + * * @param array * underlaying array */ @@ -54,7 +54,7 @@ public class CArrayOfQuiltspecs { /** * Returns element at pointer - * + * * @return element at pointer */ public Quiltspec get() { @@ -70,7 +70,7 @@ public class CArrayOfQuiltspecs { /** * Sets element at pointer - * + * * @param f * desired value */ @@ -81,7 +81,7 @@ public class CArrayOfQuiltspecs { /** * Returns array element at specified index - * + * * @param i * array index * @return element at index @@ -92,7 +92,7 @@ public class CArrayOfQuiltspecs { /** * Lessens pointer by value - * + * * @param i * lessen by */ @@ -103,7 +103,7 @@ public class CArrayOfQuiltspecs { /** * Returns pointer value - * + * * @return pointer value */ public int getPointer() { @@ -112,7 +112,7 @@ public class CArrayOfQuiltspecs { /** * Sets ponter value - * + * * @param pointer * pointer value to be set */ @@ -122,7 +122,7 @@ public class CArrayOfQuiltspecs { /** * Raises pointer by value - * + * * @param i * raise by */ @@ -141,7 +141,7 @@ public class CArrayOfQuiltspecs { /** * Returns underlaying array - * + * * @return underlaying array */ public Quiltspec[] getArray() { @@ -150,7 +150,7 @@ public class CArrayOfQuiltspecs { /** * Sets underlaying array - * + * * @param array * underlaying array */ diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Curve.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Curve.java index 786781723..ea3a3d14e 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Curve.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Curve.java @@ -103,7 +103,7 @@ public class Curve { /** * Makes new Curve - * + * * @param geo * @param pta * @param ptb @@ -143,7 +143,7 @@ public class Curve { range[0] = qs.get().breakpoints[qs.get().index]; range[1] = qs.get().breakpoints[qs.get().index + 1]; range[2] = range[1] - range[0]; - // TODO it is necessary to solve problem with "this" pointer here + // TODO it is necessary to solve problem with "this" pointer here if (range[0] != pta[0]) { // System.out.println("TODO curve.Curve-range0"); // Curve lower=new Curve(this,pta,0); @@ -229,7 +229,7 @@ public class Curve { /** * Tells whether curve needs subdivision - * + * * @return curve needs subdivison */ public boolean needsSamplingSubdivision() { diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Flist.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Flist.java index 6983691d9..f9c4c2d6f 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Flist.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Flist.java @@ -45,12 +45,12 @@ public class Flist { /** * Data elements end index - * + * */ public int end; /** - *Data elements start index + *Data elements start index */ public int start; @@ -80,7 +80,7 @@ public class Flist { } /** - * Removes duplicate array elemnts + * Removes duplicate array elemnts */ public void filter() { // INFO the aim of this method is to remove duplicates from array diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotspec.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotspec.java index 114832a1c..1dcf393a9 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotspec.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotspec.java @@ -36,9 +36,9 @@ package jogamp.opengl.glu.nurbs; /** * Knot vector specification - * + * * @author Tomas Hrasky - * + * */ public class Knotspec { @@ -314,7 +314,7 @@ public class Knotspec { /** * Copies control points - * + * * @param _inpt * input control points * @param _outpt @@ -346,7 +346,7 @@ public class Knotspec { /** * Copies one control point to other - * + * * @param topt * source control point * @param frompt @@ -374,7 +374,7 @@ public class Knotspec { /** * Inserts a knot - * + * * @param _p * inserted knot */ @@ -402,7 +402,7 @@ public class Knotspec { } } - } else {//code for curve + } else {//code for curve if (this.equals(kspectotrans)) { insert(p); } else { @@ -428,7 +428,7 @@ public class Knotspec { /** * Inserts a knot and computes new control points - * + * * @param p * inserted knot */ @@ -490,7 +490,7 @@ public class Knotspec { /** * Copies one control point to another - * + * * @param topt * source ctrl point * @param frompt @@ -519,7 +519,7 @@ public class Knotspec { /** * Computes new control point - * + * * @param x * first point * @param y @@ -549,7 +549,7 @@ public class Knotspec { * z.getRelative(0))); break; default: - //no need of default - see previous method and its case statement + //no need of default - see previous method and its case statement // System.out.println("TODO pt_oo_sum default"); break; } diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotvector.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotvector.java index aac4dfc52..571f44f06 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotvector.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Knotvector.java @@ -36,9 +36,9 @@ package jogamp.opengl.glu.nurbs; /** * Knot vector used in curve specification - * + * * @author Tomas Hrasky - * + * */ public class Knotvector { @@ -75,7 +75,7 @@ public class Knotvector { /** * Makes new knotvector - * + * * @param nknots * number of knots * @param stride @@ -92,7 +92,7 @@ public class Knotvector { /** * Initializes knotvector - * + * * @param nknots * number of knots * @param stride @@ -116,7 +116,7 @@ public class Knotvector { /** * Validates knot vector parameters - * + * * @return knot vector validity */ public int validate() { @@ -154,7 +154,7 @@ public class Knotvector { /** * Show specified message - * + * * @param msg * message to be shown */ @@ -166,7 +166,7 @@ public class Knotvector { /** * Compares two knots for equality - * + * * @param a * first knot * @param b diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Mapdesc.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Mapdesc.java index bd5d2db98..86638a827 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Mapdesc.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Mapdesc.java @@ -112,7 +112,7 @@ public class Mapdesc { float clampfactor; /** - * Value of N_MINSAVINGS property + * Value of N_MINSAVINGS property */ private float minsavings; @@ -162,7 +162,7 @@ public class Mapdesc { private float[] bboxsize; /** - * Makes new mapdesc + * Makes new mapdesc * @param type map type * @param rational is rational * @param ncoords number of control points coords @@ -318,7 +318,7 @@ public class Mapdesc { } /** - * Tells whether map is culling + * Tells whether map is culling * @return is map culling */ public boolean isCulling() { @@ -327,7 +327,7 @@ public class Mapdesc { } /** - * Tells whether map is constantly sampling + * Tells whether map is constantly sampling * @return is map constant sampling */ public boolean isConstantSampling() { @@ -335,7 +335,7 @@ public class Mapdesc { } /** - * Tells whether map is domain sampling + * Tells whether map is domain sampling * @return is map domain sampling */ public boolean isDomainSampling() { @@ -343,7 +343,7 @@ public class Mapdesc { } /** - * Returns property of specified tag value + * Returns property of specified tag value * @param tag property tag * @return property value */ diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/O_nurbscurve.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/O_nurbscurve.java index 05c89ebcf..a686da696 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/O_nurbscurve.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/O_nurbscurve.java @@ -68,7 +68,7 @@ public class O_nurbscurve { /** * Makes new O_nurbscurve - * @param realType type of curve + * @param realType type of curve */ public O_nurbscurve(int realType) { // DONE diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Patchlist.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Patchlist.java index f1e499a28..8c2922565 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Patchlist.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Patchlist.java @@ -52,7 +52,7 @@ public class Patchlist { private Patch patch; /** - * Makes new list of patches + * Makes new list of patches * @param quilts list of quilts * @param pta low border * @param ptb high border diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Property.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Property.java index 25b4dc441..79f36ce43 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Property.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Property.java @@ -36,9 +36,9 @@ package jogamp.opengl.glu.nurbs; /** * Class representing property - * + * * @author Tomas Hrasky - * + * */ public class Property { @@ -59,7 +59,7 @@ public class Property { /** * Makes new property with given parameters - * + * * @param type * property type * @param tag diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Renderhints.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Renderhints.java index 4729e2421..7c636122f 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Renderhints.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Renderhints.java @@ -35,7 +35,7 @@ package jogamp.opengl.glu.nurbs; */ /** - * Class holding rendering params + * Class holding rendering params * @author Tomas Hrasky * */ diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/Subdivider.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/Subdivider.java index 37774f811..4d8296cca 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/Subdivider.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/Subdivider.java @@ -41,7 +41,7 @@ package jogamp.opengl.glu.nurbs; */ public class Subdivider { /** - * Cull type + * Cull type */ public static final int CULL_TRIVIAL_REJECT = 0; @@ -76,7 +76,7 @@ public class Subdivider { private int subdivisions; /** - * U step when using domain distance sampling + * U step when using domain distance sampling */ private float domain_distance_u_rate; @@ -375,7 +375,7 @@ public class Subdivider { } /** - * Sample + * Sample * @param source * @param patchlist * @param subdivisions diff --git a/src/jogl/classes/jogamp/opengl/glu/nurbs/TrimVertex.java b/src/jogl/classes/jogamp/opengl/glu/nurbs/TrimVertex.java index e88d69709..1025afb7c 100644 --- a/src/jogl/classes/jogamp/opengl/glu/nurbs/TrimVertex.java +++ b/src/jogl/classes/jogamp/opengl/glu/nurbs/TrimVertex.java @@ -36,9 +36,9 @@ package jogamp.opengl.glu.nurbs; /** * Holds vertex used in trim - * + * * @author Tomas Hrasky - * + * */ public class TrimVertex { diff --git a/src/jogl/classes/jogamp/opengl/glu/registry/Registry.java b/src/jogl/classes/jogamp/opengl/glu/registry/Registry.java index 3d669d9bb..9c8523e51 100644 --- a/src/jogl/classes/jogamp/opengl/glu/registry/Registry.java +++ b/src/jogl/classes/jogamp/opengl/glu/registry/Registry.java @@ -6,15 +6,15 @@ * this file except in compliance with the License. You may obtain a copy * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * + * * http://oss.sgi.com/projects/FreeB - * + * * Note that, as provided in the License, the Software is distributed on an * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * + * * NOTE: The Original Code (as defined below) has been licensed to Sun * Microsystems, Inc. ("Sun") under the SGI Free Software License B * (Version 1.1), shown above ("SGI License"). Pursuant to Section @@ -30,7 +30,7 @@ * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. * Copyright in any portions created by third parties is as indicated * elsewhere herein. All Rights Reserved. - * + * * Additional Notice Provisions: The application programming interfaces * established by SGI in conjunction with the Original Code are The * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released @@ -51,11 +51,11 @@ import javax.media.opengl.glu.GLU; * @author Administrator */ public class Registry { - + /** Creates a new instance of Registry */ public Registry() { } - + public static String gluGetString(int name) { if( name == GLU.GLU_VERSION ) { return( "1.3" ); @@ -64,7 +64,7 @@ public class Registry { } return( null ); } - + public static boolean gluCheckExtension( String extName, String extString ) { if( extName == null || extString == null ) { return( false ); diff --git a/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQHeap.java b/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQHeap.java index 474056cc3..1ac0fd438 100644 --- a/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQHeap.java +++ b/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQHeap.java @@ -81,6 +81,7 @@ class PriorityQHeap extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqHeapDeletePriorityQ */ + @Override void pqDeletePriorityQ() { handles = null; nodes = null; @@ -137,6 +138,7 @@ class PriorityQHeap extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqHeapInit */ + @Override boolean pqInit() { int i; @@ -152,6 +154,7 @@ class PriorityQHeap extends jogamp.opengl.glu.tessellator.PriorityQ { /* really __gl_pqHeapInsert */ /* returns LONG_MAX iff out of memory */ + @Override int pqInsert(Object keyNew) { int curr; int free; @@ -207,6 +210,7 @@ class PriorityQHeap extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqHeapExtractMin */ + @Override Object pqExtractMin() { jogamp.opengl.glu.tessellator.PriorityQ.PQnode[] n = nodes; jogamp.opengl.glu.tessellator.PriorityQ.PQhandleElem[] h = handles; @@ -229,6 +233,7 @@ class PriorityQHeap extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqHeapDelete */ + @Override void pqDelete(int hCurr) { jogamp.opengl.glu.tessellator.PriorityQ.PQnode[] n = nodes; jogamp.opengl.glu.tessellator.PriorityQ.PQhandleElem[] h = handles; @@ -252,10 +257,12 @@ class PriorityQHeap extends jogamp.opengl.glu.tessellator.PriorityQ { freeList = hCurr; } + @Override Object pqMinimum() { return handles[nodes[1].handle].key; } + @Override boolean pqIsEmpty() { return size == 0; } diff --git a/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQSort.java b/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQSort.java index f9e0225e3..cf54b15c3 100644 --- a/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQSort.java +++ b/src/jogl/classes/jogamp/opengl/glu/tessellator/PriorityQSort.java @@ -71,6 +71,7 @@ class PriorityQSort extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqSortDeletePriorityQ */ + @Override void pqDeletePriorityQ() { if (heap != null) heap.pqDeletePriorityQ(); order = null; @@ -100,6 +101,7 @@ class PriorityQSort extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqSortInit */ + @Override boolean pqInit() { int p, r, i, j; int piv; @@ -191,6 +193,7 @@ class PriorityQSort extends jogamp.opengl.glu.tessellator.PriorityQ { /* really __gl_pqSortInsert */ /* returns LONG_MAX iff out of memory */ + @Override int pqInsert(Object keyNew) { int curr; @@ -220,6 +223,7 @@ class PriorityQSort extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqSortExtractMin */ + @Override Object pqExtractMin() { Object sortMin, heapMin; @@ -240,6 +244,7 @@ class PriorityQSort extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqSortMinimum */ + @Override Object pqMinimum() { Object sortMin, heapMin; @@ -257,11 +262,13 @@ class PriorityQSort extends jogamp.opengl.glu.tessellator.PriorityQ { } /* really __gl_pqSortIsEmpty */ + @Override boolean pqIsEmpty() { return (size == 0) && heap.pqIsEmpty(); } /* really __gl_pqSortDelete */ + @Override void pqDelete(int curr) { if (curr >= 0) { heap.pqDelete(curr); diff --git a/src/jogl/classes/jogamp/opengl/glu/tessellator/Render.java b/src/jogl/classes/jogamp/opengl/glu/tessellator/Render.java index 1801e1c59..a2e973508 100644 --- a/src/jogl/classes/jogamp/opengl/glu/tessellator/Render.java +++ b/src/jogl/classes/jogamp/opengl/glu/tessellator/Render.java @@ -279,6 +279,7 @@ class Render { } private static class RenderTriangle implements renderCallBack { + @Override public void render(GLUtessellatorImpl tess, jogamp.opengl.glu.tessellator.GLUhalfEdge e, long size) { /* Just add the triangle to a triangle list, so we can render all * the separate triangles at once. @@ -323,6 +324,7 @@ class Render { } private static class RenderFan implements renderCallBack { + @Override public void render(GLUtessellatorImpl tess, jogamp.opengl.glu.tessellator.GLUhalfEdge e, long size) { /* Render as many CCW triangles as possible in a fan starting from * edge "e". The fan *should* contain exactly "size" triangles @@ -345,6 +347,7 @@ class Render { } private static class RenderStrip implements renderCallBack { + @Override public void render(GLUtessellatorImpl tess, jogamp.opengl.glu.tessellator.GLUhalfEdge e, long size) { /* Render as many CCW triangles as possible in a strip starting from * edge "e". The strip *should* contain exactly "size" triangles diff --git a/src/jogl/classes/jogamp/opengl/glu/tessellator/Sweep.java b/src/jogl/classes/jogamp/opengl/glu/tessellator/Sweep.java index b4a400c1c..d2c0db61e 100644 --- a/src/jogl/classes/jogamp/opengl/glu/tessellator/Sweep.java +++ b/src/jogl/classes/jogamp/opengl/glu/tessellator/Sweep.java @@ -742,7 +742,7 @@ class Sweep { eUp.Org.s = isect.s; eUp.Org.t = isect.t; eUp.Org.pqHandle = tess.pq.pqInsert(eUp.Org); /* __gl_pqSortInsert */ - if (eUp.Org.pqHandle == Long.MAX_VALUE) { + if (eUp.Org.pqHandle == Integer.MAX_VALUE) { tess.pq.pqDeletePriorityQ(); /* __gl_pqSortDeletePriorityQ */ tess.pq = null; throw new RuntimeException(); @@ -1150,6 +1150,7 @@ class Sweep { */ { /* __gl_dictListNewDict */ tess.dict = Dict.dictNewDict(tess, new Dict.DictLeq() { + @Override public boolean leq(Object frame, Object key1, Object key2) { return EdgeLeq(tess, (ActiveRegion) key1, (ActiveRegion) key2); } @@ -1231,6 +1232,7 @@ class Sweep { /* __gl_pqSortNewPriorityQ */ pq = tess.pq = PriorityQ.pqNewPriorityQ(new PriorityQ.Leq() { + @Override public boolean leq(Object key1, Object key2) { return Geom.VertLeq(((GLUvertex) key1), (GLUvertex) key2); } @@ -1240,7 +1242,7 @@ class Sweep { vHead = tess.mesh.vHead; for (v = vHead.next; v != vHead; v = v.next) { v.pqHandle = pq.pqInsert(v); /* __gl_pqSortInsert */ - if (v.pqHandle == Long.MAX_VALUE) break; + if (v.pqHandle == Integer.MAX_VALUE) break; } if (v != vHead || !pq.pqInit()) { /* __gl_pqSortInit */ tess.pq.pqDeletePriorityQ(); /* __gl_pqSortDeletePriorityQ */ diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index 3cd4b340c..259c70641 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,6 +41,7 @@ package jogamp.opengl.macosx.cgl; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.Map; import javax.media.nativewindow.AbstractGraphicsConfiguration; @@ -48,110 +49,180 @@ import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.OffscreenLayerSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL3ES3; +import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import javax.media.opengl.GLUniformData; +import jogamp.nativewindow.macosx.OSXUtil; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLFBODrawableImpl; import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.macosx.cgl.MacOSXCGLDrawable.GLBackendType; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.common.os.Platform; 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.GLRendererQuirks; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; -public abstract class MacOSXCGLContext extends GLContextImpl -{ +public class MacOSXCGLContext extends GLContextImpl +{ // Abstract interface for implementation of this context (either // NSOpenGL-based or CGL-based) protected interface GLBackendImpl { boolean isNSContext(); long create(long share, int ctp, int major, int minor); boolean destroy(long ctx); + void associateDrawable(boolean bound); boolean copyImpl(long src, int mask); boolean makeCurrent(long ctx); boolean release(long ctx); + boolean detachPBuffer(); boolean setSwapInterval(int interval); boolean swapBuffers(); } - + /* package */ static final boolean isTigerOrLater; /* package */ static final boolean isLionOrLater; + /* package */ static final boolean isMavericksOrLater; static { final VersionNumber osvn = Platform.getOSVersionNumber(); - isTigerOrLater = osvn.getMajor() > 10 || ( osvn.getMajor() == 10 && osvn.getMinor() >= 4 ); - isLionOrLater = osvn.getMajor() > 10 || ( osvn.getMajor() == 10 && osvn.getMinor() >= 7 ); + isTigerOrLater = osvn.compareTo(Platform.OSXVersion.Tiger) >= 0; + isLionOrLater = osvn.compareTo(Platform.OSXVersion.Lion) >= 0; + isMavericksOrLater = osvn.compareTo(Platform.OSXVersion.Mavericks) >= 0; } static boolean isGLProfileSupported(int ctp, int major, int minor) { boolean ctBwdCompat = 0 != ( CTX_PROFILE_COMPAT & ctp ) ; boolean ctCore = 0 != ( CTX_PROFILE_CORE & ctp ) ; - + // We exclude 3.0, since we would map it's core to GL2. Hence we force mapping 2.1 to GL2 - if(3==major && 1<=minor && minor<=2) { - // [3.1..3.2] -> GL3* + if( 3 < major || 3 == major && 1 <= minor ) { + if(ctBwdCompat || !ctCore) { + // No compatibility profile on OS X + // Only core is supported + return false; + } if(!isLionOrLater) { - // no GL3* on pre lion + // no GL Profile >= GL3 core on pre lion return false; } - if(ctBwdCompat) { - // no compatibility profile on OS X + if(3 < major && !isMavericksOrLater) { + // no GL Profile >= GL4 core on pre mavericks return false; } - return ctCore; - } else if(major<3) { + // [3.1..3.x] -> GL3 + // [4.0..4.x] -> GL4 + return true; + } else if( major < 3 ) { // < 3.0 -> GL2 return true; } return false; // 3.0 && > 3.2 } - static int GLProfile2CGLOGLProfileValue(int ctp, int major, int minor) { + static int GLProfile2CGLOGLProfileValue(AbstractGraphicsDevice device, int ctp, int major, int minor) { if(!MacOSXCGLContext.isGLProfileSupported(ctp, major, minor)) { throw new GLException("OpenGL profile not supported: "+getGLVersion(major, minor, ctp, "@GLProfile2CGLOGLProfileVersion")); } - boolean ctCore = 0 != ( CTX_PROFILE_CORE & ctp ) ; - if( major == 3 && minor >= 1 && ctCore ) { - return CGL.kCGLOGLPVersion_3_2_Core; + final boolean ctCore = 0 != ( CTX_PROFILE_CORE & ctp ) ; + + if( major == 4 && ctCore ) { + if( GLRendererQuirks.existStickyDeviceQuirk(device, GLRendererQuirks.GL4NeedsGL3Request) ) { + // Thread safe GLRendererQuirks sticky access, since we are only interested of the result _after_ GL version mapping, + // i.e. after single threaded initialization! + return CGL.kCGLOGLPVersion_GL3_Core; + } else { + return CGL.kCGLOGLPVersion_GL4_Core; + } + } else if( major == 3 && minor >= 1 && ctCore ) { + return CGL.kCGLOGLPVersion_GL3_Core; } else { return CGL.kCGLOGLPVersion_Legacy; - } - } + } + } + + private static final String shaderBasename = "texture01_xxx"; + + 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, + "../../shader", "../../shader/bin", shaderBasename, true); + final ShaderCode fp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, MacOSXCGLContext.class, + "../../shader", "../../shader/bin", shaderBasename, true); + vp.defaultShaderCustomization(gl, true, true); + fp.defaultShaderCustomization(gl, true, true); + sp.add(vp); + sp.add(fp); + if(!sp.link(gl, System.err)) { + throw new GLException("Couldn't link program: "+sp); + } + sp.useProgram(gl, true); + + // setup mgl_PMVMatrix + final PMVMatrix pmvMatrix = new PMVMatrix(); + pmvMatrix.glMatrixMode(PMVMatrix.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + final GLUniformData pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); // P, Mv + pmvMatrixUniform.setLocation(gl, sp.program()); + gl.glUniform(pmvMatrixUniform); + + sp.useProgram(gl, false); + return sp; + } + private boolean haveSetOpenGLMode = false; private GLBackendType openGLMode = GLBackendType.NSOPENGL; - + // Implementation object (either NSOpenGL-based or CGL-based) protected GLBackendImpl impl; - + private CGLExt _cglExt; // Table that holds the addresses of the native C-language entry points for // CGL extension functions. private CGLExtProcAddressTable cglExtProcAddressTable; - + + private long updateHandle = 0; + private int lastWidth, lastHeight; + protected MacOSXCGLContext(GLDrawableImpl drawable, GLContext shareWith) { super(drawable, shareWith); initOpenGLImpl(getOpenGLMode()); } - + @Override - protected void resetStates() { + protected void resetStates(boolean isInit) { // no inner state _cglExt = null; cglExtProcAddressTable = null; - super.resetStates(); + super.resetStates(isInit); } + @Override public Object getPlatformGLExtensions() { return getCGLExt(); } - protected boolean isNSContext() { - return (null != impl) ? impl.isNSContext() : this.openGLMode == GLBackendType.NSOPENGL; + protected boolean isNSContext() { + return (null != impl) ? impl.isNSContext() : this.openGLMode == GLBackendType.NSOPENGL; } public CGLExt getCGLExt() { @@ -161,6 +232,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl return _cglExt; } + @Override public final ProcAddressTable getPlatformExtProcAddressTable() { return getCGLExtProcAddressTable(); } @@ -169,10 +241,13 @@ public abstract class MacOSXCGLContext extends GLContextImpl return cglExtProcAddressTable; } + @Override protected Map<String, String> getFunctionNameMap() { return null; } + @Override protected Map<String, String> getExtensionNameMap() { return null; } + @Override protected long createContextARBImpl(long share, boolean direct, int ctp, int major, int minor) { if(!isGLProfileSupported(ctp, major, minor)) { if(DEBUG) { @@ -200,69 +275,130 @@ public abstract class MacOSXCGLContext extends GLContextImpl return ctx; } + @Override protected void destroyContextARBImpl(long _context) { impl.release(_context); impl.destroy(_context); } + @Override public final boolean isGLReadDrawableAvailable() { return false; } - protected long createImplPreset(GLContextImpl shareWith) throws GLException { - long share = 0; - if (shareWith != null) { - // Change our OpenGL mode to match that of any share context before we create ourselves - setOpenGLMode(((MacOSXCGLContext)shareWith).getOpenGLMode()); - share = shareWith.getHandle(); - if (share == 0) { - throw new GLException("GLContextShareSet returned a NULL OpenGL context"); - } - } - - MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); - GLCapabilitiesImmutable capabilitiesChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - if (capabilitiesChosen.getPbufferFloatingPointBuffers() && !isTigerOrLater) { - throw new GLException("Floating-point pbuffers supported only on OS X 10.4 or later"); - } - GLProfile glp = capabilitiesChosen.getGLProfile(); - if(glp.isGLES1() || glp.isGLES2() || glp.isGL4() || glp.isGL3() && !isLionOrLater) { + @Override + protected boolean createImpl(final long shareWithHandle) throws GLException { + final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); + final GLCapabilitiesImmutable capabilitiesChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final GLProfile glp = capabilitiesChosen.getGLProfile(); + if( glp.isGLES() || + ( glp.isGL3() && !isLionOrLater ) || ( glp.isGL4() && !isMavericksOrLater ) ) { throw new GLException("OpenGL profile not supported on MacOSX "+Platform.getOSVersionNumber()+": "+glp); } - - if (DEBUG) { - System.err.println("Share context is " + toHexString(share) + " for " + this); + if( 0 != shareWithHandle && GLBackendType.NSOPENGL != getOpenGLMode() ) { + throw new GLException("Context sharing only supported in mode "+GLBackendType.NSOPENGL+": "+this); } - return share; - } - - protected boolean createImpl(GLContextImpl shareWith) throws GLException { - long share = createImplPreset(shareWith); - contextHandle = createContextARB(share, true); + contextHandle = createContextARB(shareWithHandle, true); return 0 != contextHandle; } - + + @Override protected void makeCurrentImpl() throws GLException { + /** FIXME: won't work w/ special drawables (like FBO) - check for CGL mode regressions! + * if (getOpenGLMode() != ((MacOSXCGLDrawable)drawable).getOpenGLMode()) { setOpenGLMode(((MacOSXCGLDrawable)drawable).getOpenGLMode()); - } + } */ if (!impl.makeCurrent(contextHandle)) { throw new GLException("Error making Context current: "+this); - } + } + drawableUpdatedNotify(); } - + + @Override protected void releaseImpl() throws GLException { if (!impl.release(contextHandle)) { throw new GLException("Error releasing OpenGL Context: "+this); } } + @Override protected void destroyImpl() throws GLException { + releaseUpdateHandle(); if(!impl.destroy(contextHandle)) { throw new GLException("Error destroying OpenGL Context: "+this); } } + private final long getUpdateHandle() { + if( 0 == updateHandle ) { + lastWidth = -1; + lastHeight = -1; + if( isCreated() && drawable.getChosenGLCapabilities().isOnscreen() && isNSContext() ) { + final boolean incompleteView; + final NativeSurface surface = drawable.getNativeSurface(); + if( surface instanceof ProxySurface ) { + incompleteView = ((ProxySurface)surface).containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE ); + } else { + incompleteView = false; + } + if(!incompleteView) { + updateHandle = CGL.updateContextRegister(contextHandle, drawable.getHandle()); + if(0 == updateHandle) { + throw new InternalError("XXX2"); + } + } + } + } + return updateHandle; + } + + private final void releaseUpdateHandle() { + if ( 0 != updateHandle ) { + CGL.updateContextUnregister(updateHandle); + updateHandle = 0; + } + } + + @Override + protected void drawableUpdatedNotify() throws GLException { + if( drawable.getChosenGLCapabilities().isOnscreen() ) { + final long _updateHandle = getUpdateHandle(); + final int w = drawable.getWidth(); + final int h = drawable.getHeight(); + final boolean updateContext = ( 0!=_updateHandle && CGL.updateContextNeedsUpdate(_updateHandle) ) || + w != lastWidth || h != lastHeight; + if(updateContext) { + lastWidth = w; + lastHeight = h; + if (contextHandle == 0) { + throw new GLException("Context not created"); + } + CGL.updateContext(contextHandle); + } + } + } + + @Override + protected void associateDrawable(boolean bound) { + // context stuff depends on drawable stuff + if(bound) { + super.associateDrawable(true); // 1) init drawable stuff + impl.associateDrawable(true); // 2) init context stuff + getUpdateHandle(); + } else { + releaseUpdateHandle(); + impl.associateDrawable(false); // 1) free context stuff + super.associateDrawable(false); // 2) free drawable stuff + } + } + + /* pp */ void detachPBuffer() { + impl.detachPBuffer(); + } + + + @Override protected void copyImpl(GLContext source, int mask) throws GLException { if( isNSContext() != ((MacOSXCGLContext)source).isNSContext() ) { throw new GLException("Source/Destination OpenGL Context tyoe mismatch: source "+source+", dest: "+this); @@ -275,7 +411,7 @@ public abstract class MacOSXCGLContext extends GLContextImpl protected void swapBuffers() { // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() if(!impl.swapBuffers()) { - throw new GLException("Error swapping buffers: "+this); + throw new GLException("Error swapping buffers: "+this); } } @@ -284,11 +420,19 @@ public abstract class MacOSXCGLContext extends GLContextImpl return impl.setSwapInterval(interval); } - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { + @Override + 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(); @@ -316,39 +460,12 @@ public abstract class MacOSXCGLContext extends GLContextImpl } } } - + + @Override protected final StringBuilder getPlatformExtensionsStringImpl() { return new StringBuilder(); } - - public boolean isExtensionAvailable(String glExtensionName) { - if (glExtensionName.equals("GL_ARB_pbuffer") || - glExtensionName.equals("GL_ARB_pixel_format")) { - return true; - } - return super.isExtensionAvailable(glExtensionName); - } - - public int getOffscreenContextPixelDataType() { - throw new GLException("Should not call this"); - } - - public int getOffscreenContextReadBuffer() { - throw new GLException("Should not call this"); - } - public boolean offscreenImageNeedsVerticalFlip() { - throw new GLException("Should not call this"); - } - - public void bindPbufferToTexture() { - throw new GLException("Should not call this"); - } - - public void releasePbufferFromTexture() { - throw new GLException("Should not call this"); - } - // Support for "mode switching" as described in MacOSXCGLDrawable public void setOpenGLMode(GLBackendType mode) { if (mode == openGLMode) { @@ -364,10 +481,10 @@ public abstract class MacOSXCGLContext extends GLContextImpl } initOpenGLImpl(mode); openGLMode = mode; - haveSetOpenGLMode = true; + haveSetOpenGLMode = true; } public final GLBackendType getOpenGLMode() { return openGLMode; } - + protected void initOpenGLImpl(GLBackendType backend) { switch (backend) { case NSOPENGL: @@ -379,8 +496,9 @@ public abstract class MacOSXCGLContext extends GLContextImpl default: throw new InternalError("Illegal implementation mode " + backend); } - } - + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); @@ -391,291 +509,736 @@ public abstract class MacOSXCGLContext extends GLContextImpl sb.append("] "); return sb.toString(); } - + // NSOpenGLContext-based implementation class NSOpenGLImpl implements GLBackendImpl { - long nsOpenGLLayer = 0; - long nsOpenGLLayerPFmt = 0; - float screenVSyncTimeout; // microSec - int vsyncTimeout; // microSec - for nsOpenGLLayer mode - - public boolean isNSContext() { return true; } - - public long create(long share, int ctp, int major, int minor) { - long ctx = 0; - final MacOSXCGLDrawable drawable = (MacOSXCGLDrawable) MacOSXCGLContext.this.drawable; - final NativeSurface surface = drawable.getNativeSurface(); - final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) surface.getGraphicsConfiguration(); - final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(surface, true); - final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - long pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2NSPixelFormat(chosenCaps, ctp, major, minor); - if (pixelFormat == 0) { + private OffscreenLayerSurface backingLayerHost = null; + /** lifecycle: [create - destroy] */ + private long pixelFormat = 0; + /** microSec - defaults to 1/60s */ + private int screenVSyncTimeout = 16666; + /** microSec - for nsOpenGLLayer mode - defaults to 1/60s + 1ms */ + private volatile int vsyncTimeout = 16666 + 1000; + private int lastWidth=0, lastHeight=0; // allowing to detect size change + private boolean needsSetContextPBuffer = false; + private ShaderProgram gl3ShaderProgram = null; + + @Override + public boolean isNSContext() { return true; } + + + /** Only returns a valid NSView. If !NSView, return null and mark either pbuffer and FBO. */ + private long getNSViewHandle(boolean[] isPBuffer, boolean[] isFBO) { + final long nsViewHandle; + if(drawable instanceof GLFBODrawableImpl) { + nsViewHandle = 0; + isPBuffer[0] = false; + isFBO[0] = true; + if(DEBUG) { + System.err.println("NS viewHandle.1: GLFBODrawableImpl drawable: isFBO "+isFBO+", isPBuffer "+isPBuffer+", "+drawable.getClass().getName()+",\n\t"+drawable); + } + } else { + final long drawableHandle = drawable.getHandle(); + final boolean isNSView = OSXUtil.isNSView(drawableHandle); + final boolean isNSWindow = OSXUtil.isNSWindow(drawableHandle); + isPBuffer[0] = CGL.isNSOpenGLPixelBuffer(drawableHandle); + isFBO[0] = false; + + if( isNSView ) { + nsViewHandle = drawableHandle; + } else if( isNSWindow ) { + nsViewHandle = OSXUtil.GetNSView(drawableHandle); + } else if( isPBuffer[0] ) { + nsViewHandle = 0; + } else { + throw new RuntimeException("Drawable's handle neither NSView, NSWindow nor PBuffer: drawableHandle "+toHexString(drawableHandle)+", isNSView "+isNSView+", isNSWindow "+isNSWindow+", isFBO "+isFBO[0]+", isPBuffer "+isPBuffer[0]+", "+drawable.getClass().getName()+",\n\t"+drawable); + } + if(DEBUG) { + System.err.println("NS viewHandle.2: drawableHandle "+toHexString(drawableHandle)+" -> nsViewHandle "+toHexString(nsViewHandle)+": isNSView "+isNSView+", isNSWindow "+isNSWindow+", isFBO "+isFBO[0]+", isPBuffer "+isPBuffer[0]+", "+drawable.getClass().getName()+",\n\t"+drawable); + } + } + needsSetContextPBuffer = isPBuffer[0]; + return nsViewHandle; + } + + @Override + public long create(long share, int ctp, int major, int minor) { + long ctx = 0; + final NativeSurface surface = drawable.getNativeSurface(); + final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) surface.getGraphicsConfiguration(); + final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final long nsViewHandle; + final boolean isPBuffer; + final boolean isFBO; + { + boolean[] _isPBuffer = { false }; + boolean[] _isFBO = { false }; + nsViewHandle = getNSViewHandle(_isPBuffer, _isFBO); + isPBuffer = _isPBuffer[0]; + isFBO = _isFBO[0]; + } + final OffscreenLayerSurface backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(surface, true); + + boolean incompleteView = null != backingLayerHost; + if( !incompleteView && surface instanceof ProxySurface ) { + incompleteView = ((ProxySurface)surface).containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE ); + } + { + final GLCapabilitiesImmutable targetCaps; + if( isFBO ) { + // Use minimum GLCapabilities for the target surface w/ same profile + targetCaps = new GLCapabilities( chosenCaps.getGLProfile() ); + } else { + targetCaps = chosenCaps; + } + pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2NSPixelFormat(config.getScreen().getDevice(), targetCaps, ctp, major, minor); + } + if (pixelFormat == 0) { + if(DEBUG) { + System.err.println("Unable to allocate pixel format with requested GLCapabilities: "+chosenCaps); + } + return 0; + } + final GLCapabilitiesImmutable fixedCaps; + if( isFBO ) { + // pixelformat of target doesn't affect caps w/ FBO + fixedCaps = chosenCaps; + } else { + final GLCapabilities _fixedCaps = MacOSXCGLGraphicsConfiguration.NSPixelFormat2GLCapabilities(chosenCaps.getGLProfile(), pixelFormat); + if( !_fixedCaps.isPBuffer() && isPBuffer ) { + throw new InternalError("handle is PBuffer, fixedCaps not: "+drawable); + } + // determine on-/offscreen caps, since pformat is ambiguous + _fixedCaps.setPBuffer( isPBuffer ); // exclusive + _fixedCaps.setBitmap( false ); // n/a in our OSX impl. + _fixedCaps.setOnscreen( !isFBO && !isPBuffer ); + fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(_fixedCaps, chosenCaps.isBackgroundOpaque()); + } + final int sRefreshRate = OSXUtil.GetScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex()); + if( 0 < sRefreshRate ) { + screenVSyncTimeout = 1000000 / sRefreshRate; + } if(DEBUG) { - System.err.println("Unable to allocate pixel format with requested GLCapabilities: "+chosenCaps); + System.err.println("NS create OSX>=lion "+isLionOrLater+", OSX>=mavericks "+isMavericksOrLater); + System.err.println("NS create incompleteView: "+incompleteView); + System.err.println("NS create backingLayerHost: "+backingLayerHost); + System.err.println("NS create share: "+share); + System.err.println("NS create drawable type: "+drawable.getClass().getName()); + System.err.println("NS create drawable handle: isPBuffer "+isPBuffer+", isFBO "+isFBO); + System.err.println("NS create pixelFormat: "+toHexString(pixelFormat)); + System.err.println("NS create chosenCaps: "+chosenCaps); + System.err.println("NS create fixedCaps: "+fixedCaps); + System.err.println("NS create drawable native-handle: "+toHexString(drawable.getHandle())); + System.err.println("NS create drawable NSView-handle: "+toHexString(nsViewHandle)); + System.err.println("NS create screen refresh-rate: "+sRefreshRate+" hz, "+screenVSyncTimeout+" micros"); + // Thread.dumpStack(); } - return 0; - } - config.setChosenPixelFormat(pixelFormat); - int sRefreshRate = CGL.getScreenRefreshRate(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getIndex()); - screenVSyncTimeout = 1000000f / (float)sRefreshRate; - if(DEBUG) { - System.err.println("NS create OSX>=lion "+isLionOrLater); - System.err.println("NS create backendType: "+drawable.getOpenGLMode()); - System.err.println("NS create backingLayerHost: "+backingLayerHost); - System.err.println("NS create share: "+share); - System.err.println("NS create chosenCaps: "+chosenCaps); - System.err.println("NS create pixelFormat: "+toHexString(pixelFormat)); - System.err.println("NS create drawable native-handle: "+toHexString(drawable.getHandle())); - System.err.println("NS create drawable NSView-handle: "+toHexString(drawable.getNSViewHandle())); - System.err.println("NS create screen refresh-rate: "+sRefreshRate+" hz, "+screenVSyncTimeout+" micros"); - // Thread.dumpStack(); - } - try { - int[] viewNotReady = new int[1]; + config.setChosenCapabilities(fixedCaps); + + final IntBuffer viewNotReady = Buffers.newDirectIntBuffer(1); // Try to allocate a context with this - ctx = CGL.createContext(share, - drawable.getNSViewHandle(), null!=backingLayerHost, - pixelFormat, - chosenCaps.isBackgroundOpaque(), - viewNotReady, 0); + ctx = CGL.createContext(share, nsViewHandle, incompleteView, + pixelFormat, chosenCaps.isBackgroundOpaque(), viewNotReady); if (0 == ctx) { - if(DEBUG) { - System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady[0])); - } - return 0; + if(DEBUG) { + System.err.println("NS create failed: viewNotReady: "+ (1 == viewNotReady.get(0))); + } + return 0; } - - if (!chosenCaps.isPBuffer() && !chosenCaps.isBackgroundOpaque()) { + + if (chosenCaps.isOnscreen() && !chosenCaps.isBackgroundOpaque()) { // Set the context opacity CGL.setContextOpacity(ctx, 0); } + return ctx; + } - if(DEBUG) { - GLCapabilitiesImmutable caps0 = MacOSXCGLGraphicsConfiguration.NSPixelFormat2GLCapabilities(null, pixelFormat); - System.err.println("NS create pixelformat2GLCaps: "+caps0); + @Override + public boolean destroy(long ctx) { + if(0!=pixelFormat) { + CGL.deletePixelFormat(pixelFormat); + pixelFormat = 0; } - GLCapabilitiesImmutable fixedCaps = MacOSXCGLGraphicsConfiguration.NSPixelFormat2GLCapabilities(chosenCaps.getGLProfile(), pixelFormat); - fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(fixedCaps, chosenCaps.isBackgroundOpaque()); - config.setChosenCapabilities(fixedCaps); - if(DEBUG) { - System.err.println("NS create fixedCaps: "+fixedCaps); + return CGL.deleteContext(ctx, true); + } + + /** + * NSOpenGLLayer creation and it's attachment is performed on the main-thread w/o [infinite] blocking. + * <p> + * Since NSOpenGLLayer creation requires this context for it's shared context creation, + * this method attempts to acquire the surface and context lock with {@link #screenVSyncTimeout}/2 maximum wait time. + * If the surface and context lock could not be acquired, this runnable is being re-queued for later execution. + * </p> + * <p> + * Hence this method blocks the main-thread only for a short period of time. + * </p> + */ + class AttachGLLayerCmd implements Runnable { + final OffscreenLayerSurface ols; + final long ctx; + final int shaderProgram; + final long pfmt; + final long pbuffer; + final int texID; + final boolean isOpaque; + final int width; + final int height; + /** Synchronized by instance's monitor */ + long nsOpenGLLayer; + /** Synchronized by instance's monitor */ + boolean valid; + + AttachGLLayerCmd(OffscreenLayerSurface ols, long ctx, int shaderProgram, long pfmt, long pbuffer, int texID, boolean isOpaque, int width, int height) { + this.ols = ols; + this.ctx = ctx; + this.shaderProgram = shaderProgram; + this.pfmt = pfmt; + this.pbuffer = pbuffer; + this.texID = texID; + this.isOpaque = isOpaque; + this.width = width; + this.height = height; + this.valid = false; + this.nsOpenGLLayer = 0; } - if(fixedCaps.isPBuffer()) { - // Must now associate the pbuffer with our newly-created context - CGL.setContextPBuffer(ctx, drawable.getHandle()); + + public final String contentToString() { + return "valid "+valid+", size "+width+"x"+height+", ctx "+toHexString(ctx)+", opaque "+isOpaque+", texID "+texID+", pbuffer "+toHexString(pbuffer)+", nsOpenGLLayer "+toHexString(nsOpenGLLayer); } - // - // handled layered surface - // - if(null != backingLayerHost) { - nsOpenGLLayerPFmt = pixelFormat; - pixelFormat = 0; - final int texWidth, texHeight; - if(drawable instanceof MacOSXPbufferCGLDrawable) { - final MacOSXPbufferCGLDrawable osxPDrawable = (MacOSXPbufferCGLDrawable)drawable; - texWidth = osxPDrawable.getTextureWidth(); - texHeight = osxPDrawable.getTextureHeight(); - } else { - texWidth = drawable.getWidth(); - texHeight = drawable.getHeight(); - } - nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, nsOpenGLLayerPFmt, drawable.getHandle(), fixedCaps.isBackgroundOpaque(), texWidth, texHeight); - if(0>=texWidth || 0>=texHeight || !drawable.isRealized()) { - throw new GLException("Drawable not realized yet or invalid texture size, texSize "+texWidth+"x"+texHeight+", "+drawable); - } - if (DEBUG) { - System.err.println("NS create nsOpenGLLayer "+toHexString(nsOpenGLLayer)+", texSize "+texWidth+"x"+texHeight+", "+drawable); - } - backingLayerHost.attachSurfaceLayer(nsOpenGLLayer); - setSwapInterval(1); // enabled per default in layered surface + + @Override + public final String toString() { + return "AttachGLLayerCmd["+contentToString()+"]"; } - } finally { - if(0!=pixelFormat) { - CGL.deletePixelFormat(pixelFormat); + + @Override + public void run() { + synchronized(this) { + if( !valid ) { + try { + final int maxwait = screenVSyncTimeout/2000; // TO 1/2 of current screen-vsync in [ms] + final RecursiveLock surfaceLock = ols.getLock(); + if( surfaceLock.tryLock( maxwait ) ) { + try { + if( MacOSXCGLContext.this.lock.tryLock( maxwait ) ) { + try { + nsOpenGLLayer = CGL.createNSOpenGLLayer(ctx, shaderProgram, pfmt, pbuffer, texID, isOpaque, width, height); + ols.attachSurfaceLayer(nsOpenGLLayer); + final int currentInterval = MacOSXCGLContext.this.getSwapInterval(); + final int interval = 0 <= currentInterval ? currentInterval : 1; + setSwapIntervalImpl(nsOpenGLLayer, interval); // enabled per default in layered surface + valid = true; + if (DEBUG) { + System.err.println("NSOpenGLLayer.Attach: OK, layer "+toHexString(nsOpenGLLayer)+" w/ pbuffer "+toHexString(pbuffer)+", texID "+texID+", texSize "+lastWidth+"x"+lastHeight+", drawableHandle "+toHexString(drawable.getHandle())+" - "+getThreadName()); + } + } finally { + MacOSXCGLContext.this.lock.unlock(); + } + } + } finally { + surfaceLock.unlock(); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + if( !valid ) { + // could not acquire lock, re-queue + if (DEBUG) { + System.err.println("NSOpenGLLayer.Attach: Re-Queue, drawableHandle "+toHexString(drawable.getHandle())+" - "+getThreadName()); + } + OSXUtil.RunLater(true /* onMain */, this, 1); + } + } + } } + } + AttachGLLayerCmd attachGLLayerCmd = null; + + class DetachGLLayerCmd implements Runnable { + final AttachGLLayerCmd cmd; + + DetachGLLayerCmd(AttachGLLayerCmd cmd) { + this.cmd = cmd; + } + + @Override + public final String toString() { + return "DetachGLLayerCmd["+cmd.contentToString()+"]"; + } + + @Override + public void run() { + synchronized( cmd ) { + if( cmd.valid ) { + // still having a valid OLS attached to surface (parent OLS could have been removed) + try { + final OffscreenLayerSurface ols = cmd.ols; + final long l = ols.getAttachedSurfaceLayer(); + if( 0 != l ) { + ols.detachSurfaceLayer(); + } + } catch(Throwable t) { + System.err.println("Catched Exception on thread "+getThreadName()); + t.printStackTrace(); + } + CGL.releaseNSOpenGLLayer(cmd.nsOpenGLLayer); + if(DEBUG) { + System.err.println("NSOpenGLLayer.Detach: OK, layer "+toHexString(cmd.nsOpenGLLayer)+" - "+getThreadName()); + } + cmd.nsOpenGLLayer = 0; + cmd.valid = false; + } else if(DEBUG) { + System.err.println("NSOpenGLLayer.Detach: Skipped "+toHexString(cmd.nsOpenGLLayer)+" - "+getThreadName()); + } + } } - return ctx; - } - - public boolean destroy(long ctx) { - if(0 != nsOpenGLLayer) { - final NativeSurface surface = drawable.getNativeSurface(); - if (DEBUG) { - System.err.println("NS destroy nsOpenGLLayer "+toHexString(nsOpenGLLayer)); - } - final OffscreenLayerSurface ols = NativeWindowFactory.getOffscreenLayerSurface(surface, true); - if(null == ols) { - throw new InternalError("XXX: "+ols); - } - CGL.releaseNSOpenGLLayer(nsOpenGLLayer); - ols.detachSurfaceLayer(nsOpenGLLayer); - CGL.deletePixelFormat(nsOpenGLLayerPFmt); - nsOpenGLLayerPFmt = 0; - nsOpenGLLayer = 0; } - return CGL.deleteContext(ctx, true); - } - public boolean copyImpl(long src, int mask) { - CGL.copyContext(contextHandle, src, mask); - return true; - } - - public boolean makeCurrent(long ctx) { - final long cglCtx = CGL.getCGLContext(ctx); - if(0 == cglCtx) { - throw new InternalError("Null CGLContext for: "+this); + @Override + public void associateDrawable(boolean bound) { + backingLayerHost = NativeWindowFactory.getOffscreenLayerSurface(drawable.getNativeSurface(), true); + + if(DEBUG) { + System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable: "+bound+", ctx "+toHexString(contextHandle)+ + ", hasBackingLayerHost "+(null!=backingLayerHost)+", attachGLLayerCmd "+attachGLLayerCmd); + // Thread.dumpStack(); + } + + if( bound ) { + if( null != backingLayerHost ) { + final GLCapabilitiesImmutable chosenCaps; + final long ctx; + final int texID; + final long pbufferHandle; + final int gl3ShaderProgramName; + + // + // handled layered surface + // + chosenCaps = drawable.getChosenGLCapabilities(); + ctx = MacOSXCGLContext.this.getHandle(); + final long drawableHandle = drawable.getHandle(); + if(drawable instanceof GLFBODrawableImpl) { + final GLFBODrawableImpl fbod = (GLFBODrawableImpl)drawable; + texID = fbod.getTextureBuffer(GL.GL_FRONT).getName(); + pbufferHandle = 0; + fbod.setSwapBufferContext(new GLFBODrawableImpl.SwapBufferContext() { + @Override + public void swapBuffers(boolean doubleBuffered) { + MacOSXCGLContext.NSOpenGLImpl.this.swapBuffers(); + } } ) ; + } else if( CGL.isNSOpenGLPixelBuffer(drawableHandle) ) { + texID = 0; + pbufferHandle = drawableHandle; + if(0 != drawableHandle) { // complete 'validatePBufferConfig(..)' procedure + CGL.setContextPBuffer(ctx, pbufferHandle); + needsSetContextPBuffer = false; + } + } else { + throw new GLException("BackingLayerHost w/ unknown handle (!FBO, !PBuffer): "+drawable); + } + lastWidth = drawable.getWidth(); + lastHeight = drawable.getHeight(); + if(0>=lastWidth || 0>=lastHeight || !drawable.isRealized()) { + throw new GLException("Drawable not realized yet or invalid texture size, texSize "+lastWidth+"x"+lastHeight+", "+drawable); + } + if( MacOSXCGLContext.this.isGL3core() ) { + if( null == gl3ShaderProgram) { + gl3ShaderProgram = createCALayerShader(MacOSXCGLContext.this.gl.getGL3ES3()); + } + gl3ShaderProgramName = gl3ShaderProgram.program(); + } else { + gl3ShaderProgramName = 0; + } + + // All CALayer lifecycle ops are deferred on main-thread + attachGLLayerCmd = new AttachGLLayerCmd( + backingLayerHost, ctx, gl3ShaderProgramName, pixelFormat, pbufferHandle, texID, + chosenCaps.isBackgroundOpaque(), lastWidth, lastHeight ); + if(DEBUG) { + System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable(true): "+attachGLLayerCmd); + } + OSXUtil.RunOnMainThread(false, attachGLLayerCmd); + } else { // -> null == backingLayerHost + lastWidth = drawable.getWidth(); + lastHeight = drawable.getHeight(); + boolean[] isPBuffer = { false }; + boolean[] isFBO = { false }; + CGL.setContextView(contextHandle, getNSViewHandle(isPBuffer, isFBO)); + } + } else { // -> !bound + if( null != backingLayerHost ) { + final AttachGLLayerCmd cmd = attachGLLayerCmd; + attachGLLayerCmd = null; + if( null == cmd ) { + throw new GLException("Null attachGLLayerCmd: "+drawable); + } + if( 0 != cmd.pbuffer ) { + CGL.setContextPBuffer(contextHandle, 0); + } + synchronized(cmd) { + if( !cmd.valid ) { + cmd.valid = true; // skip pending creation + } else { + // All CALayer lifecycle ops are deferred on main-thread + final DetachGLLayerCmd dCmd = new DetachGLLayerCmd(cmd); + if(DEBUG) { + System.err.println("MaxOSXCGLContext.NSOpenGLImpl.associateDrawable(false): "+dCmd); + } + OSXUtil.RunOnMainThread(false, dCmd); + if( null != gl3ShaderProgram ) { + gl3ShaderProgram.destroy(MacOSXCGLContext.this.gl.getGL3()); + gl3ShaderProgram = null; + } + } + } + } + CGL.clearDrawable(contextHandle); + backingLayerHost = null; + } } - int err = CGL.CGLLockContext(cglCtx); - if(CGL.kCGLNoError == err) { - return CGL.makeCurrentContext(ctx); - } else if(DEBUG) { - System.err.println("NSGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this); + + private final void validatePBufferConfig(long ctx) { + final long drawableHandle = drawable.getHandle(); + if( needsSetContextPBuffer && 0 != drawableHandle && CGL.isNSOpenGLPixelBuffer(drawableHandle) ) { + // Must associate the pbuffer with our newly-created context + needsSetContextPBuffer = false; + CGL.setContextPBuffer(ctx, drawableHandle); + if(DEBUG) { + System.err.println("NS.validateDrawableConfig bind pbuffer "+toHexString(drawableHandle)+" -> ctx "+toHexString(ctx)); + } + } } - return false; - } - public boolean release(long ctx) { - try { - gl.glFlush(); // w/o glFlush()/glFinish() OSX < 10.7 (NVidia driver) may freeze - } catch (GLException gle) { - if(DEBUG) { - System.err.println("MacOSXCGLContext.NSOpenGLImpl.release: INFO: glFlush() catched exception:"); - gle.printStackTrace(); + /** Returns true if size has been updated, otherwise false (same size). */ + private final boolean validateDrawableSizeConfig(long ctx) { + final int width = drawable.getWidth(); + final int height = drawable.getHeight(); + if( lastWidth != width || lastHeight != height ) { + lastWidth = drawable.getWidth(); + lastHeight = drawable.getHeight(); + if(DEBUG) { + System.err.println("NS.validateDrawableConfig size changed"); + } + return true; } + return false; } - final boolean res = CGL.clearCurrentContext(ctx); - final long cglCtx = CGL.getCGLContext(ctx); - if(0 == cglCtx) { - throw new InternalError("Null CGLContext for: "+this); + + @Override + public boolean copyImpl(long src, int mask) { + CGL.copyContext(contextHandle, src, mask); + return true; } - final int err = CGL.CGLUnlockContext(cglCtx); - if(DEBUG && CGL.kCGLNoError != err) { - System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err)+": "+this); - } - return res && CGL.kCGLNoError == err; - } - public boolean setSwapInterval(int interval) { - if(0 != nsOpenGLLayer) { - CGL.setNSOpenGLLayerSwapInterval(nsOpenGLLayer, interval); - vsyncTimeout = interval * (int)screenVSyncTimeout; - if(DEBUG) { System.err.println("NS setSwapInterval: "+vsyncTimeout+" micros"); } + @Override + public boolean makeCurrent(long ctx) { + final long cglCtx = CGL.getCGLContext(ctx); + if(0 == cglCtx) { + throw new InternalError("Null CGLContext for: "+this); + } + final int err = CGL.CGLLockContext(cglCtx); + if(CGL.kCGLNoError == err) { + validatePBufferConfig(ctx); // required to handle pbuffer change ASAP + return CGL.makeCurrentContext(ctx); + } else if(DEBUG) { + System.err.println("NSGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this); + } + return false; } - CGL.setSwapInterval(contextHandle, interval); - return true; - } - - public boolean swapBuffers() { - if( 0 != nsOpenGLLayer ) { - // If v-sync is disabled, frames will be drawn as quickly as possible - // w/o delay but in sync w/ CALayer. Otherwise wait until next swap interval (v-sync). - CGL.waitUntilNSOpenGLLayerIsReady(nsOpenGLLayer, vsyncTimeout); + + @Override + public boolean release(long ctx) { + try { + if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != MacOSXCGLContext.this.getGLProcAddressTable() ) { + gl.glFlush(); + } + } catch (GLException gle) { + if(DEBUG) { + System.err.println("MacOSXCGLContext.NSOpenGLImpl.release: INFO: glFlush() catched exception:"); + gle.printStackTrace(); + } + } + final boolean res = CGL.clearCurrentContext(ctx); + final long cglCtx = CGL.getCGLContext(ctx); + if(0 == cglCtx) { + throw new InternalError("Null CGLContext for: "+this); + } + final int err = CGL.CGLUnlockContext(cglCtx); + if(DEBUG && CGL.kCGLNoError != err) { + System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err)+": "+this); + } + return res && CGL.kCGLNoError == err; + } + + @Override + public boolean detachPBuffer() { + needsSetContextPBuffer = true; + // CGL.setContextPBuffer(contextHandle, 0); // doesn't work, i.e. not taking nil + return true; } - if(CGL.flushBuffer(contextHandle)) { - if(0 != nsOpenGLLayer) { - // trigger CALayer to update - CGL.setNSOpenGLLayerNeedsDisplay(nsOpenGLLayer); + + @Override + public boolean setSwapInterval(int interval) { + final AttachGLLayerCmd cmd = attachGLLayerCmd; + if(null != cmd) { + synchronized(cmd) { + if( cmd.valid && 0 != cmd.nsOpenGLLayer) { + setSwapIntervalImpl(cmd.nsOpenGLLayer, interval); + return true; + } + } } + setSwapIntervalImpl(0, interval); return true; } - return false; - } + + private void setSwapIntervalImpl(final long l, int interval) { + if( 0 != l ) { + 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"); } + } + if(DEBUG) { System.err.println("CGL setSwapInterval: "+interval); } + CGL.setSwapInterval(contextHandle, interval); + } + + 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() { + final AttachGLLayerCmd cmd = attachGLLayerCmd; + if(null != cmd) { + synchronized(cmd) { + if( cmd.valid && 0 != cmd.nsOpenGLLayer) { + if( validateDrawableSizeConfig(contextHandle) ) { + // skip wait-for-vsync for a few frames if size has changed, + // allowing to update the texture IDs ASAP. + skipSync = 10; + } + + final boolean res; + final int texID; + final boolean valid; + final boolean isFBO = drawable instanceof GLFBODrawableImpl; + if( isFBO ){ + texID = ((GLFBODrawableImpl)drawable).getTextureBuffer(GL.GL_FRONT).getName(); + valid = 0 != texID; + } else { + texID = 0; + valid = 0 != drawable.getHandle(); + } + if(valid) { + 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). + CGL.waitUntilNSOpenGLLayerIsReady(cmd.nsOpenGLLayer, vsyncTimeout); + } else { + skipSync--; + } + if(isFBO) { + // trigger CALayer to update incl. possible surface change (texture) + CGL.setNSOpenGLLayerNeedsDisplayFBO(cmd.nsOpenGLLayer, texID); + } else { + // 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; + } + return res; + } + } + } + return CGL.flushBuffer(contextHandle); + } + } class CGLImpl implements GLBackendImpl { - public boolean isNSContext() { return false; } - - public long create(long share, int ctp, int major, int minor) { - long ctx = 0; - MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); - GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); - long pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2CGLPixelFormat(chosenCaps, ctp, major, minor); - if (pixelFormat == 0) { - throw new GLException("Unable to allocate pixel format with requested GLCapabilities"); - } - config.setChosenPixelFormat(pixelFormat); - try { - // Create new context - PointerBuffer ctxPB = PointerBuffer.allocateDirect(1); - if (DEBUG) { - System.err.println("Share context for CGL-based pbuffer context is " + toHexString(share)); - } - int res = CGL.CGLCreateContext(pixelFormat, share, ctxPB); - if (res != CGL.kCGLNoError) { - throw new GLException("Error code " + res + " while creating context"); + @Override + public boolean isNSContext() { return false; } + + @Override + public long create(long share, int ctp, int major, int minor) { + long ctx = 0; + final MacOSXCGLGraphicsConfiguration config = (MacOSXCGLGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); + final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); + final long pixelFormat = MacOSXCGLGraphicsConfiguration.GLCapabilities2CGLPixelFormat(config.getScreen().getDevice(), + chosenCaps, ctp, major, minor); + if (pixelFormat == 0) { + throw new GLException("Unable to allocate pixel format with requested GLCapabilities"); } - if(chosenCaps.isPBuffer()) { - // Attach newly-created context to the pbuffer - res = CGL.CGLSetPBuffer(ctxPB.get(0), drawable.getHandle(), 0, 0, 0); + try { + // Create new context + PointerBuffer ctxPB = PointerBuffer.allocateDirect(1); + if (DEBUG) { + System.err.println("Share context for CGL-based pbuffer context is " + toHexString(share)); + } + int res = CGL.CGLCreateContext(pixelFormat, share, ctxPB); if (res != CGL.kCGLNoError) { - throw new GLException("Error code " + res + " while attaching context to pbuffer"); + throw new GLException("Error code " + res + " while creating context"); } + ctx = ctxPB.get(0); + + if (0 != ctx) { + GLCapabilities fixedCaps = MacOSXCGLGraphicsConfiguration.CGLPixelFormat2GLCapabilities(pixelFormat); + fixedCaps = GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(fixedCaps, chosenCaps.isBackgroundOpaque()); + { // determine on-/offscreen caps, since pformat is ambiguous + fixedCaps.setFBO( false ); // n/a for CGLImpl + fixedCaps.setPBuffer( fixedCaps.isPBuffer() && !chosenCaps.isOnscreen() ); + fixedCaps.setBitmap( false ); // n/a in our OSX impl. + fixedCaps.setOnscreen( !fixedCaps.isPBuffer() ); + } + config.setChosenCapabilities(fixedCaps); + if(DEBUG) { + System.err.println("CGL create fixedCaps: "+fixedCaps); + } + if(fixedCaps.isPBuffer()) { + // Must now associate the pbuffer with our newly-created context + res = CGL.CGLSetPBuffer(ctx, drawable.getHandle(), 0, 0, 0); + if (res != CGL.kCGLNoError) { + throw new GLException("Error code " + res + " while attaching context to pbuffer"); + } + } + } + } finally { + CGL.CGLDestroyPixelFormat(pixelFormat); } - ctx = ctxPB.get(0); - if(0!=ctx) { - if(DEBUG) { - GLCapabilitiesImmutable caps0 = MacOSXCGLGraphicsConfiguration.CGLPixelFormat2GLCapabilities(pixelFormat); - System.err.println("NS created: "+caps0); - } - } - } finally { - CGL.CGLDestroyPixelFormat(pixelFormat); + return ctx; } - return ctx; - } - - public boolean destroy(long ctx) { - return CGL.CGLDestroyContext(ctx) == CGL.kCGLNoError; - } - public boolean copyImpl(long src, int mask) { - CGL.CGLCopyContext(src, contextHandle, mask); - return true; - } - - public boolean makeCurrent(long ctx) { - int err = CGL.CGLLockContext(ctx); - if(CGL.kCGLNoError == err) { - err = CGL.CGLSetCurrentContext(ctx); + @Override + public boolean destroy(long ctx) { + return CGL.CGLDestroyContext(ctx) == CGL.kCGLNoError; + } + + @Override + public void associateDrawable(boolean bound) { + } + + @Override + public boolean copyImpl(long src, int mask) { + CGL.CGLCopyContext(src, contextHandle, mask); + return true; + } + + @Override + public boolean makeCurrent(long ctx) { + int err = CGL.CGLLockContext(ctx); if(CGL.kCGLNoError == err) { - return true; + err = CGL.CGLSetCurrentContext(ctx); + if(CGL.kCGLNoError == err) { + return true; + } else if(DEBUG) { + System.err.println("CGL: Could not make context current: err 0x"+Integer.toHexString(err)+": "+this); + } } else if(DEBUG) { - System.err.println("CGL: Could not make context current: err 0x"+Integer.toHexString(err)+": "+this); + System.err.println("CGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this); } - } else if(DEBUG) { - System.err.println("CGL: Could not lock context: err 0x"+Integer.toHexString(err)+": "+this); + return false; } - return false; - } - public boolean release(long ctx) { - try { - gl.glFlush(); // w/o glFlush()/glFinish() OSX < 10.7 (NVidia driver) may freeze - } catch (GLException gle) { - if(DEBUG) { - System.err.println("MacOSXCGLContext.CGLImpl.release: INFO: glFlush() catched exception:"); - gle.printStackTrace(); + @Override + public boolean release(long ctx) { + try { + if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != MacOSXCGLContext.this.getGLProcAddressTable() ) { + gl.glFlush(); + } + } catch (GLException gle) { + if(DEBUG) { + System.err.println("MacOSXCGLContext.CGLImpl.release: INFO: glFlush() catched exception:"); + gle.printStackTrace(); + } } + int err = CGL.CGLSetCurrentContext(0); + if(DEBUG && CGL.kCGLNoError != err) { + System.err.println("CGL: Could not release current context: err 0x"+Integer.toHexString(err)+": "+this); + } + int err2 = CGL.CGLUnlockContext(ctx); + if(DEBUG && CGL.kCGLNoError != err2) { + System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err2)+": "+this); + } + return CGL.kCGLNoError == err && CGL.kCGLNoError == err2; } - int err = CGL.CGLSetCurrentContext(0); - if(DEBUG && CGL.kCGLNoError != err) { - System.err.println("CGL: Could not release current context: err 0x"+Integer.toHexString(err)+": "+this); + + @Override + public boolean detachPBuffer() { + /* Doesn't work, i.e. not taking NULL + final int res = CGL.CGLSetPBuffer(contextHandle, 0, 0, 0, 0); + if (res != CGL.kCGLNoError) { + throw new GLException("Error code " + res + " while detaching context from pbuffer"); + } */ + return true; } - int err2 = CGL.CGLUnlockContext(ctx); - if(DEBUG && CGL.kCGLNoError != err2) { - System.err.println("CGL: Could not unlock context: err 0x"+Integer.toHexString(err2)+": "+this); + + @Override + public boolean setSwapInterval(int interval) { + final IntBuffer lval = Buffers.newDirectIntBuffer(1); + lval.put(0, interval); + CGL.CGLSetParameter(contextHandle, CGL.kCGLCPSwapInterval, lval); + return true; } - return CGL.kCGLNoError == err && CGL.kCGLNoError == err2; - } - - public boolean setSwapInterval(int interval) { - int[] lval = new int[] { interval } ; - CGL.CGLSetParameter(contextHandle, CGL.kCGLCPSwapInterval, lval, 0); - return true; - } - public boolean swapBuffers() { - return CGL.kCGLNoError == CGL.CGLFlushDrawable(contextHandle); - } - } + @Override + public boolean swapBuffers() { + return CGL.kCGLNoError == CGL.CGLFlushDrawable(contextHandle); + } + } } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java index 7b5efc31a..448e3e221 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,10 +42,10 @@ package jogamp.opengl.macosx.cgl; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import javax.media.nativewindow.NativeSurface; +import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; @@ -82,62 +82,75 @@ public abstract class MacOSXCGLDrawable extends GLDrawableImpl { // lifetime of a given GLPbuffer. This is not a fully general // solution (for example, you can't share textures among a // GLPbuffer, a GLJPanel and a GLCanvas simultaneously) but should - // be enough to get things off the ground. + // be enough to get things off the ground. public enum GLBackendType { - NSOPENGL(0), CGL(1); - + /** Default OpenGL Backend */ + NSOPENGL(0), + /** Alternative OpenGL Backend, only used for external context! */ + CGL(1); + public final int id; GLBackendType(int id){ this.id = id; } } - private List<WeakReference<MacOSXCGLContext>> createdContexts = new ArrayList<WeakReference<MacOSXCGLContext>>(); - + /* pp */ List<WeakReference<MacOSXCGLContext>> createdContexts = new ArrayList<WeakReference<MacOSXCGLContext>>(); + private boolean haveSetOpenGLMode = false; private GLBackendType openGLMode = GLBackendType.NSOPENGL; - + public MacOSXCGLDrawable(GLDrawableFactory factory, NativeSurface comp, boolean realized) { super(factory, comp, realized); initOpenGLImpl(getOpenGLMode()); } - + + @Override protected void setRealizedImpl() { } - protected long getNSViewHandle() { - return GLBackendType.NSOPENGL == openGLMode ? getHandle() : 0; - } - - protected void registerContext(MacOSXCGLContext ctx) { + @Override + protected void associateContext(GLContext ctx, boolean bound) { // NOTE: we need to keep track of the created contexts in order to // implement swapBuffers() because of how Mac OS X implements its // OpenGL window interface synchronized (createdContexts) { - createdContexts.add(new WeakReference<MacOSXCGLContext>(ctx)); + if(bound) { + final MacOSXCGLContext osxCtx = (MacOSXCGLContext)ctx; + createdContexts.add(new WeakReference<MacOSXCGLContext>(osxCtx)); + } else { + for(int i=0; i<createdContexts.size(); ) { + final MacOSXCGLContext _ctx = createdContexts.get(i).get(); + if( _ctx == null || _ctx == ctx) { + createdContexts.remove(i); + } else { + i++; + } + } + } } } - protected final void swapBuffersImpl() { - // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() - synchronized (createdContexts) { - for (Iterator<WeakReference<MacOSXCGLContext>> iter = createdContexts.iterator(); iter.hasNext(); ) { - WeakReference<MacOSXCGLContext> ref = iter.next(); - MacOSXCGLContext ctx = ref.get(); - if (ctx != null) { - ctx.swapBuffers(); - } else { - iter.remove(); - } + + @Override + protected final void swapBuffersImpl(boolean doubleBuffered) { + if(doubleBuffered) { + synchronized (createdContexts) { + for(int i=0; i<createdContexts.size(); ) { + final MacOSXCGLContext ctx = createdContexts.get(i).get(); + if (ctx != null) { + ctx.swapBuffers(); + i++; + } else { + createdContexts.remove(i); + } + } } } - } - - public GLDynamicLookupHelper getGLDynamicLookupHelper() { - return getFactoryImpl().getGLDynamicLookupHelper(0); } - protected static String getThreadName() { - return Thread.currentThread().getName(); + @Override + public GLDynamicLookupHelper getGLDynamicLookupHelper() { + return getFactoryImpl().getGLDynamicLookupHelper(0); } // Support for "mode switching" as described in MacOSXCGLDrawable @@ -148,17 +161,16 @@ public abstract class MacOSXCGLDrawable extends GLDrawableImpl { if (haveSetOpenGLMode) { throw new GLException("Can't switch between using NSOpenGLPixelBuffer and CGLPBufferObj more than once"); } - - destroyImpl(); + setRealized(false); if (DEBUG) { System.err.println("MacOSXCGLDrawable: Switching context mode " + openGLMode + " -> " + mode); } initOpenGLImpl(mode); openGLMode = mode; - haveSetOpenGLMode = true; + haveSetOpenGLMode = true; } public final GLBackendType getOpenGLMode() { return openGLMode; } protected void initOpenGLImpl(GLBackendType backend) { /* nop */ } - + } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java index 71c0d5539..9ce71cfe9 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,17 +41,17 @@ package jogamp.opengl.macosx.cgl; import java.nio.Buffer; +import java.nio.FloatBuffer; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; import javax.media.opengl.GL; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; @@ -60,21 +60,27 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import javax.media.opengl.GLProfile.ShutdownType; +import jogamp.nativewindow.WrappedSurface; +import jogamp.nativewindow.macosx.OSXDummyUpstreamSurfaceHook; import jogamp.opengl.DesktopGLDynamicLookupHelper; +import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableFactoryImpl; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.GLDynamicLookupHelper; +import jogamp.opengl.GLGraphicsConfigurationUtil; +import jogamp.opengl.SharedResourceRunner; -import com.jogamp.common.JogampRuntimeException; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.nativewindow.WrappedSurface; +import com.jogamp.nativewindow.MutableGraphicsConfiguration; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { private static DesktopGLDynamicLookupHelper macOSXCGLDynamicLookupHelper = null; - + public MacOSXCGLDrawableFactory() { super(); @@ -90,31 +96,40 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { } if(null!=tmp && tmp.isLibComplete()) { macOSXCGLDynamicLookupHelper = tmp; - /** FIXME ?? + /** FIXME ?? CGL.getCGLProcAddressTable().reset(macOSXCGLDynamicLookupHelper); */ } } } - + defaultDevice = new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); - + if(null!=macOSXCGLDynamicLookupHelper) { // Register our GraphicsConfigurationFactory implementations // The act of constructing them causes them to be registered MacOSXCGLGraphicsConfigurationFactory.registerFactory(); if(GLProfile.isAWTAvailable()) { try { - ReflectionUtil.callStaticMethod("jogamp.opengl.macosx.cgl.awt.MacOSXAWTCGLGraphicsConfigurationFactory", - "registerFactory", null, null, getClass().getClassLoader()); - } catch (JogampRuntimeException jre) { /* n/a .. */ } + ReflectionUtil.callStaticMethod("jogamp.opengl.macosx.cgl.awt.MacOSXAWTCGLGraphicsConfigurationFactory", + "registerFactory", null, null, getClass().getClassLoader()); + } catch (Exception jre) { /* n/a .. */ } } - + sharedMap = new HashMap<String, SharedResource>(); - } + } + } + + @Override + protected final boolean isComplete() { + return null != macOSXCGLDynamicLookupHelper; } - protected final void destroy(ShutdownType shutdownType) { + @Override + protected final void shutdownImpl() { + if( DEBUG ) { + System.err.println("MacOSXCGLDrawableFactory.shutdown"); + } if(null != sharedMap) { sharedMap.clear(); sharedMap = null; @@ -122,13 +137,13 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { defaultDevice = null; /** * Pulling away the native library may cause havoc .. - * - if(ShutdownType.COMPLETE == shutdownType && null != macOSXCGLDynamicLookupHelper) { - macOSXCGLDynamicLookupHelper.destroy(); - macOSXCGLDynamicLookupHelper = null; - } */ + * + macOSXCGLDynamicLookupHelper.destroy(); + */ + macOSXCGLDynamicLookupHelper = null; } + @Override public GLDynamicLookupHelper getGLDynamicLookupHelper(int profile) { return macOSXCGLDynamicLookupHelper; } @@ -136,37 +151,62 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { private HashMap<String, SharedResource> sharedMap = new HashMap<String, SharedResource>(); private MacOSXGraphicsDevice defaultDevice; - static class SharedResource { + static class SharedResource implements SharedResourceRunner.Resource { // private MacOSXCGLDrawable drawable; // private MacOSXCGLContext context; + private final GLRendererQuirks glRendererQuirks; MacOSXGraphicsDevice device; - boolean wasContextCreated; + boolean valid; boolean hasNPOTTextures; boolean hasRECTTextures; boolean hasAppleFloatPixels; - SharedResource(MacOSXGraphicsDevice device, boolean wasContextCreated, + SharedResource(MacOSXGraphicsDevice device, boolean valid, boolean hasNPOTTextures, boolean hasRECTTextures, boolean hasAppletFloatPixels - /* MacOSXCGLDrawable draw, MacOSXCGLContext ctx */) { + /* MacOSXCGLDrawable draw, MacOSXCGLContext ctx */, GLRendererQuirks glRendererQuirks) { // drawable = draw; - // context = ctx; + // this.context = ctx; + this.glRendererQuirks = glRendererQuirks; this.device = device; - this.wasContextCreated = wasContextCreated; + this.valid = valid; this.hasNPOTTextures = hasNPOTTextures; this.hasRECTTextures = hasRECTTextures; this.hasAppleFloatPixels = hasAppletFloatPixels; } - final MacOSXGraphicsDevice getDevice() { return device; } - final boolean wasContextAvailable() { return wasContextCreated; } + @Override + public final boolean isValid() { + return valid; + } + @Override + public final MacOSXGraphicsDevice getDevice() { return device; } + // final MacOSXCGLContext getContext() { return context; } final boolean isNPOTTextureAvailable() { return hasNPOTTextures; } final boolean isRECTTextureAvailable() { return hasRECTTextures; } final boolean isAppleFloatPixelsAvailable() { return hasAppleFloatPixels; } + @Override + public final AbstractGraphicsScreen getScreen() { + return null; + } + @Override + public final GLDrawableImpl getDrawable() { + return null; + } + @Override + public GLContextImpl getContext() { + return null; + } + @Override + public GLRendererQuirks getRendererQuirks() { + return glRendererQuirks; + } } + @Override public final AbstractGraphicsDevice getDefaultDevice() { return defaultDevice; } + @Override public final boolean getIsDeviceCompatible(AbstractGraphicsDevice device) { if(null!=macOSXCGLDynamicLookupHelper && device instanceof MacOSXGraphicsDevice) { return true; @@ -174,7 +214,7 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { return false; } - private HashSet<String> devicesTried = new HashSet<String>(); + private final HashSet<String> devicesTried = new HashSet<String>(); private boolean getDeviceTried(String connection) { synchronized (devicesTried) { @@ -191,8 +231,9 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { devicesTried.remove(connection); } } - - /* package */ SharedResource getOrCreateOSXSharedResource(AbstractGraphicsDevice adevice) { + + @Override + protected final SharedResource getOrCreateSharedResourceImpl(AbstractGraphicsDevice adevice) { final String connection = adevice.getConnection(); SharedResource sr; synchronized(sharedMap) { @@ -201,7 +242,8 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { if(null==sr && !getDeviceTried(connection)) { addDeviceTried(connection); final MacOSXGraphicsDevice sharedDevice = new MacOSXGraphicsDevice(adevice.getUnitID()); - boolean madeCurrent = false; + GLRendererQuirks glRendererQuirks = null; + boolean isValid = false; boolean hasNPOTTextures = false; boolean hasRECTTextures = false; boolean hasAppleFloatPixels = false; @@ -209,96 +251,68 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { GLProfile glp = GLProfile.get(sharedDevice, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false); if (null == glp) { throw new GLException("Couldn't get default GLProfile for device: "+sharedDevice); - } - final GLCapabilities caps = new GLCapabilities(glp); - caps.setRedBits(5); caps.setGreenBits(5); caps.setBlueBits(5); caps.setAlphaBits(0); - caps.setDepthBits(5); - caps.setDoubleBuffered(false); - caps.setOnscreen(false); - caps.setPBuffer(true); - final MacOSXCGLDrawable drawable = (MacOSXCGLDrawable) createGLDrawable( createOffscreenSurfaceImpl(sharedDevice, caps, caps, null, 64, 64) ); - if(null!=drawable) { - drawable.setRealized(true); - final GLContext context = drawable.createContext(null); - if (null != context) { - try { - context.makeCurrent(); // could cause exception - madeCurrent = context.isCurrent(); - if(madeCurrent) { - GL gl = context.getGL(); - hasNPOTTextures = gl.isNPOTTextureAvailable(); - hasRECTTextures = gl.isExtensionAvailable("GL_EXT_texture_rectangle"); - hasAppleFloatPixels = gl.isExtensionAvailable("GL_APPLE_float_pixels"); - } - } catch (GLException gle) { - if (DEBUG) { - System.err.println("MacOSXCGLDrawableFactory.createShared: INFO: makeCurrent catched exception:"); - gle.printStackTrace(); - } - } finally { - try { - context.destroy(); - } catch (GLException gle) { - if (DEBUG) { - System.err.println("MacOSXCGLDrawableFactory.createShared: INFO: destroy catched exception:"); - gle.printStackTrace(); - } - } + } + final GLCapabilitiesImmutable caps = new GLCapabilities(glp); + final GLDrawableImpl sharedDrawable = createOnscreenDrawableImpl(createDummySurfaceImpl(sharedDevice, false, caps, caps, null, 64, 64)); + sharedDrawable.setRealized(true); + + final MacOSXCGLContext sharedContext = (MacOSXCGLContext) sharedDrawable.createContext(null); + if (null == sharedContext) { + throw new GLException("Couldn't create shared context for drawable: "+sharedDrawable); + } + + try { + sharedContext.makeCurrent(); // could cause exception + isValid = sharedContext.isCurrent(); + if(isValid) { + GL gl = sharedContext.getGL(); + hasNPOTTextures = gl.isNPOTTextureAvailable(); + hasRECTTextures = gl.isExtensionAvailable(GLExtensions.EXT_texture_rectangle); + hasAppleFloatPixels = gl.isExtensionAvailable(GLExtensions.APPLE_float_pixels); + glRendererQuirks = sharedContext.getRendererQuirks(); + } + } catch (GLException gle) { + if (DEBUG) { + System.err.println("MacOSXCGLDrawableFactory.createShared: INFO: makeCurrent catched exception:"); + gle.printStackTrace(); + } + } finally { + try { + sharedContext.destroy(); + } catch (GLException gle) { + if (DEBUG) { + System.err.println("MacOSXCGLDrawableFactory.createShared: INFO: destroy catched exception:"); + gle.printStackTrace(); } } - drawable.destroy(); } + sharedDrawable.setRealized(false); } - sr = new SharedResource(sharedDevice, madeCurrent, hasNPOTTextures, hasRECTTextures, hasAppleFloatPixels); + sr = new SharedResource(sharedDevice, isValid, hasNPOTTextures, hasRECTTextures, hasAppleFloatPixels, glRendererQuirks); synchronized(sharedMap) { sharedMap.put(connection, sr); } removeDeviceTried(connection); if (DEBUG) { System.err.println("MacOSXCGLDrawableFactory.createShared: device: " + sharedDevice); - System.err.println("MacOSXCGLDrawableFactory.createShared: context: madeCurrent " + madeCurrent + ", NPOT "+hasNPOTTextures+ - ", RECT "+hasRECTTextures+", FloatPixels "+hasAppleFloatPixels); - } + System.err.println("MacOSXCGLDrawableFactory.createShared: context: madeCurrent " + isValid + ", NPOT "+hasNPOTTextures+ + ", RECT "+hasRECTTextures+", FloatPixels "+hasAppleFloatPixels+", "+glRendererQuirks); + } } return sr; } - + + @Override protected final Thread getSharedResourceThread() { return null; } - - protected final boolean createSharedResource(AbstractGraphicsDevice device) { - try { - SharedResource sr = getOrCreateOSXSharedResource(device); - if(null!=sr) { - return sr.wasContextAvailable(); - } - } catch (GLException gle) { - if(DEBUG) { - System.err.println("Catched Exception while MaxOSXCGL Shared Resource initialization:"); - gle.printStackTrace(); - } - } - return false; - } - - protected final GLContext getOrCreateSharedContextImpl(AbstractGraphicsDevice device) { - // FIXME: not implemented .. needs a dummy OSX surface - return null; - } - - protected AbstractGraphicsDevice getOrCreateSharedDeviceImpl(AbstractGraphicsDevice device) { - SharedResource sr = getOrCreateOSXSharedResource(device); - if(null!=sr) { - return sr.getDevice(); - } - return null; - } + @Override protected List<GLCapabilitiesImmutable> getAvailableCapabilitiesImpl(AbstractGraphicsDevice device) { return MacOSXCGLGraphicsConfiguration.getAvailableCapabilities(this, device); } + @Override protected GLDrawableImpl createOnscreenDrawableImpl(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); @@ -306,54 +320,81 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { return new MacOSXOnscreenCGLDrawable(this, target); } + @Override protected GLDrawableImpl createOffscreenDrawableImpl(NativeSurface target) { - AbstractGraphicsConfiguration config = target.getGraphicsConfiguration(); - GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final MutableGraphicsConfiguration config = (MutableGraphicsConfiguration) target.getGraphicsConfiguration(); + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); if(!caps.isPBuffer()) { + // Actual implementation is using PBuffer ... + final GLCapabilities modCaps = (GLCapabilities) caps.cloneMutable(); + modCaps.setPBuffer(true); + modCaps.setBitmap(false); + config.setChosenCapabilities(modCaps); return new MacOSXOffscreenCGLDrawable(this, target); } return new MacOSXPbufferCGLDrawable(this, target); } - public boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { - return true; + @Override + 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 + protected ProxySurface createMutableSurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstreamHook) { + final MacOSXGraphicsDevice device; + if( createNewDevice || !(deviceReq instanceof MacOSXGraphicsDevice) ) { + device = new MacOSXGraphicsDevice(deviceReq.getUnitID()); + } else { + device = (MacOSXGraphicsDevice)deviceReq; + } + final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); + final MacOSXCGLGraphicsConfiguration config = MacOSXCGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen, true); + if(null == config) { + throw new GLException("Choosing GraphicsConfiguration failed w/ "+capsChosen+" on "+screen); + } + return new WrappedSurface(config, 0, upstreamHook, createNewDevice); + } + + @Override + public final ProxySurface createDummySurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable chosenCaps, GLCapabilitiesImmutable requestedCaps, GLCapabilitiesChooser chooser, int width, int height) { + chosenCaps = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps); + return createMutableSurfaceImpl(deviceReq, createNewDevice, chosenCaps, requestedCaps, chooser, + new OSXDummyUpstreamSurfaceHook(width, height)); } - protected NativeSurface createOffscreenSurfaceImpl(AbstractGraphicsDevice device,GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, int width, int height) { - AbstractGraphicsScreen screen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_MACOSX); - WrappedSurface ns = new WrappedSurface(MacOSXCGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen, true)); - ns.surfaceSizeChanged(width, height); - return ns; + @Override + protected ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice deviceReq, int screenIdx, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream) { + final MacOSXGraphicsDevice device = new MacOSXGraphicsDevice(deviceReq.getUnitID()); + final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, screenIdx); + final MacOSXCGLGraphicsConfiguration config = MacOSXCGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen, true); + return new WrappedSurface(config, windowHandle, upstream, true); } - protected ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice device, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { - AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); - WrappedSurface ns = new WrappedSurface(MacOSXCGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen, true), windowHandle); - return ns; - } - + @Override protected GLContext createExternalGLContextImpl() { return MacOSXExternalCGLContext.create(this); } + @Override public boolean canCreateExternalGLDrawable(AbstractGraphicsDevice device) { return false; } + @Override protected GLDrawable createExternalGLDrawableImpl() { // FIXME throw new GLException("Not yet implemented"); } - public boolean canCreateContextOnJava2DSurface(AbstractGraphicsDevice device) { - return false; - } - - public GLContext createContextOnJava2DSurface(Object graphics, GLContext shareWith) - throws GLException { - throw new GLException("not supported in non AWT enviroment"); - } - //------------------------------------------------------ // Gamma-related functionality // @@ -362,21 +403,24 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { /** Returns the length of the computed gamma ramp for this OS and hardware. Returns 0 if gamma changes are not supported. */ + @Override protected int getGammaRampLength() { return GAMMA_RAMP_LENGTH; } + @Override protected boolean setGammaRamp(float[] ramp) { - return CGL.setGammaRamp(ramp.length, - ramp, 0, - ramp, 0, - ramp, 0); + final FloatBuffer rampNIO = Buffers.newDirectFloatBuffer(ramp); + + return CGL.setGammaRamp(ramp.length, rampNIO, rampNIO, rampNIO); } + @Override protected Buffer getGammaRamp() { return null; } + @Override protected void resetGammaRamp(Buffer originalGammaRamp) { CGL.resetGammaRamp(); } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java index 03ec94db6..cda8307c7 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,35 +20,35 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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"); libsGL.add("GL"); - libsList.add(libsGL); + libsList.add(libsGL); return libsList; } - + @Override public final List<String> getToolGetProcAddressFuncNameList() { - return null; + return null; /** OSX manual says: NSImage use is discouraged List res = new ArrayList(); res.add("GetProcAddress"); // dummy @@ -59,7 +59,7 @@ public class MacOSXCGLDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBu public final long toolGetProcAddress(long toolGetProcAddressHandle, String funcName) { return 0; /** OSX manual says: NSImage use is discouraged - return CGL.getProcAddress(glFuncName); // manual implementation + return CGL.getProcAddress(glFuncName); // manual implementation */ } } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfiguration.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfiguration.java index 8393a688e..481c0b94b 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfiguration.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfiguration.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,13 +29,14 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.macosx.cgl; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.List; @@ -46,40 +47,34 @@ import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; - +import com.jogamp.common.nio.Buffers; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.nativewindow.MutableGraphicsConfiguration; public class MacOSXCGLGraphicsConfiguration extends MutableGraphicsConfiguration implements Cloneable { - long pixelformat; - MacOSXCGLGraphicsConfiguration(AbstractGraphicsScreen screen, - GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, - long pixelformat) { + MacOSXCGLGraphicsConfiguration(AbstractGraphicsScreen screen, + GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested) { super(screen, capsChosen, capsRequested); - this.pixelformat=pixelformat; } + @Override public Object clone() { return super.clone(); } - void setChosenPixelFormat(long pixelformat) { - this.pixelformat=pixelformat; - } - protected static List<GLCapabilitiesImmutable> getAvailableCapabilities(MacOSXCGLDrawableFactory factory, AbstractGraphicsDevice device) { - MacOSXCGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateOSXSharedResource(device); + MacOSXCGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResourceImpl(device); if(null == sharedResource) { throw new GLException("Shared resource for device n/a: "+device); } // MacOSXGraphicsDevice osxDevice = sharedResource.getDevice(); return new ArrayList<GLCapabilitiesImmutable>(0); } - - static final int[] cglInternalAttributeToken = new int[] { + + static final IntBuffer cglInternalAttributeToken = Buffers.newDirectIntBuffer(new int[] { CGL.kCGLPFAOpenGLProfile, // >= lion - CGL.NSOpenGLPFAAccelerated, // query only (prefer accelerated, but allow non accelerated), ignored for createPixelformat + CGL.NSOpenGLPFAAccelerated, // query only (prefer accelerated, but allow non accelerated), ignored for createPixelformat CGL.NSOpenGLPFANoRecovery, CGL.kCGLPFAColorFloat, CGL.NSOpenGLPFAPixelBuffer, @@ -91,70 +86,66 @@ public class MacOSXCGLGraphicsConfiguration extends MutableGraphicsConfiguration CGL.NSOpenGLPFAAccumSize, CGL.NSOpenGLPFAStencilSize, CGL.NSOpenGLPFASampleBuffers, - CGL.NSOpenGLPFASamples }; + CGL.NSOpenGLPFASamples }); - static int[] GLCapabilities2NSAttribList(GLCapabilitiesImmutable caps, int ctp, int major, int minor) { - int len = cglInternalAttributeToken.length; - int off = 0; - if ( !MacOSXCGLContext.isLionOrLater ) { - // no OpenGLProfile - off++; - len--; - } - int[] ivalues = new int[len]; + static IntBuffer GLCapabilities2NSAttribList(AbstractGraphicsDevice device, IntBuffer attrToken, GLCapabilitiesImmutable caps, int ctp, int major, int minor) { + final int len = attrToken.remaining(); + final int off = attrToken.position(); + final IntBuffer ivalues = Buffers.newDirectIntBuffer(len); for (int idx = 0; idx < len; idx++) { - final int attr = cglInternalAttributeToken[idx+off]; + final int attr = attrToken.get(idx+off); switch (attr) { - case CGL.kCGLPFAOpenGLProfile: - ivalues[idx] = MacOSXCGLContext.GLProfile2CGLOGLProfileValue(ctp, major, minor); + case CGL.kCGLPFAOpenGLProfile: + ivalues.put(idx, MacOSXCGLContext.GLProfile2CGLOGLProfileValue(device, ctp, major, minor)); break; case CGL.NSOpenGLPFANoRecovery: - ivalues[idx] = caps.getHardwareAccelerated() ? 1 : 0; + ivalues.put(idx, caps.getHardwareAccelerated() ? 1 : 0); break; - + case CGL.kCGLPFAColorFloat: - ivalues[idx] = caps.getPbufferFloatingPointBuffers() ? 1 : 0; + // ivalues.put(idx, ( !caps.isOnscreen() && caps.isPBuffer() && caps.getPbufferFloatingPointBuffers() ) ? 1 : 0); + ivalues.put(idx, 0); break; case CGL.NSOpenGLPFAPixelBuffer: - ivalues[idx] = caps.isPBuffer() ? 1 : 0; + ivalues.put(idx, ( !caps.isOnscreen() && caps.isPBuffer() ) ? 1 : 0); break; case CGL.NSOpenGLPFADoubleBuffer: - ivalues[idx] = (caps.getDoubleBuffered() ? 1 : 0); + ivalues.put(idx, (caps.getDoubleBuffered() ? 1 : 0)); break; case CGL.NSOpenGLPFAStereo: - ivalues[idx] = (caps.getStereo() ? 1 : 0); + ivalues.put(idx, (caps.getStereo() ? 1 : 0)); break; case CGL.NSOpenGLPFAColorSize: - ivalues[idx] = (caps.getRedBits() + caps.getGreenBits() + caps.getBlueBits()); + ivalues.put(idx, (caps.getRedBits() + caps.getGreenBits() + caps.getBlueBits())); break; case CGL.NSOpenGLPFAAlphaSize: - ivalues[idx] = caps.getAlphaBits(); + ivalues.put(idx, caps.getAlphaBits()); break; case CGL.NSOpenGLPFADepthSize: - ivalues[idx] = caps.getDepthBits(); + ivalues.put(idx, caps.getDepthBits()); break; case CGL.NSOpenGLPFAAccumSize: - ivalues[idx] = (caps.getAccumRedBits() + caps.getAccumGreenBits() + caps.getAccumBlueBits() + caps.getAccumAlphaBits()); + ivalues.put(idx, (caps.getAccumRedBits() + caps.getAccumGreenBits() + caps.getAccumBlueBits() + caps.getAccumAlphaBits())); break; case CGL.NSOpenGLPFAStencilSize: - ivalues[idx] = caps.getStencilBits(); + ivalues.put(idx, caps.getStencilBits()); break; case CGL.NSOpenGLPFASampleBuffers: - ivalues[idx] = caps.getSampleBuffers() ? 1 : 0; + ivalues.put(idx, caps.getSampleBuffers() ? 1 : 0); break; case CGL.NSOpenGLPFASamples: - ivalues[idx] = caps.getSampleBuffers() ? ivalues[idx] = caps.getNumSamples() : 0; + ivalues.put(idx, caps.getNumSamples()); break; default: @@ -164,152 +155,158 @@ public class MacOSXCGLGraphicsConfiguration extends MutableGraphicsConfiguration return ivalues; } - static long GLCapabilities2NSPixelFormat(GLCapabilitiesImmutable caps, int ctp, int major, int minor) { - int len = cglInternalAttributeToken.length; - int off = 0; + static long GLCapabilities2NSPixelFormat(AbstractGraphicsDevice device, GLCapabilitiesImmutable caps, int ctp, int major, int minor) { + final IntBuffer attrToken = cglInternalAttributeToken.duplicate(); if ( !MacOSXCGLContext.isLionOrLater ) { // no OpenGLProfile - off++; - len--; - } - int[] ivalues = GLCapabilities2NSAttribList(caps, ctp, major, minor); - return CGL.createPixelFormat(cglInternalAttributeToken, off, len, ivalues, 0); + attrToken.position(1); + } + final IntBuffer ivalues = GLCapabilities2NSAttribList(device, attrToken, caps, ctp, major, minor); + return CGL.createPixelFormat(attrToken, attrToken.remaining(), ivalues); } - static GLCapabilitiesImmutable NSPixelFormat2GLCapabilities(GLProfile glp, long pixelFormat) { + static GLCapabilities NSPixelFormat2GLCapabilities(GLProfile glp, long pixelFormat) { return PixelFormat2GLCapabilities(glp, pixelFormat, true); } - static long GLCapabilities2CGLPixelFormat(GLCapabilitiesImmutable caps, int ctp, int major, int minor) { + static long GLCapabilities2CGLPixelFormat(AbstractGraphicsDevice device, GLCapabilitiesImmutable caps, int ctp, int major, int minor) { // Set up pixel format attributes - int[] attrs = new int[256]; + final IntBuffer attrs = Buffers.newDirectIntBuffer(256); int i = 0; if(MacOSXCGLContext.isLionOrLater) { - attrs[i++] = CGL.kCGLPFAOpenGLProfile; - attrs[i++] = MacOSXCGLContext.GLProfile2CGLOGLProfileValue(ctp, major, minor); - } - if(caps.isPBuffer()) { - attrs[i++] = CGL.kCGLPFAPBuffer; - } - if (caps.getPbufferFloatingPointBuffers()) { - attrs[i++] = CGL.kCGLPFAColorFloat; + attrs.put(i++, CGL.kCGLPFAOpenGLProfile); + attrs.put(i++, MacOSXCGLContext.GLProfile2CGLOGLProfileValue(device, ctp, major, minor)); } + /** + if(!caps.isOnscreen() && caps.isPBuffer()) { + attrs.put(i++, CGL.kCGLPFAPBuffer); + if (caps.getPbufferFloatingPointBuffers()) { + attrs.put(i++, CGL.kCGLPFAColorFloat); + } + } */ if (caps.getDoubleBuffered()) { - attrs[i++] = CGL.kCGLPFADoubleBuffer; + attrs.put(i++, CGL.kCGLPFADoubleBuffer); } if (caps.getStereo()) { - attrs[i++] = CGL.kCGLPFAStereo; + attrs.put(i++, CGL.kCGLPFAStereo); } - attrs[i++] = CGL.kCGLPFAColorSize; - attrs[i++] = (caps.getRedBits() + - caps.getGreenBits() + - caps.getBlueBits()); - attrs[i++] = CGL.kCGLPFAAlphaSize; - attrs[i++] = caps.getAlphaBits(); - attrs[i++] = CGL.kCGLPFADepthSize; - attrs[i++] = caps.getDepthBits(); + attrs.put(i++, CGL.kCGLPFAColorSize); + attrs.put(i++, ( caps.getRedBits() + + caps.getGreenBits() + + caps.getBlueBits() ) ); + attrs.put(i++, CGL.kCGLPFAAlphaSize); + attrs.put(i++, caps.getAlphaBits()); + attrs.put(i++, CGL.kCGLPFADepthSize); + attrs.put(i++, caps.getDepthBits()); // FIXME: should validate stencil size as is done in MacOSXWindowSystemInterface.m - attrs[i++] = CGL.kCGLPFAStencilSize; - attrs[i++] = caps.getStencilBits(); - attrs[i++] = CGL.kCGLPFAAccumSize; - attrs[i++] = (caps.getAccumRedBits() + - caps.getAccumGreenBits() + - caps.getAccumBlueBits() + - caps.getAccumAlphaBits()); + attrs.put(i++, CGL.kCGLPFAStencilSize); + attrs.put(i++, caps.getStencilBits()); + attrs.put(i++, CGL.kCGLPFAAccumSize); + attrs.put(i++, ( caps.getAccumRedBits() + + caps.getAccumGreenBits() + + caps.getAccumBlueBits() + + caps.getAccumAlphaBits() ) ); if (caps.getSampleBuffers()) { - attrs[i++] = CGL.kCGLPFASampleBuffers; - attrs[i++] = 1; - attrs[i++] = CGL.kCGLPFASamples; - attrs[i++] = caps.getNumSamples(); + attrs.put(i++, CGL.kCGLPFASampleBuffers); + attrs.put(i++, 1); + attrs.put(i++, CGL.kCGLPFASamples); + attrs.put(i++, caps.getNumSamples()); } // Use attribute array to select pixel format PointerBuffer fmt = PointerBuffer.allocateDirect(1); - int[] numScreens = new int[1]; - int res = CGL.CGLChoosePixelFormat(attrs, 0, fmt, numScreens, 0); + IntBuffer numScreens = Buffers.newDirectIntBuffer(1); + int res = CGL.CGLChoosePixelFormat(attrs, fmt, numScreens); if (res != CGL.kCGLNoError) { throw new GLException("Error code " + res + " while choosing pixel format"); } return fmt.get(0); } - - static GLCapabilitiesImmutable CGLPixelFormat2GLCapabilities(long pixelFormat) { + + static GLCapabilities CGLPixelFormat2GLCapabilities(long pixelFormat) { return PixelFormat2GLCapabilities(null, pixelFormat, false); } - private static GLCapabilitiesImmutable PixelFormat2GLCapabilities(GLProfile glp, long pixelFormat, boolean nsUsage) { - int len = cglInternalAttributeToken.length; - int off = 0; + private static GLCapabilities PixelFormat2GLCapabilities(GLProfile glp, long pixelFormat, boolean nsUsage) { + final IntBuffer attrToken = cglInternalAttributeToken.duplicate(); + final int off; if ( !MacOSXCGLContext.isLionOrLater ) { // no OpenGLProfile - off++; - len--; - } - int[] ivalues = new int[len]; + off = 1; + } else { + off = 0; + } + attrToken.position(off); + final int len = attrToken.remaining(); + final IntBuffer ivalues = Buffers.newDirectIntBuffer(len); // On this platform the pixel format is associated with the // context and not the drawable. However it's a reasonable // approximation to just store the chosen pixel format up in the - // NativeSurface's AbstractGraphicsConfiguration, + // NativeSurface's AbstractGraphicsConfiguration, // since the public API doesn't provide for a different GLCapabilities per context. // Note: These restrictions of the platform's API might be considered as a bug anyways. // Figure out what attributes we really got if(nsUsage) { - CGL.queryPixelFormat(pixelFormat, cglInternalAttributeToken, off, len, ivalues, 0); + CGL.queryPixelFormat(pixelFormat, attrToken, len, ivalues); } else { - CGL.CGLQueryPixelFormat(pixelFormat, cglInternalAttributeToken, off, len, ivalues, 0); + CGL.CGLQueryPixelFormat(pixelFormat, attrToken, len, ivalues); } + if(null == glp && MacOSXCGLContext.isLionOrLater) { // pre-scan for OpenGL Profile for (int i = 0; i < len; i++) { - if(CGL.kCGLPFAOpenGLProfile == cglInternalAttributeToken[i+off]) { - switch(ivalues[i]) { - case CGL.kCGLOGLPVersion_3_2_Core: + final int ivalue = ivalues.get(i); + if(CGL.kCGLPFAOpenGLProfile == attrToken.get(i+off)) { + switch(ivalue) { + case CGL.kCGLOGLPVersion_GL4_Core: + glp = GLProfile.get(GLProfile.GL4); + break; + case CGL.kCGLOGLPVersion_GL3_Core: glp = GLProfile.get(GLProfile.GL3); break; case CGL.kCGLOGLPVersion_Legacy: glp = GLProfile.get(GLProfile.GL2); - break; + break; default: - throw new RuntimeException("Unhandled OSX OpenGL Profile: 0x"+Integer.toHexString(ivalues[i])); + throw new RuntimeException("Unhandled OSX OpenGL Profile: 0x"+Integer.toHexString(ivalue)); } - } + } } } if(null == glp) { glp = GLProfile.get(GLProfile.GL2); } - GLCapabilities caps = new GLCapabilities(glp); + final GLCapabilities caps = new GLCapabilities(glp); + int alphaBits = 0; for (int i = 0; i < len; i++) { - int attr = cglInternalAttributeToken[i+off]; + final int attr = attrToken.get(i+off); + final int ivalue = ivalues.get(i); switch (attr) { case CGL.NSOpenGLPFAAccelerated: - caps.setHardwareAccelerated(ivalues[i] != 0); + caps.setHardwareAccelerated(ivalue != 0); break; - + case CGL.kCGLPFAColorFloat: - caps.setPbufferFloatingPointBuffers(ivalues[i] != 0); + // caps.setPbufferFloatingPointBuffers(ivalue != 0); break; case CGL.NSOpenGLPFAPixelBuffer: - caps.setPBuffer(ivalues[i] != 0); + caps.setPBuffer(ivalue != 0); break; case CGL.NSOpenGLPFADoubleBuffer: - caps.setDoubleBuffered(ivalues[i] != 0); + caps.setDoubleBuffered(ivalue != 0); break; case CGL.NSOpenGLPFAStereo: - caps.setStereo(ivalues[i] != 0); + caps.setStereo(ivalue != 0); break; case CGL.NSOpenGLPFAColorSize: { - int bitSize = ivalues[i]; - if (bitSize == 32) - bitSize = 24; - bitSize /= 3; + final int bitSize = ( 32 == ivalue ? 24 : ivalue ) / 3; caps.setRedBits(bitSize); caps.setGreenBits(bitSize); caps.setBlueBits(bitSize); @@ -317,16 +314,17 @@ public class MacOSXCGLGraphicsConfiguration extends MutableGraphicsConfiguration break; case CGL.NSOpenGLPFAAlphaSize: - caps.setAlphaBits(ivalues[i]); + // ALPHA shall be set at last - due to it's auto setting by !opaque / samples + alphaBits = ivalue; break; case CGL.NSOpenGLPFADepthSize: - caps.setDepthBits(ivalues[i]); + caps.setDepthBits(ivalue); break; case CGL.NSOpenGLPFAAccumSize: { - int bitSize = ivalues[i] / 4; + final int bitSize = ivalue / 4; caps.setAccumRedBits(bitSize); caps.setAccumGreenBits(bitSize); caps.setAccumBlueBits(bitSize); @@ -335,21 +333,22 @@ public class MacOSXCGLGraphicsConfiguration extends MutableGraphicsConfiguration break; case CGL.NSOpenGLPFAStencilSize: - caps.setStencilBits(ivalues[i]); + caps.setStencilBits(ivalue); break; case CGL.NSOpenGLPFASampleBuffers: - caps.setSampleBuffers(ivalues[i] != 0); + caps.setSampleBuffers(ivalue != 0); break; case CGL.NSOpenGLPFASamples: - caps.setNumSamples(ivalues[i]); + caps.setNumSamples(ivalue); break; default: break; } } + caps.setAlphaBits(alphaBits); return caps; } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfigurationFactory.java index 1a9070aef..db2a1df68 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -34,13 +34,17 @@ package jogamp.opengl.macosx.cgl; import jogamp.opengl.GLGraphicsConfigurationFactory; +import jogamp.opengl.GLGraphicsConfigurationUtil; + import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLDrawableFactory; /** Subclass of GraphicsConfigurationFactory used when non-AWT tookits @@ -50,21 +54,16 @@ import javax.media.opengl.GLCapabilitiesImmutable; public class MacOSXCGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFactory { static void registerFactory() { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice.class, new MacOSXCGLGraphicsConfigurationFactory()); + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice.class, GLCapabilitiesImmutable.class, new MacOSXCGLGraphicsConfigurationFactory()); } - private MacOSXCGLGraphicsConfigurationFactory() { + private MacOSXCGLGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { - return chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, absScreen, false); - } + CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { - static MacOSXCGLGraphicsConfiguration chooseGraphicsConfigurationStatic(CapabilitiesImmutable capsChosen, - CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, - AbstractGraphicsScreen absScreen, boolean usePBuffer) { if (absScreen == null) { throw new IllegalArgumentException("AbstractGraphicsScreen is null"); } @@ -77,11 +76,23 @@ public class MacOSXCGLGraphicsConfigurationFactory extends GLGraphicsConfigurati throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilities objects - requested"); } - if (chooser != null && - !(chooser instanceof GLCapabilitiesChooser)) { + if (chooser != null && !(chooser instanceof GLCapabilitiesChooser)) { throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilitiesChooser objects"); } - return new MacOSXCGLGraphicsConfiguration(absScreen, (GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested, 0); + return chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested, (GLCapabilitiesChooser)chooser, absScreen, false); + } + + static MacOSXCGLGraphicsConfiguration chooseGraphicsConfigurationStatic(GLCapabilitiesImmutable capsChosen, + GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, + AbstractGraphicsScreen absScreen, boolean usePBuffer) { + if (absScreen == null) { + throw new IllegalArgumentException("AbstractGraphicsScreen is null"); + } + final AbstractGraphicsDevice device = absScreen.getDevice(); + capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, GLDrawableFactory.getDesktopFactory(), device); + + return new MacOSXCGLGraphicsConfiguration(absScreen, (GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested); } } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXExternalCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXExternalCGLContext.java index 58cea4ade..ebb0fc6d1 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXExternalCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXExternalCGLContext.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2005 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -49,23 +49,21 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; -import com.jogamp.nativewindow.WrappedSurface; - -import jogamp.opengl.GLContextImpl; +import jogamp.nativewindow.WrappedSurface; import jogamp.opengl.GLContextShareSet; import jogamp.opengl.macosx.cgl.MacOSXCGLDrawable.GLBackendType; public class MacOSXExternalCGLContext extends MacOSXCGLContext { - private GLContext lastContext; private MacOSXExternalCGLContext(Drawable drawable, boolean isNSContext, long handle) { super(drawable, null); setOpenGLMode(isNSContext ? GLBackendType.NSOPENGL : GLBackendType.CGL ); - drawable.registerContext(this); this.contextHandle = handle; GLContextShareSet.contextCreated(this); - setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_COMPAT); + if( !setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, false /* withinGLVersionsMapping */) ) { // use GL_VERSION + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } getGLStateTracker().setEnabled(false); // external context usage can't track state in Java } @@ -108,44 +106,30 @@ public class MacOSXExternalCGLContext extends MacOSXCGLContext { } AbstractGraphicsScreen aScreen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_MACOSX); - MacOSXCGLGraphicsConfiguration cfg = new MacOSXCGLGraphicsConfiguration(aScreen, caps, caps, pixelFormat); + MacOSXCGLGraphicsConfiguration cfg = new MacOSXCGLGraphicsConfiguration(aScreen, caps, caps); if(0 == currentDrawable) { // set a fake marker stating a valid drawable - currentDrawable = 1; + currentDrawable = 1; } - WrappedSurface ns = new WrappedSurface(cfg); - ns.setSurfaceHandle(currentDrawable); + WrappedSurface ns = new WrappedSurface(cfg, currentDrawable, 64, 64, true); return new MacOSXExternalCGLContext(new Drawable(factory, ns), isNSContext, contextHandle); } - protected boolean createImpl(GLContextImpl shareWith) throws GLException { + @Override + protected boolean createImpl(final long shareWithHandle) throws GLException { return true; } - public int makeCurrent() throws GLException { - // Save last context if necessary to allow external GLContexts to - // talk to other GLContexts created by this library - GLContext cur = getCurrent(); - if (cur != null && cur != this) { - lastContext = cur; - setCurrent(null); - } - return super.makeCurrent(); - } - - public void release() throws GLException { - super.release(); - setCurrent(lastContext); - lastContext = null; - } - + @Override protected void makeCurrentImpl() throws GLException { } + @Override protected void releaseImpl() throws GLException { } + @Override protected void destroyImpl() throws GLException { } @@ -155,14 +139,17 @@ public class MacOSXExternalCGLContext extends MacOSXCGLContext { super(factory, comp, true); } + @Override public GLContext createContext(GLContext shareWith) { throw new GLException("Should not call this"); } + @Override public int getWidth() { throw new GLException("Should not call this"); } + @Override public int getHeight() { throw new GLException("Should not call this"); } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLContext.java deleted file mode 100644 index 949963fa0..000000000 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLContext.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.macosx.cgl; - -import javax.media.opengl.*; -import jogamp.opengl.*; - -public class MacOSXOffscreenCGLContext extends MacOSXPbufferCGLContext -{ - public MacOSXOffscreenCGLContext(MacOSXPbufferCGLDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } - - public int getOffscreenContextPixelDataType() { - GL gl = getGL(); - return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; - } - - public int getOffscreenContextReadBuffer() { - return GL.GL_FRONT; - } - - public boolean offscreenImageNeedsVerticalFlip() { - return true; - } -} diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLDrawable.java index f81cd725e..446a834b9 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOffscreenCGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -51,7 +51,8 @@ public class MacOSXOffscreenCGLDrawable extends MacOSXPbufferCGLDrawable { super(factory, target); } + @Override public GLContext createContext(GLContext shareWith) { - return new MacOSXOffscreenCGLContext(this, shareWith); + return new MacOSXCGLContext(this, shareWith); } } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLContext.java deleted file mode 100644 index 9e051311c..000000000 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLContext.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.macosx.cgl; - -import javax.media.opengl.GLContext; -import javax.media.opengl.GLException; - -import jogamp.opengl.GLContextImpl; - -public class MacOSXOnscreenCGLContext extends MacOSXCGLContext { - - public MacOSXOnscreenCGLContext(MacOSXOnscreenCGLDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } - - @Override - protected void makeCurrentImpl() throws GLException { - super.makeCurrentImpl(); - drawableUpdatedNotify(); - } - - @Override - protected void drawableUpdatedNotify() throws GLException { - final int w = drawable.getWidth(); - final int h = drawable.getHeight(); - final boolean updateContext = ( 0!=updateHandle && CGL.updateContextNeedsUpdate(updateHandle) ) || - w != lastWidth || h != lastHeight; - if(updateContext) { - lastWidth = w; - lastHeight = h; - if (contextHandle == 0) { - throw new GLException("Context not created"); - } - CGL.updateContext(contextHandle); - } - } - - @Override - protected boolean createImpl(GLContextImpl sharedWith) { - boolean res = super.createImpl(sharedWith); - lastWidth = -1; - lastHeight = -1; - if(res && isNSContext()) { - if(0 != updateHandle) { - throw new InternalError("XXX1"); - } - updateHandle = CGL.updateContextRegister(contextHandle, drawable.getHandle()); - if(0 == updateHandle) { - throw new InternalError("XXX2"); - } - } - return res; - } - - @Override - protected void destroyImpl() throws GLException { - if ( 0 != updateHandle ) { - CGL.updateContextUnregister(updateHandle); - updateHandle = 0; - } - super.destroyImpl(); - } - - private long updateHandle = 0; - private int lastWidth, lastHeight; -} diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLDrawable.java index 80c54042f..c6f0c1383 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXOnscreenCGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -45,15 +45,14 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; public class MacOSXOnscreenCGLDrawable extends MacOSXCGLDrawable { - + protected MacOSXOnscreenCGLDrawable(GLDrawableFactory factory, NativeSurface component) { super(factory, component, false); } + @Override public GLContext createContext(GLContext shareWith) { - final MacOSXOnscreenCGLContext ctx= new MacOSXOnscreenCGLContext(this, shareWith); - registerContext(ctx); - return ctx; + return new MacOSXCGLContext(this, shareWith); } } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java deleted file mode 100644 index 7ba7d2d5a..000000000 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLContext.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - */ - -package jogamp.opengl.macosx.cgl; - -import javax.media.opengl.GL; -import javax.media.opengl.GLContext; -import javax.media.opengl.GLException; -import javax.media.opengl.GLPbuffer; - -import jogamp.opengl.GLContextImpl; - -public class MacOSXPbufferCGLContext extends MacOSXCGLContext { - - // State for render-to-texture and render-to-texture-rectangle support - private int texture; // actual texture object - - public MacOSXPbufferCGLContext(MacOSXPbufferCGLDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } - - public void bindPbufferToTexture() { - GL gl = getGL(); - gl.glBindTexture(((MacOSXPbufferCGLDrawable)drawable).getTextureTarget(), texture); - // FIXME: not clear whether this is really necessary, but since - // the API docs seem to imply it is and since it doesn't seem to - // impact performance, leaving it in - CGL.setContextTextureImageToPBuffer(contextHandle, drawable.getHandle(), GL.GL_FRONT); - } - - public void releasePbufferFromTexture() { - } - - protected boolean createImpl(GLContextImpl shareWith) { - boolean res = super.createImpl(shareWith); - if(res) { - // Initialize render-to-texture support if requested - final GL gl = getGL(); - final MacOSXPbufferCGLDrawable osxPDrawable = (MacOSXPbufferCGLDrawable)drawable; - final int textureTarget = osxPDrawable.getTextureTarget(); - - int[] tmp = new int[1]; - gl.glGenTextures(1, tmp, 0); - texture = tmp[0]; - gl.glBindTexture(textureTarget, texture); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); - gl.glTexImage2D(textureTarget, 0, GL.GL_RGB, osxPDrawable.getTextureWidth(), osxPDrawable.getTextureHeight(), - 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, null); - gl.glCopyTexSubImage2D(textureTarget, 0, 0, 0, 0, 0, drawable.getWidth(), drawable.getHeight()); - } - return res; - } - - public int getFloatingPointMode() { - return GLPbuffer.APPLE_FLOAT; - } -} diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java index 1e49d1fc9..f6e8b8fa3 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXPbufferCGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,20 +29,22 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.macosx.cgl; +import java.lang.ref.WeakReference; + import javax.media.nativewindow.DefaultGraphicsConfiguration; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.SurfaceChangeable; +import javax.media.nativewindow.MutableSurface; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GLCapabilitiesImmutable; @@ -54,7 +56,7 @@ import javax.media.opengl.GLProfile; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.opengl.util.GLBuffers; -public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { +public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { // Abstract interface for implementation of this drawable (either // NSOpenGL-based or CGL-based) interface GLBackendImpl { @@ -64,24 +66,18 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { // Implementation object (either NSOpenGL-based or CGL-based) protected GLBackendImpl impl; - + // State for render-to-texture and render-to-texture-rectangle support // private int textureTarget; // e.g. GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_NV // private int texture; // actual texture object - // Note that we can not store this in the NativeSurface because the - // semantic is that contains an NSView - protected long pBuffer; protected int pBufferTexTarget, pBufferTexWidth, pBufferTexHeight; public MacOSXPbufferCGLDrawable(GLDrawableFactory factory, NativeSurface target) { super(factory, target, false); } - protected void destroyImpl() { - setRealized(false); - } - + @Override protected void setRealizedImpl() { if(realized) { createPbuffer(); @@ -90,45 +86,45 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { } } + @Override public GLContext createContext(GLContext shareWith) { - final MacOSXPbufferCGLContext ctx = new MacOSXPbufferCGLContext(this, shareWith); - registerContext(ctx); - return ctx; + return new MacOSXCGLContext(this, shareWith); } - @Override - protected long getNSViewHandle() { - // pbuffer handle is NSOpenGLPixelBuffer - return 0; - } - - @Override - public long getHandle() { - return pBuffer; - } - protected int getTextureTarget() { return pBufferTexTarget; } protected int getTextureWidth() { return pBufferTexWidth; } protected int getTextureHeight() { return pBufferTexHeight; } - + protected void destroyPbuffer() { - if (this.pBuffer != 0) { - NativeSurface ns = getNativeSurface(); + final MutableSurface ms = (MutableSurface) getNativeSurface(); + final long pBuffer = ms.getSurfaceHandle(); + if (0 != pBuffer) { + synchronized (createdContexts) { + for(int i=0; i<createdContexts.size(); ) { + final WeakReference<MacOSXCGLContext> ref = createdContexts.get(i); + final MacOSXCGLContext ctx = ref.get(); + if (ctx != null) { + ctx.detachPBuffer(); + i++; + } else { + createdContexts.remove(i); + } + } + } impl.destroy(pBuffer); - this.pBuffer = 0; - ((SurfaceChangeable)ns).setSurfaceHandle(0); + ms.setSurfaceHandle(0); } } private void createPbuffer() { - final NativeSurface ns = getNativeSurface(); - final DefaultGraphicsConfiguration config = (DefaultGraphicsConfiguration) ns.getGraphicsConfiguration(); + final MutableSurface ms = (MutableSurface) getNativeSurface(); + final DefaultGraphicsConfiguration config = (DefaultGraphicsConfiguration) ms.getGraphicsConfiguration(); final GLCapabilitiesImmutable capabilities = (GLCapabilitiesImmutable)config.getChosenCapabilities(); final GLProfile glProfile = capabilities.getGLProfile(); - MacOSXCGLDrawableFactory.SharedResource sr = ((MacOSXCGLDrawableFactory)factory).getOrCreateOSXSharedResource(config.getScreen().getDevice()); - + MacOSXCGLDrawableFactory.SharedResource sr = ((MacOSXCGLDrawableFactory)factory).getOrCreateSharedResourceImpl(config.getScreen().getDevice()); + if (DEBUG) { - System.out.println("Pbuffer config: " + config); + System.out.println(getThreadName()+": Pbuffer config: " + config); if(null != sr) { System.out.println("Pbuffer NPOT Texure avail: "+sr.isNPOTTextureAvailable()); System.out.println("Pbuffer RECT Texture avail: "+sr.isRECTTextureAvailable()); @@ -137,12 +133,8 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { } } - if ( capabilities.getPbufferRenderToTextureRectangle() && null!=sr && sr.isRECTTextureAvailable() ) { - pBufferTexTarget = GL2.GL_TEXTURE_RECTANGLE; - } else { - pBufferTexTarget = GL.GL_TEXTURE_2D; - } - if ( GL2.GL_TEXTURE_RECTANGLE == pBufferTexTarget || ( null!=sr && sr.isNPOTTextureAvailable() ) ) { + pBufferTexTarget = GL.GL_TEXTURE_2D; + if ( null!=sr && sr.isNPOTTextureAvailable() ) { pBufferTexWidth = getWidth(); pBufferTexHeight = getHeight(); } else { @@ -150,19 +142,8 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { pBufferTexHeight = GLBuffers.getNextPowerOf2(getHeight()); } - int internalFormat = GL.GL_RGBA; - if (capabilities.getPbufferFloatingPointBuffers()) { - if(!glProfile.isGL2GL3() || null==sr || sr.isAppleFloatPixelsAvailable()) { - throw new GLException("Floating-point support (GL_APPLE_float_pixels) not available"); - } - switch (capabilities.getRedBits()) { - case 16: internalFormat = GL2.GL_RGBA_FLOAT16_APPLE; break; - case 32: internalFormat = GL2.GL_RGBA_FLOAT32_APPLE; break; - default: throw new GLException("Invalid floating-point bit depth (only 16 and 32 supported)"); - } - } - - pBuffer = impl.create(pBufferTexTarget, internalFormat, getWidth(), getHeight()); + final int internalFormat = GL.GL_RGBA; + final long pBuffer = impl.create(pBufferTexTarget, internalFormat, getWidth(), getHeight()); if(DEBUG) { System.err.println("MacOSXPbufferCGLDrawable tex: target "+toHexString(pBufferTexTarget)+ ", pbufferSize "+getWidth()+"x"+getHeight()+ @@ -175,14 +156,16 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { throw new GLException("pbuffer creation error: CGL.createPBuffer() failed"); } - ((SurfaceChangeable)ns).setSurfaceHandle(pBuffer); + ms.setSurfaceHandle(pBuffer); } + @Override public void setOpenGLMode(GLBackendType mode) { super.setOpenGLMode(mode); createPbuffer(); // recreate } + @Override protected void initOpenGLImpl(GLBackendType backend) { switch (backend) { case NSOPENGL: @@ -194,14 +177,16 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { default: throw new InternalError("Illegal implementation mode " + backend); } - } - + } + // NSOpenGLPixelBuffer implementation class NSOpenGLImpl implements GLBackendImpl { + @Override public long create(int renderTarget, int internalFormat, int width, int height) { return CGL.createPBuffer(renderTarget, internalFormat, width, height); } + @Override public void destroy(long pbuffer) { CGL.destroyPBuffer(pbuffer); } @@ -209,6 +194,7 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { // CGL implementation class CGLImpl implements GLBackendImpl { + @Override public long create(int renderTarget, int internalFormat, int width, int height) { PointerBuffer pbuffer = PointerBuffer.allocateDirect(1); int res = CGL.CGLCreatePBuffer(width, height, renderTarget, internalFormat, 0, pbuffer); @@ -218,12 +204,13 @@ public class MacOSXPbufferCGLDrawable extends MacOSXCGLDrawable { return pbuffer.get(0); } + @Override public void destroy(long pbuffer) { int res = CGL.CGLDestroyPBuffer(pbuffer); if (res != CGL.kCGLNoError) { throw new GLException("Error destroying CGL-based pbuffer: error code " + res); } } - } - + } + } diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLDrawableFactory.java deleted file mode 100644 index fe60710f0..000000000 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLDrawableFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.macosx.cgl.awt; - -import javax.media.nativewindow.*; -import javax.media.opengl.*; -import jogamp.opengl.macosx.cgl.*; - -public class MacOSXAWTCGLDrawableFactory extends MacOSXCGLDrawableFactory { - - public MacOSXAWTCGLDrawableFactory() { - super(); - } - - public boolean canCreateContextOnJava2DSurface(AbstractGraphicsDevice device) { - return true; - } - - public GLContext createContextOnJava2DSurface(Object graphics, GLContext shareWith) - throws GLException { - return new MacOSXJava2DCGLContext(shareWith); - } -} diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLGraphicsConfigurationFactory.java index a6fa01bad..21923531f 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXAWTCGLGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -58,14 +58,15 @@ import jogamp.opengl.macosx.cgl.MacOSXCGLGraphicsConfiguration; public class MacOSXAWTCGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFactory { public static void registerFactory() { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.awt.AWTGraphicsDevice.class, new MacOSXAWTCGLGraphicsConfigurationFactory()); - } + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.awt.AWTGraphicsDevice.class, GLCapabilitiesImmutable.class, new MacOSXAWTCGLGraphicsConfigurationFactory()); + } private MacOSXAWTCGLGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { + CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { GraphicsDevice device = null; if (absScreen != null && !(absScreen instanceof AWTGraphicsScreen)) { @@ -103,9 +104,9 @@ public class MacOSXAWTCGLGraphicsConfigurationFactory extends GLGraphicsConfigur GraphicsConfiguration gc = device.getDefaultConfiguration(); MacOSXCGLGraphicsConfiguration macConfig = (MacOSXCGLGraphicsConfiguration) - GraphicsConfigurationFactory.getFactory(macDevice).chooseGraphicsConfiguration(capsChosen, + GraphicsConfigurationFactory.getFactory(macDevice, capsChosen).chooseGraphicsConfiguration(capsChosen, capsRequested, - chooser, macScreen); + chooser, macScreen, nativeVisualID); if (macConfig == null) { throw new GLException("Unable to choose a GraphicsConfiguration: "+capsChosen+",\n\t"+chooser+"\n\t"+macScreen); diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXJava2DCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXJava2DCGLContext.java deleted file mode 100644 index f41400d83..000000000 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/awt/MacOSXJava2DCGLContext.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.macosx.cgl.awt; - -import java.awt.Graphics; - -import javax.media.opengl.GLContext; -import javax.media.opengl.GLException; - -import jogamp.opengl.GLContextImpl; -import jogamp.opengl.awt.Java2D; -import jogamp.opengl.awt.Java2DGLContext; -import jogamp.opengl.macosx.cgl.MacOSXCGLContext; -import jogamp.opengl.macosx.cgl.MacOSXCGLDrawable.GLBackendType; - - -/** MacOSXCGLContext implementation supporting the Java2D/JOGL bridge - * on Mac OS X. The external GLDrawable mechanism does not work on Mac - * OS X due to how drawables and contexts are operated upon on this - * platform, so it is necessary to supply an alternative means to - * create, make current, and destroy contexts on the Java2D "drawable" - * on the Mac platform. - */ - -public class MacOSXJava2DCGLContext extends MacOSXCGLContext implements Java2DGLContext { - private Graphics graphics; - - // FIXME: ignoring context sharing for the time being; will need to - // rethink this in particular if using FBOs to implement the - // Java2D/OpenGL pipeline on Mac OS X - - MacOSXJava2DCGLContext(GLContext shareWith) { - super(null, shareWith); - } - - public void setGraphics(Graphics g) { - this.graphics = g; - } - - protected void makeCurrentImpl() throws GLException { - if (!Java2D.makeOGLContextCurrentOnSurface(graphics, contextHandle)) { - throw new GLException("Error making context current"); - } - } - - protected boolean createImpl(GLContextImpl shareWith) { - long share = createImplPreset(shareWith); - - long ctx = Java2D.createOGLContextOnSurface(graphics, share); - if (ctx == 0) { - if(DEBUG) { - System.err.println("Error creating current: "+this); - } - return false; - } - if (!Java2D.makeOGLContextCurrentOnSurface(graphics, contextHandle)) { - Java2D.destroyOGLContext(ctx); - if(DEBUG) { - System.err.println("Error making created context current: "+this); - } - return false; - } - setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT); // use GL_VERSION - contextHandle = ctx; - return true; - } - - protected void releaseImpl() throws GLException { - // FIXME: would need another primitive in the Java2D class in - // order to implement this; hopefully should not matter for - // correctness - } - - protected void destroyImpl() throws GLException { - Java2D.destroyOGLContext(contextHandle); - } - - public void setOpenGLMode(GLBackendType mode) { - if (mode != GLBackendType.CGL) { - throw new GLException("OpenGL mode switching not supported for Java2D GLContexts"); - } - super.setOpenGLMode(mode); - } -} diff --git a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java new file mode 100644 index 000000000..eeaaa5872 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java @@ -0,0 +1,947 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.openal.av; + + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import jogamp.opengl.Debug; + +import com.jogamp.common.util.LFRingbuffer; +import com.jogamp.common.util.Ringbuffer; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.openal.AL; +import com.jogamp.openal.ALC; +import com.jogamp.openal.ALCcontext; +import com.jogamp.openal.ALCdevice; +import com.jogamp.openal.ALExt; +import com.jogamp.openal.ALFactory; +import com.jogamp.openal.util.ALHelpers; +import com.jogamp.opengl.util.av.AudioSink; + +/*** + * OpenAL Audio Sink + */ +public class ALAudioSink implements AudioSink { + + private static final String AL_SOFT_buffer_samples = "AL_SOFT_buffer_samples"; + private static final String ALC_EXT_thread_local_context = "ALC_EXT_thread_local_context"; + private static final boolean DEBUG_TRACE; + private static final ALC alc; + private static final AL al; + private static final ALExt alExt; + private static final boolean staticAvailable; + + private String deviceSpecifier; + private ALCdevice device; + private boolean hasSOFTBufferSamples; + private boolean hasALC_thread_local_context; + private AudioFormat preferredAudioFormat; + private ALCcontext context; + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + + /** Playback speed, range [0.5 - 2.0], default 1.0. */ + private float playSpeed; + private float volume = 1.0f; + + static class ALAudioFrame extends AudioFrame { + private final int alBuffer; + + ALAudioFrame(int alBuffer) { + this.alBuffer = alBuffer; + } + public ALAudioFrame(int alBuffer, int pts, int duration, int dataSize) { + super(pts, duration, dataSize); + this.alBuffer = alBuffer; + } + + /** Get this frame's OpenAL buffer name */ + public final int getALBuffer() { return alBuffer; } + + public String toString() { + return "ALAudioFrame[pts " + pts + " ms, l " + duration + " ms, " + byteSize + " bytes, buffer "+alBuffer+"]"; + } + } + + // private ALAudioFrame[] alFrames = null; + private int[] alBufferNames = null; + private int frameGrowAmount = 0; + private int frameLimit = 0; + + private Ringbuffer<ALAudioFrame> alFramesAvail = null; + private Ringbuffer<ALAudioFrame> alFramesPlaying = null; + private volatile int alBufferBytesQueued = 0; + private volatile int playingPTS = AudioFrame.INVALID_PTS; + private volatile int enqueuedFrameCount; + + private int[] alSource = null; + private AudioFormat chosenFormat; + private int alChannelLayout; + private int alSampleType; + private int alFormat; + private boolean initialized; + + private volatile boolean playRequested = false; + + static { + Debug.initSingleton(); + DEBUG_TRACE = Debug.isPropertyDefined("jogl.debug.AudioSink.trace", true); + + ALC _alc = null; + AL _al = null; + ALExt _alExt = null; + try { + _alc = ALFactory.getALC(); + _al = ALFactory.getAL(); + _alExt = ALFactory.getALExt(); + } catch(Throwable t) { + if( DEBUG ) { + System.err.println("ALAudioSink: Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + alc = _alc; + al = _al; + alExt = _alExt; + staticAvailable = null != alc && null != al && null != alExt; + } + + public ALAudioSink() { + initialized = false; + chosenFormat = null; + + if( !staticAvailable ) { + return; + } + synchronized(ALAudioSink.class) { + try { + // Get handle to default device. + device = alc.alcOpenDevice(null); + if (device == null) { + throw new RuntimeException(getThreadName()+": ALAudioSink: Error opening default OpenAL device"); + } + + // Get the device specifier. + deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER); + if (deviceSpecifier == null) { + throw new RuntimeException(getThreadName()+": ALAudioSink: Error getting specifier for default OpenAL device"); + } + + // Create audio context. + context = alc.alcCreateContext(device, null); + if (context == null) { + throw new RuntimeException(getThreadName()+": ALAudioSink: Error creating OpenAL context for "+deviceSpecifier); + } + + lockContext(); + try { + // Check for an error. + if ( alc.alcGetError(device) != ALC.ALC_NO_ERROR ) { + throw new RuntimeException(getThreadName()+": ALAudioSink: Error making OpenAL context current"); + } + + hasSOFTBufferSamples = al.alIsExtensionPresent(AL_SOFT_buffer_samples); + hasALC_thread_local_context = alc.alcIsExtensionPresent(null, ALC_EXT_thread_local_context) || + alc.alcIsExtensionPresent(device, ALC_EXT_thread_local_context) ; + preferredAudioFormat = queryPreferredAudioFormat(); + if( DEBUG ) { + System.out.println("ALAudioSink: OpenAL Extensions:"+al.alGetString(AL.AL_EXTENSIONS)); + System.out.println("ALAudioSink: Null device OpenAL Extensions:"+alc.alcGetString(null, ALC.ALC_EXTENSIONS)); + System.out.println("ALAudioSink: Device "+deviceSpecifier+" OpenAL Extensions:"+alc.alcGetString(device, ALC.ALC_EXTENSIONS)); + System.out.println("ALAudioSink: hasSOFTBufferSamples "+hasSOFTBufferSamples); + System.out.println("ALAudioSink: hasALC_thread_local_context "+hasALC_thread_local_context); + System.out.println("ALAudioSink: preferredAudioFormat "+preferredAudioFormat); + } + + // Create source + { + alSource = new int[1]; + al.alGenSources(1, alSource, 0); + final int err = al.alGetError(); + if( AL.AL_NO_ERROR != err ) { + alSource = null; + throw new RuntimeException(getThreadName()+": ALAudioSink: Error generating Source: 0x"+Integer.toHexString(err)); + } + } + + if( DEBUG ) { + System.err.println("ALAudioSink: Using device: " + deviceSpecifier); + } + initialized = true; + } finally { + unlockContext(); + } + return; + } catch ( Exception e ) { + if( DEBUG ) { + System.err.println(e.getMessage()); + } + destroy(); + } + } + } + + private final AudioFormat queryPreferredAudioFormat() { + int sampleRate = DefaultFormat.sampleRate; + final int[] value = new int[1]; + alc.alcGetIntegerv(device, ALC.ALC_FREQUENCY, 1, value, 0); + if ( alc.alcGetError(device) == ALC.ALC_NO_ERROR ) { + sampleRate = value[0]; + } + return new AudioFormat(sampleRate, DefaultFormat.sampleSize, DefaultFormat.channelCount, DefaultFormat.signed, DefaultFormat.fixedP, DefaultFormat.planar, DefaultFormat.littleEndian); + } + + private final void lockContext() { + lock.lock(); + if( hasALC_thread_local_context ) { + alExt.alcSetThreadContext(context); + } else { + alc.alcMakeContextCurrent(context); + } + final int alcErr = alc.alcGetError(null); + if( ALC.ALC_NO_ERROR != alcErr ) { + final String err = getThreadName()+": ALCError "+toHexString(alcErr)+" while makeCurrent. "+this; + System.err.println(err); + Thread.dumpStack(); + lock.unlock(); + throw new RuntimeException(err); + } + final int alErr = al.alGetError(); + if( ALC.ALC_NO_ERROR != alErr ) { + if( DEBUG ) { + System.err.println(getThreadName()+": Prev - ALError "+toHexString(alErr)+" @ makeCurrent. "+this); + Thread.dumpStack(); + } + } + } + private final void unlockContext() { + if( hasALC_thread_local_context ) { + alExt.alcSetThreadContext(null); + } else { + alc.alcMakeContextCurrent(null); + } + lock.unlock(); + } + private final void destroyContext() { + lock.lock(); + try { + if( null != context ) { + try { + alc.alcDestroyContext(context); + } catch (Throwable t) { + if( DEBUG ) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + context = null; + } + // unroll lock ! + while(lock.getHoldCount() > 1) { + lock.unlock(); + } + } finally { + lock.unlock(); + } + } + + @Override + public final String toString() { + final int alSrcName = null != alSource ? alSource[0] : 0; + final int alBuffersLen = null != alBufferNames ? alBufferNames.length : 0; + final int ctxHash = context != null ? context.hashCode() : 0; + return "ALAudioSink[init "+initialized+", playRequested "+playRequested+", device "+deviceSpecifier+", ctx "+toHexString(ctxHash)+", alSource "+alSrcName+ + ", chosen "+chosenFormat+ + ", al[chan "+ALHelpers.alChannelLayoutName(alChannelLayout)+", type "+ALHelpers.alSampleTypeName(alSampleType)+ + ", fmt "+toHexString(alFormat)+", soft "+hasSOFTBufferSamples+ + "], playSpeed "+playSpeed+", buffers[total "+alBuffersLen+", avail "+alFramesAvail.size()+", "+ + "queued["+alFramesPlaying.size()+", apts "+getPTS()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes], "+ + "queue[g "+frameGrowAmount+", l "+frameLimit+"]"; + } + + private final String shortString() { + final int alSrcName = null != alSource ? alSource[0] : 0; + final int ctxHash = context != null ? context.hashCode() : 0; + return "[ctx "+toHexString(ctxHash)+", playReq "+playRequested+", alSrc "+alSrcName+ + ", queued["+alFramesPlaying.size()+", " + alBufferBytesQueued+" bytes], "+ + "queue[g "+frameGrowAmount+", l "+frameLimit+"]"; + } + + public final String getPerfString() { + final int alBuffersLen = null != alBufferNames ? alBufferNames.length : 0; + return "Play [buffer "+alFramesPlaying.size()+"/"+alBuffersLen+", apts "+getPTS()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes]"; + } + + @Override + public final AudioFormat getPreferredFormat() { + if( !staticAvailable ) { + return null; + } + return preferredAudioFormat; + } + + @Override + public final int getMaxSupportedChannels() { + if( !staticAvailable ) { + return 0; + } + return hasSOFTBufferSamples ? 8 : 2; + } + + @Override + public final boolean isSupported(AudioFormat format) { + if( !staticAvailable ) { + return false; + } + if( format.planar || !format.littleEndian ) { + // FIXME big-endian supported w/ SOFT where it's native format! + return false; + } + final int alChannelLayout = ALHelpers.getDefaultALChannelLayout(format.channelCount); + if( AL.AL_NONE != alChannelLayout ) { + final int alSampleType = ALHelpers.getALSampleType(format.sampleSize, format.signed, format.fixedP); + if( AL.AL_NONE != alSampleType ) { + lockContext(); + try { + final int alFormat = ALHelpers.getALFormat(alChannelLayout, alSampleType, hasSOFTBufferSamples, al, alExt); + return AL.AL_NONE != alFormat; + } finally { + unlockContext(); + } + } + } + return false; + } + + @Override + public final boolean init(AudioFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { + if( !staticAvailable ) { + return false; + } + alChannelLayout = ALHelpers.getDefaultALChannelLayout(requestedFormat.channelCount); + alSampleType = ALHelpers.getALSampleType(requestedFormat.sampleSize, requestedFormat.signed, requestedFormat.fixedP); + lockContext(); + try { + if( AL.AL_NONE != alChannelLayout && AL.AL_NONE != alSampleType ) { + alFormat = ALHelpers.getALFormat(alChannelLayout, alSampleType, hasSOFTBufferSamples, al, alExt); + } else { + alFormat = AL.AL_NONE; + } + if( AL.AL_NONE == alFormat ) { + // not supported + return false; + } + // Allocate buffers + destroyBuffers(); + { + final float useFrameDuration = frameDuration > 1f ? frameDuration : AudioSink.DefaultFrameDuration; + final int initialFrameCount = requestedFormat.getFrameCount( + initialQueueSize > 0 ? initialQueueSize : AudioSink.DefaultInitialQueueSize, useFrameDuration); + // frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { + alBufferNames = new int[initialFrameCount]; + al.alGenBuffers(initialFrameCount, alBufferNames, 0); + final int err = al.alGetError(); + if( AL.AL_NO_ERROR != err ) { + alBufferNames = null; + throw new RuntimeException(getThreadName()+": ALAudioSink: Error generating Buffers: 0x"+Integer.toHexString(err)); + } + final ALAudioFrame[] alFrames = new ALAudioFrame[initialFrameCount]; + for(int i=0; i<initialFrameCount; i++) { + alFrames[i] = new ALAudioFrame(alBufferNames[i]); + } + + alFramesAvail = new LFRingbuffer<ALAudioFrame>(alFrames); + alFramesPlaying = new LFRingbuffer<ALAudioFrame>(ALAudioFrame[].class, initialFrameCount); + this.frameGrowAmount = requestedFormat.getFrameCount( + queueGrowAmount > 0 ? queueGrowAmount : AudioSink.DefaultQueueGrowAmount, useFrameDuration); + this.frameLimit = requestedFormat.getFrameCount( + queueLimit > 0 ? queueLimit : AudioSink.DefaultQueueLimitWithVideo, useFrameDuration); + if( DEBUG_TRACE ) { + alFramesAvail.dump(System.err, "Avail-init"); + alFramesPlaying.dump(System.err, "Playi-init"); + } + } + } finally { + unlockContext(); + } + + chosenFormat = requestedFormat; + return true; + } + + private static int[] concat(int[] first, int[] second) { + final int[] result = Arrays.copyOf(first, first.length + second.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + /** + private static <T> T[] concat(T[] first, T[] second) { + final T[] result = Arrays.copyOf(first, first.length + second.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } */ + + private boolean growBuffers() { + if( !alFramesAvail.isEmpty() || !alFramesPlaying.isFull() ) { + throw new InternalError("Buffers: Avail is !empty "+alFramesAvail+" or Playing is !full "+alFramesPlaying); + } + if( alFramesAvail.capacity() >= frameLimit || alFramesPlaying.capacity() >= frameLimit ) { + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink.growBuffers: Frame limit "+frameLimit+" reached: Avail "+alFramesAvail+", Playing "+alFramesPlaying); + } + return false; + } + + final int[] newALBufferNames = new int[frameGrowAmount]; + al.alGenBuffers(frameGrowAmount, newALBufferNames, 0); + final int err = al.alGetError(); + if( AL.AL_NO_ERROR != err ) { + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink.growBuffers: Error generating "+frameGrowAmount+" new Buffers: 0x"+Integer.toHexString(err)); + } + return false; + } + alBufferNames = concat(alBufferNames, newALBufferNames); + + final ALAudioFrame[] newALBuffers = new ALAudioFrame[frameGrowAmount]; + for(int i=0; i<frameGrowAmount; i++) { + newALBuffers[i] = new ALAudioFrame(newALBufferNames[i]); + } + // alFrames = concat(alFrames , newALBuffers); + + alFramesAvail.growEmptyBuffer(newALBuffers); + alFramesPlaying.growFullBuffer(frameGrowAmount); + if( alFramesAvail.isEmpty() || alFramesPlaying.isFull() ) { + throw new InternalError("Buffers: Avail is empty "+alFramesAvail+" or Playing is full "+alFramesPlaying); + } + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink: Buffer grown "+frameGrowAmount+": Avail "+alFramesAvail+", playing "+alFramesPlaying); + } + if( DEBUG_TRACE ) { + alFramesAvail.dump(System.err, "Avail-grow"); + alFramesPlaying.dump(System.err, "Playi-grow"); + } + return true; + } + + private void destroyBuffers() { + if( !staticAvailable ) { + return; + } + if( null != alBufferNames ) { + try { + al.alDeleteBuffers(alBufferNames.length, alBufferNames, 0); + } catch (Throwable t) { + if( DEBUG ) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + alFramesAvail.clear(); + alFramesAvail = null; + alFramesPlaying.clear(); + alFramesPlaying = null; + alBufferBytesQueued = 0; + // alFrames = null; + alBufferNames = null; + } + } + + @Override + public final void destroy() { + initialized = false; + if( !staticAvailable ) { + return; + } + if( null != context ) { + lockContext(); + } + try { + stopImpl(true); + if( null != alSource ) { + try { + al.alDeleteSources(1, alSource, 0); + } catch (Throwable t) { + if( DEBUG ) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + alSource = null; + } + + destroyBuffers(); + } finally { + destroyContext(); + } + if( null != device ) { + try { + alc.alcCloseDevice(device); + } catch (Throwable t) { + if( DEBUG ) { + System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage()); + t.printStackTrace(); + } + } + device = null; + } + chosenFormat = null; + } + + @Override + public final boolean isInitialized() { + return initialized; + } + + private final int dequeueBuffer(final boolean wait, final boolean ignoreBufferInconsistency) { + int alErr = AL.AL_NO_ERROR; + final int releaseBufferCount; + if( alBufferBytesQueued > 0 ) { + final int releaseBufferLimes = Math.max(1, alFramesPlaying.size() / 4 ); + final int[] val=new int[1]; + int i=0; + do { + al.alGetSourcei(alSource[0], AL.AL_BUFFERS_PROCESSED, val, 0); + alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + throw new RuntimeException(getThreadName()+": ALError "+toHexString(alErr)+" while quering processed buffers at source. "+this); + } + if( wait && val[0] < releaseBufferLimes ) { + i++; + // clip wait at [2 .. 100] ms + final int avgBufferDura = chosenFormat.getBytesDuration( alBufferBytesQueued / alFramesPlaying.size() ); + final int sleep = Math.max(2, Math.min(100, releaseBufferLimes * avgBufferDura)); + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink: Dequeue.wait["+i+"]: avgBufferDura "+avgBufferDura+", releaseBufferLimes "+releaseBufferLimes+", sleep "+sleep+" ms, playImpl "+(AL.AL_PLAYING == getSourceState(false))+", processed "+val[0]+", "+this); + } + unlockContext(); + try { + Thread.sleep( sleep - 1 ); + } catch (InterruptedException e) { + } finally { + lockContext(); + } + } + } while ( wait && val[0] < releaseBufferLimes && alBufferBytesQueued > 0 ); + releaseBufferCount = val[0]; + } else { + releaseBufferCount = 0; + } + + if( releaseBufferCount > 0 ) { + final int[] buffers = new int[releaseBufferCount]; + al.alSourceUnqueueBuffers(alSource[0], releaseBufferCount, buffers, 0); + alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + throw new RuntimeException(getThreadName()+": ALError "+toHexString(alErr)+" while dequeueing "+releaseBufferCount+" buffers. "+this); + } + for ( int i=0; i<releaseBufferCount; i++ ) { + final ALAudioFrame releasedBuffer = alFramesPlaying.get(); + if( null == releasedBuffer ) { + if( !ignoreBufferInconsistency ) { + throw new InternalError("Internal Error: "+this); + } + } else { + if(DEBUG_TRACE) { + System.err.println("< [al "+buffers[i]+", q "+releasedBuffer.alBuffer+"] <- "+shortString()+" @ "+getThreadName()); + } + if( releasedBuffer.alBuffer != buffers[i] ) { + if( !ignoreBufferInconsistency ) { + alFramesAvail.dump(System.err, "Avail-deq02-post"); + alFramesPlaying.dump(System.err, "Playi-deq02-post"); + throw new InternalError("Buffer name mismatch: dequeued: "+buffers[i]+", released "+releasedBuffer+", "+this); + } + } + } + alBufferBytesQueued -= releasedBuffer.getByteSize(); + if( !alFramesAvail.put(releasedBuffer) ) { + throw new InternalError("Internal Error: "+this); + } + if(DEBUG_TRACE) { + System.err.println("<< [al "+buffers[i]+", q "+releasedBuffer.alBuffer+"] <- "+shortString()+" @ "+getThreadName()); + } + } + } + return releaseBufferCount; + } + private final void dequeueForceAll() { + if(DEBUG_TRACE) { + System.err.println("< _FLUSH_ <- "+shortString()+" @ "+getThreadName()); + } + final int[] val=new int[1]; + al.alSourcei(alSource[0], AL.AL_BUFFER, 0); // explicit force zero buffer! + if(DEBUG_TRACE) { + al.alGetSourcei(alSource[0], AL.AL_BUFFERS_PROCESSED, val, 0); + } + final int alErr = al.alGetError(); + while ( !alFramesPlaying.isEmpty() ) { + final ALAudioFrame releasedBuffer = alFramesPlaying.get(); + if( null == releasedBuffer ) { + throw new InternalError("Internal Error: "+this); + } + alBufferBytesQueued -= releasedBuffer.getByteSize(); + if( !alFramesAvail.put(releasedBuffer) ) { + throw new InternalError("Internal Error: "+this); + } + } + alBufferBytesQueued = 0; + if(DEBUG_TRACE) { + System.err.println("<< _FLUSH_ [al "+val[0]+", err "+toHexString(alErr)+"] <- "+shortString()+" @ "+getThreadName()); + Thread.dumpStack(); + } + } + + private final int dequeueBuffer(boolean wait, int inPTS, int inDuration) { + final int dequeuedBufferCount = dequeueBuffer( wait, false /* ignoreBufferInconsistency */ ); + final ALAudioFrame currentBuffer = alFramesPlaying.peek(); + if( null != currentBuffer ) { + playingPTS = currentBuffer.getPTS(); + } else { + playingPTS = inPTS; + } + if( DEBUG ) { + if( dequeuedBufferCount > 0 ) { + System.err.println(getThreadName()+": ALAudioSink: Write "+inPTS+", "+inDuration+" ms, dequeued "+dequeuedBufferCount+", wait "+wait+", "+getPerfString()); + } + } + return dequeuedBufferCount; + } + + @Override + public final AudioFrame enqueueData(AudioDataFrame audioDataFrame) { + return enqueueData(audioDataFrame.getPTS(), audioDataFrame.getData(), audioDataFrame.getByteSize()); + } + + @Override + public final AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount) { + if( !initialized || null == chosenFormat ) { + return null; + } + final ALAudioFrame alFrame; + + // OpenAL consumes buffers in the background + // we first need to initialize the OpenAL buffers then + // start continuous playback. + lockContext(); + try { + final int duration = chosenFormat.getBytesDuration(byteCount); + final boolean dequeueDone; + if( alFramesAvail.isEmpty() ) { + // try to dequeue first + dequeueDone = dequeueBuffer(false, pts, duration) > 0; + if( alFramesAvail.isEmpty() ) { + // try to grow + growBuffers(); + } + } else { + dequeueDone = false; + } + if( !dequeueDone && alFramesPlaying.size() > 0 ) { // dequeue only possible if playing .. + final boolean wait = isPlayingImpl0() && alFramesAvail.isEmpty(); // possible if grow failed or already exceeds it's limit! + dequeueBuffer(wait, pts, duration); + } + + alFrame = alFramesAvail.get(); + if( null == alFrame ) { + alFramesAvail.dump(System.err, "Avail"); + throw new InternalError("Internal Error: avail.get null "+alFramesAvail+", "+this); + } + alFrame.setPTS(pts); + alFrame.setDuration(duration); + alFrame.setByteSize(byteCount); + if( !alFramesPlaying.put( alFrame ) ) { + throw new InternalError("Internal Error: "+this); + } + final int[] alBufferNames = new int[] { alFrame.alBuffer }; + if( hasSOFTBufferSamples ) { + final int samplesPerChannel = chosenFormat.getBytesSampleCount(byteCount) / chosenFormat.channelCount; + // final int samplesPerChannel = ALHelpers.bytesToSampleCount(byteCount, alChannelLayout, alSampleType); + alExt.alBufferSamplesSOFT(alFrame.alBuffer, chosenFormat.sampleRate, alFormat, + samplesPerChannel, alChannelLayout, alSampleType, bytes); + } else { + al.alBufferData(alFrame.alBuffer, alFormat, bytes, byteCount, chosenFormat.sampleRate); + } + + if(DEBUG_TRACE) { + System.err.println("> "+alFrame.alBuffer+" -> "+shortString()+" @ "+getThreadName()); + } + + al.alSourceQueueBuffers(alSource[0], 1, alBufferNames, 0); + final int alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + throw new RuntimeException(getThreadName()+": ALError "+toHexString(alErr)+" while queueing buffer "+toHexString(alBufferNames[0])+". "+this); + } + alBufferBytesQueued += byteCount; + enqueuedFrameCount++; + + if(DEBUG_TRACE) { + System.err.println(">> "+alFrame.alBuffer+" -> "+shortString()+" @ "+getThreadName()); + } + + playImpl(); // continue playing, fixes issue where we ran out of enqueued data! + } finally { + unlockContext(); + } + return alFrame; + } + + @Override + public final boolean isPlaying() { + if( !initialized || null == chosenFormat ) { + return false; + } + if( playRequested ) { + lockContext(); + try { + return isPlayingImpl0(); + } finally { + unlockContext(); + } + } else { + return false; + } + } + private final boolean isPlayingImpl0() { + if( playRequested ) { + return AL.AL_PLAYING == getSourceState(false); + } else { + return false; + } + } + private final int getSourceState(boolean ignoreError) { + final int[] val = new int[1]; + al.alGetSourcei(alSource[0], AL.AL_SOURCE_STATE, val, 0); + final int alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + final String msg = getThreadName()+": ALError "+toHexString(alErr)+" while querying SOURCE_STATE. "+this; + if( ignoreError ) { + if( DEBUG ) { + System.err.println(msg); + } + } else { + throw new RuntimeException(msg); + } + } + return val[0]; + } + + @Override + public final void play() { + if( !initialized || null == chosenFormat ) { + return; + } + playRequested = true; + lockContext(); + try { + playImpl(); + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink: PLAY playImpl "+(AL.AL_PLAYING == getSourceState(false))+", "+this); + } + } finally { + unlockContext(); + } + } + private final void playImpl() { + if( playRequested && AL.AL_PLAYING != getSourceState(false) ) { + al.alSourcePlay(alSource[0]); + final int alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + throw new RuntimeException(getThreadName()+": ALError "+toHexString(alErr)+" while start playing. "+this); + } + } + } + + @Override + public final void pause() { + if( !initialized || null == chosenFormat ) { + return; + } + if( playRequested ) { + lockContext(); + try { + pauseImpl(); + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink: PAUSE playImpl "+(AL.AL_PLAYING == getSourceState(false))+", "+this); + } + } finally { + unlockContext(); + } + } + } + private final void pauseImpl() { + if( isPlayingImpl0() ) { + playRequested = false; + al.alSourcePause(alSource[0]); + final int alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + throw new RuntimeException(getThreadName()+": ALError "+toHexString(alErr)+" while pausing. "+this); + } + } + } + private final void stopImpl(boolean ignoreError) { + if( AL.AL_STOPPED != getSourceState(ignoreError) ) { + playRequested = false; + al.alSourceStop(alSource[0]); + final int alErr = al.alGetError(); + if( AL.AL_NO_ERROR != alErr ) { + final String msg = "ALError "+toHexString(alErr)+" while stopping. "+this; + if( ignoreError ) { + if( DEBUG ) { + System.err.println(getThreadName()+": "+msg); + } + } else { + throw new RuntimeException(getThreadName()+": ALError "+toHexString(alErr)+" while stopping. "+this); + } + } + } + } + + @Override + public final float getPlaySpeed() { return playSpeed; } + + @Override + public final boolean setPlaySpeed(float rate) { + if( !initialized || null == chosenFormat ) { + return false; + } + lockContext(); + try { + if( Math.abs(1.0f - rate) < 0.01f ) { + rate = 1.0f; + } + if( 0.5f <= rate && rate <= 2.0f ) { // OpenAL limits + playSpeed = rate; + al.alSourcef(alSource[0], AL.AL_PITCH, playSpeed); + return true; + } + } finally { + unlockContext(); + } + return false; + } + + @Override + public final float getVolume() { + return volume; + } + + @Override + public final boolean setVolume(float v) { + if( !initialized || null == chosenFormat ) { + return false; + } + lockContext(); + try { + if( Math.abs(v) < 0.01f ) { + v = 0.0f; + } else if( Math.abs(1.0f - v) < 0.01f ) { + v = 1.0f; + } + if( 0.0f <= v && v <= 1.0f ) { // OpenAL limits + volume = v; + al.alSourcef(alSource[0], AL.AL_GAIN, v); + return true; + } + } finally { + unlockContext(); + } + return false; + } + + @Override + public final void flush() { + if( !initialized || null == chosenFormat ) { + return; + } + lockContext(); + try { + // pauseImpl(); + stopImpl(false); + // Redundant: dequeueBuffer( false /* wait */, true /* ignoreBufferInconsistency */); + dequeueForceAll(); + if( alBufferNames.length != alFramesAvail.size() || alFramesPlaying.size() != 0 ) { + throw new InternalError("XXX: "+this); + } + if( DEBUG ) { + System.err.println(getThreadName()+": ALAudioSink: FLUSH playImpl "+(AL.AL_PLAYING == getSourceState(false))+", "+this); + } + } finally { + unlockContext(); + } + } + + @Override + public final int getEnqueuedFrameCount() { + return enqueuedFrameCount; + } + + @Override + public final int getFrameCount() { + return null != alBufferNames ? alBufferNames.length : 0; + } + + @Override + public final int getQueuedFrameCount() { + if( !initialized || null == chosenFormat ) { + return 0; + } + return alFramesPlaying.size(); + } + + @Override + public final int getFreeFrameCount() { + if( !initialized || null == chosenFormat ) { + return 0; + } + return alFramesAvail.size(); + } + + @Override + public final int getQueuedByteCount() { + if( !initialized || null == chosenFormat ) { + return 0; + } + return alBufferBytesQueued; + } + + @Override + public final int getQueuedTime() { + if( !initialized || null == chosenFormat ) { + return 0; + } + return chosenFormat.getBytesDuration(alBufferBytesQueued); + } + + @Override + public final int getPTS() { return playingPTS; } + + private static final String toHexString(int v) { return "0x"+Integer.toHexString(v); } + private static final String getThreadName() { return Thread.currentThread().getName(); } +} 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..2c1dfa237 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/openal/av/ALDummyUsage.java @@ -0,0 +1,16 @@ +package jogamp.opengl.openal.av; + +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; + + 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/shader/texture01_xxx.fp b/src/jogl/classes/jogamp/opengl/shader/texture01_xxx.fp new file mode 100644 index 000000000..8b30b65f9 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/shader/texture01_xxx.fp @@ -0,0 +1,19 @@ +// Copyright 2012 JogAmp Community. All rights reserved. + +#if __VERSION__ >= 130 + #define varying in + out vec4 mgl_FragColor; + #define texture2D texture +#else + #define mgl_FragColor gl_FragColor +#endif + +varying vec2 mgl_texCoord; + +uniform sampler2D mgl_Texture0; + +void main (void) +{ + mgl_FragColor = texture2D(mgl_Texture0, mgl_texCoord); +} + diff --git a/src/jogl/classes/jogamp/opengl/shader/texture01_xxx.vp b/src/jogl/classes/jogamp/opengl/shader/texture01_xxx.vp new file mode 100644 index 000000000..d9ef6b493 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/shader/texture01_xxx.vp @@ -0,0 +1,19 @@ +// Copyright 2012 JogAmp Community. All rights reserved. + +#if __VERSION__ >= 130 + #define attribute in + #define varying out +#endif + +uniform mat4 mgl_PMVMatrix[2]; + +attribute vec4 mgl_Vertex; +attribute vec4 mgl_MultiTexCoord; + +varying vec2 mgl_texCoord; + +void main(void) +{ + mgl_texCoord = mgl_MultiTexCoord.st; + gl_Position = mgl_PMVMatrix[0] * mgl_PMVMatrix[1] * mgl_Vertex; +} diff --git a/src/jogl/classes/jogamp/opengl/util/GLArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/GLArrayHandler.java index 22690b06d..810a9286b 100644 --- a/src/jogl/classes/jogamp/opengl/util/GLArrayHandler.java +++ b/src/jogl/classes/jogamp/opengl/util/GLArrayHandler.java @@ -31,41 +31,52 @@ package jogamp.opengl.util; import javax.media.opengl.*; /** - * Handles consistency of buffer data and array state. - * Implementations shall consider buffer types (VBO, ..), interleaved, etc. - * They also need to consider array state types, i.e. fixed function or GLSL. + * Handles consistency of buffer data and array state.<br/> + * Implementations shall consider buffer types (VBO, ..), interleaved, etc.<br/> + * They also need to consider array state types, i.e. fixed function or GLSL.<br/> */ public interface GLArrayHandler { + /** - * Implementation shall associate the data with the array - * and synchronize the data with the GPU. - * + * if <code>bind</code> is true and the data uses VBO, + * the latter will be bound and data written to the GPU if required. + * <p> + * If <code>bind</code> is false and the data uses VBO, + * the latter will be unbound. + * </p> + * * @param gl current GL object - * @param enable true if array data shall be valid, otherwise false. - * @param ext extension object allowing passing of an implementation detail + * @param bind true if VBO shall be bound and data written, + * otherwise clear VBO binding. + * @return true if data uses VBO and action was performed, otherwise false */ - public void syncData(GL gl, boolean enable, Object ext); - + public boolean bindBuffer(GL gl, boolean bind); + /** * Implementation shall enable or disable the array state. - * + * <p> + * Before enabling the array state, + * implementation shall synchronize the data with the GPU + * and associate the data with the array. + * </p> + * * @param gl current GL object * @param enable true if array shall be enabled, otherwise false. - * @param ext extension object allowing passing of an implementation detail + * @param ext extension object allowing passing of an implementation detail */ public void enableState(GL gl, boolean enable, Object ext); - + /** - * Supporting interleaved arrays, where sub handlers may handle + * Supporting interleaved arrays, where sub handlers may handle * the array state and the <i>master</i> handler the buffer consistency. - * + * * @param handler the sub handler * @throws UnsupportedOperationException if this array handler does not support interleaved arrays */ public void addSubHandler(GLArrayHandlerFlat handler) throws UnsupportedOperationException; public void setSubArrayVBOName(int vboName); - + } diff --git a/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerFlat.java b/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerFlat.java index dca9129ad..179142fee 100644 --- a/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerFlat.java +++ b/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerFlat.java @@ -39,23 +39,21 @@ public interface GLArrayHandlerFlat { /** * Implementation shall associate the data with the array - * + * * @param gl current GL object - * @param enable true if array data shall be valid, otherwise false. - * @param force true force data association, bypassing optimization - * @param ext extension object allowing passing of an implementation detail + * @param ext extension object allowing passing of an implementation detail */ - public void syncData(GL gl, boolean enable, boolean force, Object ext); - + public void syncData(GL gl, Object ext); + /** * Implementation shall enable or disable the array state. - * + * * @param gl current GL object * @param enable true if array shall be enabled, otherwise false. - * @param ext extension object allowing passing of an implementation detail + * @param ext extension object allowing passing of an implementation detail */ - public void enableState(GL gl, boolean enable, Object ext); - + public void enableState(GL gl, boolean enable, Object ext); + public GLArrayDataWrapper getData(); } diff --git a/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerInterleaved.java b/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerInterleaved.java index d31b41582..89e01edd8 100644 --- a/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerInterleaved.java +++ b/src/jogl/classes/jogamp/opengl/util/GLArrayHandlerInterleaved.java @@ -28,8 +28,6 @@ package jogamp.opengl.util; - -import java.nio.Buffer; import java.util.ArrayList; import java.util.List; @@ -38,61 +36,46 @@ import javax.media.opengl.GL; import com.jogamp.opengl.util.GLArrayDataEditable; /** - * Interleaved fixed function arrays, i.e. where this buffer data - * represents many arrays. + * Interleaved fixed function arrays, i.e. where this buffer data + * represents many arrays. */ -public class GLArrayHandlerInterleaved implements GLArrayHandler { - private GLArrayDataEditable ad; - private List<GLArrayHandlerFlat> subArrays = new ArrayList<GLArrayHandlerFlat>(); +public class GLArrayHandlerInterleaved extends GLVBOArrayHandler { + private final List<GLArrayHandlerFlat> subArrays = new ArrayList<GLArrayHandlerFlat>(); public GLArrayHandlerInterleaved(GLArrayDataEditable ad) { - this.ad = ad; + super(ad); } - + + @Override public final void setSubArrayVBOName(int vboName) { for(int i=0; i<subArrays.size(); i++) { subArrays.get(i).getData().setVBOName(vboName); - } + } } - + + @Override public final void addSubHandler(GLArrayHandlerFlat handler) { subArrays.add(handler); } - private final void syncSubData(GL gl, boolean enable, boolean force, Object ext) { + private final void syncSubData(GL gl, Object ext) { for(int i=0; i<subArrays.size(); i++) { - subArrays.get(i).syncData(gl, enable, force, ext); - } - } - - public final void syncData(GL gl, boolean enable, Object ext) { - if(enable) { - final Buffer buffer = ad.getBuffer(); + subArrays.get(i).syncData(gl, ext); + } + } - if(ad.isVBO()) { - // always bind and refresh the VBO mgr, - // in case more than one gl*Pointer objects are in use - gl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); - if(!ad.isVBOWritten()) { - if(null!=buffer) { - gl.glBufferData(ad.getVBOTarget(), buffer.limit() * ad.getComponentSizeInBytes(), buffer, ad.getVBOUsage()); - } - ad.setVBOWritten(true); - } - } - syncSubData(gl, true, true, ext); - } else { - syncSubData(gl, false, true, ext); - if(ad.isVBO()) { - gl.glBindBuffer(ad.getVBOTarget(), 0); + @Override + public final void enableState(GL gl, boolean enable, Object ext) { + if(enable) { + final boolean vboBound = bindBuffer(gl, true); + syncSubData(gl, ext); + if(vboBound) { + bindBuffer(gl, false); } } - } - - public final void enableState(GL gl, boolean enable, Object ext) { for(int i=0; i<subArrays.size(); i++) { subArrays.get(i).enableState(gl, enable, ext); - } + } } } diff --git a/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java index 6c8e2e762..8a587980d 100644 --- a/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java +++ b/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java @@ -28,55 +28,43 @@ package jogamp.opengl.util; -import javax.media.opengl.*; +import javax.media.opengl.GL; +import javax.media.opengl.GLException; -import com.jogamp.opengl.util.*; +import com.jogamp.opengl.util.GLArrayDataEditable; -import java.nio.*; /** - * Used for pure VBO data arrays, i.e. where the buffer data - * does not represents a specific array name. + * Used for pure VBO data arrays, i.e. where the buffer data + * does not represents a specific array name. */ -public class GLDataArrayHandler implements GLArrayHandler { - private GLArrayDataEditable ad; +public class GLDataArrayHandler extends GLVBOArrayHandler { public GLDataArrayHandler(GLArrayDataEditable ad) { - this.ad = ad; + super(ad); } + @Override public final void setSubArrayVBOName(int vboName) { throw new UnsupportedOperationException(); } - + + @Override public final void addSubHandler(GLArrayHandlerFlat handler) { throw new UnsupportedOperationException(); } - - public final void syncData(GL gl, boolean enable, Object ext) { - if(!ad.isVBO()) { - // makes no sense otherwise - throw new GLException("GLDataArrayHandler can only handle VBOs."); - } - if(enable) { - Buffer buffer = ad.getBuffer(); - // always bind and refresh the VBO mgr, - // in case more than one gl*Pointer objects are in use - gl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); - if(!ad.isVBOWritten()) { - if(null!=buffer) { - gl.glBufferData(ad.getVBOTarget(), buffer.limit() * ad.getComponentSizeInBytes(), buffer, ad.getVBOUsage()); - } - ad.setVBOWritten(true); + @Override + public final void enableState(GL gl, boolean enable, Object ext) { + if(enable) { + if(!ad.isVBO()) { + // makes no sense otherwise + throw new GLException("GLDataArrayHandler can only handle VBOs."); } - } else { - gl.glBindBuffer(ad.getVBOTarget(), 0); - } - } - - public final void enableState(GL gl, boolean enable, Object ext) { - // no array association + bindBuffer(gl, true); + bindBuffer(gl, false); + } + // no array association } } diff --git a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java index d8939dc0f..7f7a99a2d 100644 --- a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java +++ b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java @@ -28,47 +28,36 @@ package jogamp.opengl.util; -import javax.media.opengl.*; -import javax.media.opengl.fixedfunc.*; +import javax.media.opengl.GL; +import javax.media.opengl.GLException; +import javax.media.opengl.fixedfunc.GLPointerFunc; import com.jogamp.opengl.util.GLArrayDataEditable; -import java.nio.*; - /** - * Used for 1:1 fixed function arrays, i.e. where the buffer data - * represents this array only. + * Used for 1:1 fixed function arrays, i.e. where the buffer data + * represents this array only. */ -public class GLFixedArrayHandler implements GLArrayHandler { - private GLArrayDataEditable ad; - +public class GLFixedArrayHandler extends GLVBOArrayHandler { public GLFixedArrayHandler(GLArrayDataEditable ad) { - this.ad = ad; + super(ad); } - + + @Override public final void setSubArrayVBOName(int vboName) { throw new UnsupportedOperationException(); } - + + @Override public final void addSubHandler(GLArrayHandlerFlat handler) { throw new UnsupportedOperationException(); } - - public final void syncData(GL gl, boolean enable, Object ext) { + + @Override + public final void enableState(GL gl, boolean enable, Object ext) { + final GLPointerFunc glp = gl.getGL2ES1(); if(enable) { - final Buffer buffer = ad.getBuffer(); - if(ad.isVBO()) { - // always bind and refresh the VBO mgr, - // in case more than one gl*Pointer objects are in use - gl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); - if(!ad.isVBOWritten()) { - if(null!=buffer) { - gl.glBufferData(ad.getVBOTarget(), buffer.limit() * ad.getComponentSizeInBytes(), buffer, ad.getVBOUsage()); - } - ad.setVBOWritten(true); - } - } - final GLPointerFunc glp = gl.getGL2ES1(); + final boolean vboBound = bindBuffer(gl, true); switch(ad.getIndex()) { case GLPointerFunc.GL_VERTEX_ARRAY: glp.glVertexPointer(ad); @@ -83,17 +72,12 @@ public class GLFixedArrayHandler implements GLArrayHandler { glp.glTexCoordPointer(ad); break; default: - throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); + throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); } - } else if(ad.isVBO()) { - gl.glBindBuffer(ad.getVBOTarget(), 0); - } - } - - public final void enableState(GL gl, boolean enable, Object ext) { - final GLPointerFunc glp = gl.getGL2ES1(); - if(enable) { - glp.glEnableClientState(ad.getIndex()); + if(vboBound) { + bindBuffer(gl, false); + } + glp.glEnableClientState(ad.getIndex()); } else { glp.glDisableClientState(ad.getIndex()); } diff --git a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java index 2937cc720..acec0510f 100644 --- a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java +++ b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java @@ -35,7 +35,7 @@ import javax.media.opengl.fixedfunc.GLPointerFunc; import com.jogamp.opengl.util.GLArrayDataWrapper; /** - * Used for interleaved fixed function arrays, i.e. where the buffer data itself is handled + * Used for interleaved fixed function arrays, i.e. where the buffer data itself is handled * separately and interleaves many arrays. */ public class GLFixedArrayHandlerFlat implements GLArrayHandlerFlat { @@ -45,36 +45,37 @@ public class GLFixedArrayHandlerFlat implements GLArrayHandlerFlat { this.ad = ad; } + @Override public GLArrayDataWrapper getData() { return ad; } - - public final void syncData(GL gl, boolean enable, boolean force, Object ext) { - if(enable) { - final GLPointerFunc glp = gl.getGL2ES1(); - switch(ad.getIndex()) { - case GLPointerFunc.GL_VERTEX_ARRAY: - glp.glVertexPointer(ad); - break; - case GLPointerFunc.GL_NORMAL_ARRAY: - glp.glNormalPointer(ad); - break; - case GLPointerFunc.GL_COLOR_ARRAY: - glp.glColorPointer(ad); - break; - case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: - glp.glTexCoordPointer(ad); - break; - default: - throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); - } + + @Override + public final void syncData(GL gl, Object ext) { + final GLPointerFunc glp = gl.getGL2ES1(); + switch(ad.getIndex()) { + case GLPointerFunc.GL_VERTEX_ARRAY: + glp.glVertexPointer(ad); + break; + case GLPointerFunc.GL_NORMAL_ARRAY: + glp.glNormalPointer(ad); + break; + case GLPointerFunc.GL_COLOR_ARRAY: + glp.glColorPointer(ad); + break; + case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: + glp.glTexCoordPointer(ad); + break; + default: + throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); } } + @Override public final void enableState(GL gl, boolean enable, Object ext) { final GLPointerFunc glp = gl.getGL2ES1(); if(enable) { - glp.glEnableClientState(ad.getIndex()); + glp.glEnableClientState(ad.getIndex()); } else { glp.glDisableClientState(ad.getIndex()); } diff --git a/src/jogl/classes/jogamp/opengl/util/GLVBOArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/GLVBOArrayHandler.java new file mode 100644 index 000000000..5198cacfa --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/GLVBOArrayHandler.java @@ -0,0 +1,71 @@ +/** + * Copyright 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 met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl.util; + +import java.nio.Buffer; + +import javax.media.opengl.GL; + +import com.jogamp.opengl.util.GLArrayDataEditable; + +/** + * Interleaved fixed function arrays, i.e. where this buffer data + * represents many arrays. + */ +public abstract class GLVBOArrayHandler implements GLArrayHandler { + protected GLArrayDataEditable ad; + + public GLVBOArrayHandler(GLArrayDataEditable ad) { + this.ad = ad; + } + + @Override + public final boolean bindBuffer(GL gl, boolean bind) { + if( !ad.isVBO() ) { + return false; + } + if(bind) { + // always bind and refresh the VBO mgr, + // in case more than one gl*Pointer objects are in use + gl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); + if(!ad.isVBOWritten()) { + final Buffer buffer = ad.getBuffer(); + if(null!=buffer) { + gl.glBufferData(ad.getVBOTarget(), buffer.limit() * ad.getComponentSizeInBytes(), buffer, ad.getVBOUsage()); + } + ad.setVBOWritten(true); + } + } else { + gl.glBindBuffer(ad.getVBOTarget(), 0); + } + return true; + } + +} + diff --git a/src/jogl/classes/jogamp/opengl/util/av/EGLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/EGLMediaPlayerImpl.java index 31f13297b..c39b78bb8 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/EGLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/EGLMediaPlayerImpl.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -44,43 +44,40 @@ import jogamp.opengl.egl.EGLExt; public abstract class EGLMediaPlayerImpl extends GLMediaPlayerImpl { final protected TextureType texType; final protected boolean useKHRSync; - + public enum TextureType { - GL(0), KHRImage(1); - + GL(0), KHRImage(1); + public final int id; TextureType(int id){ this.id = id; } - } - + } + public static class EGLTextureFrame extends TextureSequence.TextureFrame { - + public EGLTextureFrame(Buffer clientBuffer, Texture t, long khrImage, long khrSync) { super(t); this.clientBuffer = clientBuffer; this.image = khrImage; this.sync = khrSync; } - + public final Buffer getClientBuffer() { return clientBuffer; } - public final long getImage() { return image; } + public final long getImage() { return image; } public final long getSync() { return sync; } - + + @Override public String toString() { - return "EGLTextureFrame[" + texture + ", img "+ image + ", sync "+ sync+", clientBuffer "+clientBuffer+"]"; + return "EGLTextureFrame[pts " + pts + " ms, l " + duration + " ms, texID "+ texture.getTextureObject() + ", img "+ image + ", sync "+ sync+", clientBuffer "+clientBuffer+"]"; } protected final Buffer clientBuffer; protected final long image; protected final long sync; } - - protected EGLMediaPlayerImpl() { - this(TextureType.GL, false); - } - + protected EGLMediaPlayerImpl(TextureType texType, boolean useKHRSync) { super(); this.texType = texType; @@ -88,36 +85,36 @@ public abstract class EGLMediaPlayerImpl extends GLMediaPlayerImpl { } @Override - protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) { - final Texture texture = super.createTexImageImpl(gl, idx, tex, width, height, false); + protected TextureSequence.TextureFrame createTexImage(GL gl, int texName) { + final Texture texture = super.createTexImageImpl(gl, texName, width, height); final Buffer clientBuffer; final long image; final long sync; - final boolean eglUsage = TextureType.KHRImage == texType || useKHRSync ; + final boolean eglUsage = TextureType.KHRImage == texType || useKHRSync ; final EGLContext eglCtx; final EGLExt eglExt; final EGLDrawable eglDrawable; - + if(eglUsage) { eglCtx = (EGLContext) gl.getContext(); eglExt = eglCtx.getEGLExt(); - eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); + eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); } else { eglCtx = null; eglExt = null; eglDrawable = null; } - + if(TextureType.KHRImage == texType) { IntBuffer nioTmp = Buffers.newDirectIntBuffer(1); // create EGLImage from texture clientBuffer = null; // FIXME nioTmp.put(0, EGL.EGL_NONE); - image = eglExt.eglCreateImageKHR( eglDrawable.getDisplay(), eglCtx.getHandle(), + image = eglExt.eglCreateImageKHR( eglDrawable.getNativeSurface().getDisplayHandle(), eglCtx.getHandle(), EGLExt.EGL_GL_TEXTURE_2D_KHR, clientBuffer, nioTmp); if (0==image) { - throw new RuntimeException("EGLImage creation failed: "+EGL.eglGetError()+", ctx "+eglCtx+", tex "+tex[idx]+", err "+toHexString(EGL.eglGetError())); + throw new RuntimeException("EGLImage creation failed: "+EGL.eglGetError()+", ctx "+eglCtx+", tex "+texName+", err "+toHexString(EGL.eglGetError())); } } else { clientBuffer = null; @@ -125,12 +122,12 @@ public abstract class EGLMediaPlayerImpl extends GLMediaPlayerImpl { } if(useKHRSync) { - int[] tmp = new int[1]; + IntBuffer tmp = Buffers.newDirectIntBuffer(1); // Create sync object so that we can be sure that gl has finished // rendering the EGLImage texture before we tell OpenMAX to fill // it with a new frame. - tmp[0] = EGL.EGL_NONE; - sync = eglExt.eglCreateSyncKHR(eglDrawable.getDisplay(), EGLExt.EGL_SYNC_FENCE_KHR, tmp, 0); + tmp.put(0, EGL.EGL_NONE); + sync = eglExt.eglCreateSyncKHR(eglDrawable.getNativeSurface().getDisplayHandle(), EGLExt.EGL_SYNC_FENCE_KHR, tmp); if (0==sync) { throw new RuntimeException("EGLSync creation failed: "+EGL.eglGetError()+", ctx "+eglCtx+", err "+toHexString(EGL.eglGetError())); } @@ -139,31 +136,31 @@ public abstract class EGLMediaPlayerImpl extends GLMediaPlayerImpl { } return new EGLTextureFrame(clientBuffer, texture, image, sync); } - + @Override - protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) { - final boolean eglUsage = TextureType.KHRImage == texType || useKHRSync ; + protected void destroyTexFrame(GL gl, TextureSequence.TextureFrame frame) { + final boolean eglUsage = TextureType.KHRImage == texType || useKHRSync ; final EGLContext eglCtx; final EGLExt eglExt; final EGLDrawable eglDrawable; - + if(eglUsage) { eglCtx = (EGLContext) gl.getContext(); eglExt = eglCtx.getEGLExt(); - eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); + eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); } else { eglCtx = null; eglExt = null; eglDrawable = null; } - final EGLTextureFrame eglTex = (EGLTextureFrame) imgTex; - + final EGLTextureFrame eglTex = (EGLTextureFrame) frame; + if(0!=eglTex.getImage()) { - eglExt.eglDestroyImageKHR(eglDrawable.getDisplay(), eglTex.getImage()); + eglExt.eglDestroyImageKHR(eglDrawable.getNativeSurface().getDisplayHandle(), eglTex.getImage()); } if(0!=eglTex.getSync()) { - eglExt.eglDestroySyncKHR(eglDrawable.getDisplay(), eglTex.getSync()); + eglExt.eglDestroySyncKHR(eglDrawable.getNativeSurface().getDisplayHandle(), eglTex.getSync()); } - super.destroyTexImage(gl, imgTex); + super.destroyTexFrame(gl, frame); } } diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java index d3d45e692..7cea51dc8 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -28,331 +28,670 @@ package jogamp.opengl.util.av; import java.io.IOException; -import java.net.URLConnection; +import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.opengl.GL; import javax.media.opengl.GL2; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLES2; import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import jogamp.opengl.Debug; +import com.jogamp.common.net.URIQueryProps; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.LFRingbuffer; +import com.jogamp.common.util.Ringbuffer; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.util.TimeFrameI; +import com.jogamp.opengl.util.av.AudioSink; import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; /** * After object creation an implementation may customize the behavior: * <ul> - * <li>{@link #setTextureCount(int)}</li> + * <li>{@link #setDesTextureCount(int)}</li> * <li>{@link #setTextureTarget(int)}</li> * <li>{@link EGLMediaPlayerImpl#setEGLTexImageAttribs(boolean, boolean)}.</li> * </ul> - * + * * <p> * See {@link GLMediaPlayer}. * </p> */ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { + private static final int STREAM_WORKER_DELAY = Debug.getIntProperty("jogl.debug.GLMediaPlayer.StreamWorker.delay", false, 0); protected static final String unknown = "unknown"; - protected State state; + protected volatile State state; + private final Object stateLock = new Object(); + protected int textureCount; protected int textureTarget; protected int textureFormat; + protected int textureInternalFormat; protected int textureType; protected int texUnit; - - + + protected int[] texMinMagFilter = { GL.GL_NEAREST, GL.GL_NEAREST }; protected int[] texWrapST = { GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE }; - - protected URLConnection urlConn = null; - - protected float playSpeed = 1.0f; - - /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + + /** User requested URI stream location. */ + protected URI streamLoc = null; + /** + * In case {@link #streamLoc} is a {@link GLMediaPlayer#CameraInputScheme}, + * {@link #cameraPath} holds the URI's path portion + * as parsed in {@link #initStream(URI, int, int, int)}. + * @see #cameraProps + */ + protected String cameraPath = null; + /** Optional camera properties, see {@link #cameraPath}. */ + protected Map<String, String> cameraProps = null; + + protected volatile float playSpeed = 1.0f; + protected float audioVolume = 1.0f; + + /** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ + protected int vid = GLMediaPlayer.STREAM_ID_AUTO; + /** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ + protected int aid = GLMediaPlayer.STREAM_ID_AUTO; + /** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected int width = 0; - /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected int height = 0; - /** Video fps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Video avg. fps. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected float fps = 0; - /** Stream bps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Video avg. frame duration in ms. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ + protected float frame_duration = 0f; + /** Stream bps. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected int bps_stream = 0; - /** Video bps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Video bps. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected int bps_video = 0; - /** Audio bps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Audio bps. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected int bps_audio = 0; - /** In frames. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ - protected int totalFrames = 0; - /** In ms. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** In frames. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ + protected int videoFrames = 0; + /** In frames. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ + protected int audioFrames = 0; + /** In ms. Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected int duration = 0; - /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected String acodec = unknown; - /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */ + /** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */ protected String vcodec = unknown; - - protected int frameNumber = 0; - - protected TextureSequence.TextureFrame[] texFrames = null; - protected HashMap<Integer, TextureSequence.TextureFrame> texFrameMap = new HashMap<Integer, TextureSequence.TextureFrame>(); - private ArrayList<GLMediaEventListener> eventListeners = new ArrayList<GLMediaEventListener>(); + + protected volatile int decodedFrameCount = 0; + protected int presentedFrameCount = 0; + protected int displayedFrameCount = 0; + protected volatile int video_pts_last = 0; + + /** + * Help detect EOS, limit is {@link #MAX_FRAMELESS_MS_UNTIL_EOS}. + * To be used either by getNextTexture(..) or StreamWorker for audio-only. + */ + private int nullFrameCount = 0; + private int maxNullFrameCountUntilEOS = 0; + /** + * Help detect EOS, limit {@value} milliseconds without a valid frame. + */ + private static final int MAX_FRAMELESS_MS_UNTIL_EOS = 5000; + private static final int MAX_FRAMELESS_UNTIL_EOS_DEFAULT = MAX_FRAMELESS_MS_UNTIL_EOS / 30; // default value assuming 30fps + + /** See {@link #getAudioSink()}. Set by implementation if used from within {@link #initStreamImpl(int, int)}! */ + protected AudioSink audioSink = null; + protected boolean audioSinkPlaySpeedSet = false; + + /** System Clock Reference (SCR) of first audio PTS at start time. */ + private long audio_scr_t0 = 0; + private boolean audioSCR_reset = true; + + /** System Clock Reference (SCR) of first video frame at start time. */ + private long video_scr_t0 = 0; + /** System Clock Reference (SCR) PTS offset, i.e. first video PTS at start time. */ + private int video_scr_pts = 0; + /** Cumulative video pts diff. */ + private float video_dpts_cum = 0; + /** Cumulative video frames. */ + private int video_dpts_count = 0; + /** Number of min frame count required for video cumulative sync. */ + private static final int VIDEO_DPTS_NUM = 20; + /** Cumulative coefficient, value {@value}. */ + private static final float VIDEO_DPTS_COEFF = 0.7943282f; // (float) Math.exp(Math.log(0.01) / VIDEO_DPTS_NUM); + /** Maximum valid video pts diff. */ + private static final int VIDEO_DPTS_MAX = 5000; // 5s max diff + /** Trigger video PTS reset with given cause as bitfield. */ + private boolean videoSCR_reset = false; + + protected TextureFrame[] videoFramesOrig = null; + protected Ringbuffer<TextureFrame> videoFramesFree = null; + protected Ringbuffer<TextureFrame> videoFramesDecoded = null; + protected volatile TextureFrame lastFrame = null; + /** + * @see #isGLOriented() + */ + protected boolean isInGLOrientation = false; + + private final ArrayList<GLMediaEventListener> eventListeners = new ArrayList<GLMediaEventListener>(); protected GLMediaPlayerImpl() { - this.textureCount=3; + this.textureCount=0; this.textureTarget=GL.GL_TEXTURE_2D; this.textureFormat = GL.GL_RGBA; - this.textureType = GL.GL_UNSIGNED_BYTE; + this.textureInternalFormat = GL.GL_RGBA; + this.textureType = GL.GL_UNSIGNED_BYTE; this.texUnit = 0; this.state = State.Uninitialized; } @Override - public void setTextureUnit(int u) { texUnit = u; } - + public final void setTextureUnit(int u) { texUnit = u; } + @Override - public int getTextureUnit() { return texUnit; } - - protected final void setTextureCount(int textureCount) { - this.textureCount=textureCount; - } + public final int getTextureUnit() { return texUnit; } + + @Override + public final int getTextureTarget() { return textureTarget; } + @Override public final int getTextureCount() { return textureCount; } - + protected final void setTextureTarget(int target) { textureTarget=target; } - protected final void setTextureFormat(int f) { textureFormat=f; } + protected final void setTextureFormat(int internalFormat, int format) { + textureInternalFormat=internalFormat; + textureFormat=format; + } protected final void setTextureType(int t) { textureType=t; } + @Override public final void setTextureMinMagFilter(int[] minMagFilter) { texMinMagFilter[0] = minMagFilter[0]; texMinMagFilter[1] = minMagFilter[1];} + @Override public final int[] getTextureMinMagFilter() { return texMinMagFilter; } - - public final void setTextureWrapST(int[] wrapST) { texWrapST[0] = wrapST[0]; texWrapST[1] = wrapST[1];} - public final int[] getTextureWrapST() { return texWrapST; } @Override - public final TextureSequence.TextureFrame getLastTexture() throws IllegalStateException { - if(State.Uninitialized == state) { - throw new IllegalStateException("Instance not initialized: "+this); - } - return getLastTextureImpl(); - } - protected abstract TextureSequence.TextureFrame getLastTextureImpl(); - + public final void setTextureWrapST(int[] wrapST) { texWrapST[0] = wrapST[0]; texWrapST[1] = wrapST[1];} @Override - public final synchronized TextureSequence.TextureFrame getNextTexture(GL gl, boolean blocking) throws IllegalStateException { - if(State.Uninitialized == state) { - throw new IllegalStateException("Instance not initialized: "+this); - } - if(State.Playing == state) { - final TextureSequence.TextureFrame f = getNextTextureImpl(gl, blocking); - return f; + public final int[] getTextureWrapST() { return texWrapST; } + + private final void checkGLInit() { + if(State.Uninitialized == state || State.Initialized == state ) { + throw new IllegalStateException("GL not initialized: "+this); } - return getLastTextureImpl(); } - protected abstract TextureSequence.TextureFrame getNextTextureImpl(GL gl, boolean blocking); - + @Override public String getRequiredExtensionsShaderStub() throws IllegalStateException { - if(State.Uninitialized == state) { - throw new IllegalStateException("Instance not initialized: "+this); - } + checkGLInit(); if(GLES2.GL_TEXTURE_EXTERNAL_OES == textureTarget) { - return TextureSequence.GL_OES_EGL_image_external_Required_Prelude; + return ShaderCode.createExtensionDirective(GLExtensions.OES_EGL_image_external, ShaderCode.ENABLE); } return ""; } - + @Override public String getTextureSampler2DType() throws IllegalStateException { - if(State.Uninitialized == state) { - throw new IllegalStateException("Instance not initialized: "+this); - } + checkGLInit(); switch(textureTarget) { case GL.GL_TEXTURE_2D: - case GL2.GL_TEXTURE_RECTANGLE: + case GL2.GL_TEXTURE_RECTANGLE: return TextureSequence.sampler2D; case GLES2.GL_TEXTURE_EXTERNAL_OES: return TextureSequence.samplerExternalOES; default: - throw new GLException("Unsuported texture target: "+toHexString(textureTarget)); + throw new GLException("Unsuported texture target: "+toHexString(textureTarget)); } } - + /** * {@inheritDoc} - * + * * This implementation simply returns the build-in function name of <code>texture2D</code>, * if not overridden by specialization. */ @Override public String getTextureLookupFunctionName(String desiredFuncName) throws IllegalStateException { - if(State.Uninitialized == state) { - throw new IllegalStateException("Instance not initialized: "+this); - } + checkGLInit(); return "texture2D"; } - + /** * {@inheritDoc} - * - * This implementation simply returns an empty string since it's using + * + * This implementation simply returns an empty string since it's using * the build-in function <code>texture2D</code>, * if not overridden by specialization. */ @Override public String getTextureLookupFragmentShaderImpl() throws IllegalStateException { - if(State.Uninitialized == state) { - throw new IllegalStateException("Instance not initialized: "+this); - } - return ""; + checkGLInit(); + return ""; } - + @Override - public final synchronized float getPlaySpeed() { - return playSpeed; - } - + public final int getDecodedFrameCount() { return decodedFrameCount; } + @Override - public final synchronized void setPlaySpeed(float rate) { - if(State.Uninitialized != state && setPlaySpeedImpl(rate)) { - playSpeed = rate; + public final int getPresentedFrameCount() { return presentedFrameCount; } + + @Override + public final int getVideoPTS() { return video_pts_last; } + + @Override + public final int getAudioPTS() { + if( State.Uninitialized != state ) { + return getAudioPTSImpl(); } - if(DEBUG) { System.err.println("SetPlaySpeed: "+toString()); } + return 0; } - protected abstract boolean setPlaySpeedImpl(float rate); - - public final State start() { - switch(state) { - case Stopped: - case Paused: - if(startImpl()) { - state = State.Playing; - } + /** Override if not using audioSink! */ + protected int getAudioPTSImpl() { + if( null != audioSink ) { + return audioSink.getPTS(); + } else { + return 0; } - if(DEBUG) { System.err.println("Start: "+toString()); } - return state; } - protected abstract boolean startImpl(); - - public final State pause() { - if(State.Playing == state && pauseImpl()) { - state = State.Paused; + + @Override + public final State getState() { return state; } + + @Override + public final State play() { + synchronized( stateLock ) { + final State preState = state; + switch( state ) { + case Paused: + if( playImpl() ) { + resetAVPTS(); + if( null != audioSink ) { + audioSink.play(); // cont. w/ new data + } + if( null != streamWorker ) { + streamWorker.doResume(); + } + changeState(0, State.Playing); + } + default: + } + if(DEBUG) { System.err.println("Play: "+preState+" -> "+state+", "+toString()); } + return state; } - if(DEBUG) { System.err.println("Pause: "+toString()); } - return state; } - protected abstract boolean pauseImpl(); - - public final State stop() { - switch(state) { - case Playing: - case Paused: - if(stopImpl()) { - state = State.Stopped; + protected abstract boolean playImpl(); + + @Override + public final State pause(boolean flush) { + return pauseImpl(flush, 0); + } + private final State pauseImpl(boolean flush, int event_mask) { + synchronized( stateLock ) { + final State preState = state; + if( State.Playing == state ) { + event_mask = addStateEventMask(event_mask, GLMediaPlayer.State.Paused); + state = State.Paused; + if( null != streamWorker ) { + streamWorker.doPause(); + } + if( flush ) { + resetAVPTSAndFlush(); + } else if( null != audioSink ) { + audioSink.pause(); } + attributesUpdated( event_mask ); + if( !pauseImpl() ) { + play(); + } + } + if(DEBUG) { System.err.println("Pause: "+preState+" -> "+state+", "+toString()); } + return state; } - if(DEBUG) { System.err.println("Stop: "+toString()); } - return state; } - protected abstract boolean stopImpl(); - + protected abstract boolean pauseImpl(); + @Override - public final int getCurrentPosition() { - if(State.Uninitialized != state) { - return getCurrentPositionImpl(); + public final State destroy(GL gl) { + return destroyImpl(gl, 0); + } + private final State destroyImpl(GL gl, int event_mask) { + synchronized( stateLock ) { + if( null != streamWorker ) { + streamWorker.doStop(); + streamWorker = null; + } + destroyImpl(gl); + removeAllTextureFrames(gl); + textureCount=0; + changeState(event_mask, State.Uninitialized); + attachedObjects.clear(); + return state; } - return 0; } - protected abstract int getCurrentPositionImpl(); - + protected abstract void destroyImpl(GL gl); + + @Override public final int seek(int msec) { - final int cp; - switch(state) { - case Stopped: - case Playing: - case Paused: - cp = seekImpl(msec); - break; - default: - cp = 0; + synchronized( stateLock ) { + final State preState = state; + final int pts1; + switch(state) { + case Playing: + case Paused: + final State _state = state; + state = State.Paused; + if( null != streamWorker ) { + streamWorker.doPause(); + } + // Adjust target .. + if( msec >= duration ) { + msec = duration - (int)Math.floor(frame_duration); + } else if( msec < 0 ) { + msec = 0; + } + pts1 = seekImpl(msec); + resetAVPTSAndFlush(); + if( null != audioSink && State.Playing == _state ) { + audioSink.play(); // cont. w/ new data + } + if(DEBUG) { + System.err.println("Seek("+msec+"): "+getPerfString()); + } + if( null != streamWorker ) { + streamWorker.doResume(); + } + state = _state; + break; + default: + pts1 = 0; + } + if(DEBUG) { System.err.println("Seek("+msec+"): "+preState+" -> "+state+", "+toString()); } + return pts1; } - if(DEBUG) { System.err.println("Seek("+msec+"): "+toString()); } - return cp; } protected abstract int seekImpl(int msec); - - public final State getState() { return state; } - - @Override - public final State initGLStream(GL gl, URLConnection urlConn) throws IllegalStateException, GLException, IOException { - if(State.Uninitialized != state) { - throw new IllegalStateException("Instance not in state "+State.Uninitialized+", but "+state+", "+this); - } - this.urlConn = urlConn; - if (this.urlConn != null) { - try { - if(null != gl) { - if(null!=texFrames) { - // re-init .. - removeAllImageTextures(gl); - } else { - texFrames = new TextureSequence.TextureFrame[textureCount]; - } - final int[] tex = new int[textureCount]; - { - gl.glGenTextures(textureCount, tex, 0); - final int err = gl.glGetError(); - if( GL.GL_NO_ERROR != err ) { - throw new RuntimeException("TextureNames creation failed (num: "+textureCount+"): err "+toHexString(err)); - } + + @Override + public final float getPlaySpeed() { + return playSpeed; + } + + @Override + public final boolean setPlaySpeed(float rate) { + synchronized( stateLock ) { + final float preSpeed = playSpeed; + boolean res = false; + if(State.Uninitialized != state ) { + if( rate > 0.01f ) { + if( Math.abs(1.0f - rate) < 0.01f ) { + rate = 1.0f; } - initGLStreamImpl(gl, tex); - - for(int i=0; i<textureCount; i++) { - final TextureSequence.TextureFrame tf = createTexImage(gl, i, tex); - texFrames[i] = tf; - texFrameMap.put(tex[i], tf); + if( setPlaySpeedImpl(rate) ) { + resetAVPTS(); + playSpeed = rate; + res = true; } } - state = State.Stopped; - return state; - } catch (Throwable t) { - throw new GLException("Error initializing GL resources", t); } + if(DEBUG) { System.err.println("setPlaySpeed("+rate+"): "+state+", "+preSpeed+" -> "+playSpeed+", "+toString()); } + return res; + } + } + /** + * Override if not using AudioSink, or AudioSink's {@link AudioSink#setPlaySpeed(float)} is not sufficient! + * <p> + * AudioSink shall respect <code>!audioSinkPlaySpeedSet</code> to determine data_size + * at {@link AudioSink#enqueueData(com.jogamp.opengl.util.av.AudioSink.AudioFrame)}. + * </p> + */ + protected boolean setPlaySpeedImpl(float rate) { + if( null != audioSink ) { + audioSinkPlaySpeedSet = audioSink.setPlaySpeed(rate); + } + // still true, even if audioSink rejects command since we deal w/ video sync + // and AudioSink w/ audioSinkPlaySpeedSet at enqueueData(..). + return true; + } + + @Override + public final float getAudioVolume() { + getAudioVolumeImpl(); + return audioVolume; + } + /** + * Override if not using AudioSink, or AudioSink's {@link AudioSink#getVolume()} is not sufficient! + */ + protected void getAudioVolumeImpl() { + if( null != audioSink ) { + audioVolume = audioSink.getVolume(); + } + } + + @Override + public boolean setAudioVolume(float v) { + synchronized( stateLock ) { + final float preVolume = audioVolume; + boolean res = false; + if(State.Uninitialized != state ) { + if( Math.abs(v) < 0.01f ) { + v = 0.0f; + } else if( Math.abs(1.0f - v) < 0.01f ) { + v = 1.0f; + } + if( setAudioVolumeImpl(v) ) { + audioVolume = v; + res = true; + } + } + if(DEBUG) { System.err.println("setAudioVolume("+v+"): "+state+", "+preVolume+" -> "+audioVolume+", "+toString()); } + return res; } - return state; } - /** - * Implementation shall set the following set of data here - * @param gl TODO - * @param texNames TODO + * Override if not using AudioSink, or AudioSink's {@link AudioSink#setVolume(float)} is not sufficient! + */ + protected boolean setAudioVolumeImpl(float v) { + if( null != audioSink ) { + return audioSink.setVolume(v); + } + // still true, even if audioSink rejects command .. + return true; + } + + @Override + public final void initStream(URI streamLoc, final int vid, final int aid, int reqTextureCount) throws IllegalStateException, IllegalArgumentException { + synchronized( stateLock ) { + if(State.Uninitialized != state) { + throw new IllegalStateException("Instance not in state unintialized: "+this); + } + if(null == streamLoc) { + throw new IllegalArgumentException("streamLock is null"); + } + if( STREAM_ID_NONE != vid ) { + textureCount = validateTextureCount(reqTextureCount); + if( textureCount < TEXTURE_COUNT_MIN ) { + throw new InternalError("Validated texture count < "+TEXTURE_COUNT_MIN+": "+textureCount); + } + } else { + textureCount = 0; + } + decodedFrameCount = 0; + presentedFrameCount = 0; + displayedFrameCount = 0; + nullFrameCount = 0; + maxNullFrameCountUntilEOS = MAX_FRAMELESS_UNTIL_EOS_DEFAULT; + this.streamLoc = streamLoc; + + // Pre-parse for camera-input scheme + cameraPath = null; + cameraProps = null; + final String streamLocScheme = streamLoc.getScheme(); + if( null != streamLocScheme && streamLocScheme.equals(CameraInputScheme) ) { + final String rawPath = streamLoc.getRawPath(); + if( null != rawPath && rawPath.length() > 0 ) { + // cut-off root fwd-slash + cameraPath = rawPath.substring(1); + final URIQueryProps props = URIQueryProps.create(streamLoc, ';'); + cameraProps = props.getProperties(); + } else { + throw new IllegalArgumentException("Camera path is empty: "+streamLoc.toString()); + } + } + + this.vid = vid; + this.aid = aid; + if ( this.streamLoc != null ) { + new Thread() { + public void run() { + try { + // StreamWorker may be used, see API-doc of StreamWorker + initStreamImpl(vid, aid); + } catch (Throwable t) { + streamErr = new StreamException(t.getClass().getSimpleName()+" while initializing: "+GLMediaPlayerImpl.this.toString(), t); + changeState(GLMediaEventListener.EVENT_CHANGE_ERR, GLMediaPlayer.State.Uninitialized); + } // also initializes width, height, .. etc + } + }.start(); + } + } + } + /** + * Implementation shall set the following set of data here + * @see #vid + * @see #aid * @see #width * @see #height * @see #fps * @see #bps_stream - * @see #totalFrames + * @see #videoFrames + * @see #audioFrames * @see #acodec * @see #vcodec */ - protected abstract void initGLStreamImpl(GL gl, int[] texNames) throws IOException; - - protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) { - return new TextureSequence.TextureFrame( createTexImageImpl(gl, idx, tex, width, height, false) ); + protected abstract void initStreamImpl(int vid, int aid) throws Exception; + + @Override + public final StreamException getStreamException() { + final StreamException e; + synchronized( stateLock ) { + e = streamErr; + streamErr = null; + } + return e; } - - protected Texture createTexImageImpl(GL gl, int idx, int[] tex, int tWidth, int tHeight, boolean mustFlipVertically) { - if( 0 > tex[idx] ) { - throw new RuntimeException("TextureName "+toHexString(tex[idx])+" invalid."); + + @Override + public final void initGL(GL gl) throws IllegalStateException, StreamException, GLException { + synchronized( stateLock ) { + if(State.Initialized != state ) { + throw new IllegalStateException("Stream not in state initialized: "+this); + } + if( null != streamWorker ) { + final StreamException streamInitErr = getStreamException(); + if( null != streamInitErr ) { + streamWorker = null; // already terminated! + destroy(null); + throw streamInitErr; + } + } + try { + if( STREAM_ID_NONE != vid ) { + removeAllTextureFrames(gl); + initGLImpl(gl); + if(DEBUG) { + System.err.println("initGLImpl.X "+this); + } + videoFramesOrig = createTexFrames(gl, textureCount); + if( TEXTURE_COUNT_MIN == textureCount ) { + videoFramesFree = null; + videoFramesDecoded = null; + lastFrame = videoFramesOrig[0]; + } else { + videoFramesFree = new LFRingbuffer<TextureFrame>(videoFramesOrig); + videoFramesDecoded = new LFRingbuffer<TextureFrame>(TextureFrame[].class, textureCount); + lastFrame = videoFramesFree.getBlocking( ); + } + if( null != streamWorker ) { + streamWorker.initGL(gl); + } + } else { + removeAllTextureFrames(null); + initGLImpl(null); + setTextureFormat(-1, -1); + setTextureType(-1); + videoFramesOrig = null; + videoFramesFree = null; + videoFramesDecoded = null; + lastFrame = null; + } + changeState(0, State.Paused); + } catch (Throwable t) { + destroyImpl(gl, GLMediaEventListener.EVENT_CHANGE_ERR); // -> GLMediaPlayer.State.Uninitialized + throw new GLException("Error initializing GL resources", t); + } + } + } + /** + * Shall initialize all GL related resources, if not audio-only. + * <p> + * Shall also take care of {@link AudioSink} initialization if appropriate. + * </p> + * @param gl null for audio-only, otherwise a valid and current GL object. + * @throws IOException + * @throws GLException + */ + protected abstract void initGLImpl(GL gl) throws IOException, GLException; + + /** + * Returns the validated number of textures to be handled. + * <p> + * Default is {@link #TEXTURE_COUNT_DEFAULT} minimum textures, if <code>desiredTextureCount</code> + * is < {@link #TEXTURE_COUNT_MIN}, {@link #TEXTURE_COUNT_MIN} is returned. + * </p> + * <p> + * Implementation must at least return a texture count of {@link #TEXTURE_COUNT_MIN}, <i>two</i>, the last texture and the decoding texture. + * </p> + */ + protected int validateTextureCount(int desiredTextureCount) { + return desiredTextureCount < TEXTURE_COUNT_MIN ? TEXTURE_COUNT_MIN : desiredTextureCount; + } + + protected TextureFrame[] createTexFrames(GL gl, final int count) { + final int[] texNames = new int[count]; + gl.glGenTextures(count, texNames, 0); + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("TextureNames creation failed (num: "+count+"): err "+toHexString(err)); + } + final TextureFrame[] texFrames = new TextureFrame[count]; + for(int i=0; i<count; i++) { + texFrames[i] = createTexImage(gl, texNames[i]); + } + return texFrames; + } + protected abstract TextureFrame createTexImage(GL gl, int texName); + + protected final Texture createTexImageImpl(GL gl, int texName, int tWidth, int tHeight) { + if( 0 > texName ) { + throw new RuntimeException("TextureName "+toHexString(texName)+" invalid."); } gl.glActiveTexture(GL.GL_TEXTURE0+getTextureUnit()); - gl.glBindTexture(textureTarget, tex[idx]); + gl.glBindTexture(textureTarget, texName); { final int err = gl.glGetError(); if( GL.GL_NO_ERROR != err ) { - throw new RuntimeException("Couldn't bind textureName "+toHexString(tex[idx])+" to 2D target, err "+toHexString(err)); + throw new RuntimeException("Couldn't bind textureName "+toHexString(texName)+" to 2D target, err "+toHexString(err)); } } @@ -361,9 +700,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { gl.glTexImage2D( textureTarget, // target 0, // level - GL.GL_RGBA, // internal format - tWidth, // width - tHeight, // height + textureInternalFormat, // internal format + tWidth, // width + tHeight, // height 0, // border textureFormat, textureType, @@ -371,55 +710,691 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { { final int err = gl.glGetError(); if( GL.GL_NO_ERROR != err ) { - throw new RuntimeException("Couldn't create TexImage2D RGBA "+tWidth+"x"+tHeight+", err "+toHexString(err)); + throw new RuntimeException("Couldn't create TexImage2D RGBA "+tWidth+"x"+tHeight+", target "+toHexString(textureTarget)+ + ", ifmt "+toHexString(textureInternalFormat)+", fmt "+toHexString(textureFormat)+", type "+toHexString(textureType)+ + ", err "+toHexString(err)); } } if(DEBUG) { System.err.println("Created TexImage2D RGBA "+tWidth+"x"+tHeight+", target "+toHexString(textureTarget)+ - ", ifmt "+toHexString(GL.GL_RGBA)+", fmt "+toHexString(textureFormat)+", type "+toHexString(textureType)); + ", ifmt "+toHexString(textureInternalFormat)+", fmt "+toHexString(textureFormat)+", type "+toHexString(textureType)); } } gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, texMinMagFilter[0]); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, texMinMagFilter[1]); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, texMinMagFilter[1]); gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, texWrapST[0]); gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, texWrapST[1]); - - return com.jogamp.opengl.util.texture.TextureIO.newTexture(tex[idx], - textureTarget, + + return com.jogamp.opengl.util.texture.TextureIO.newTexture( + texName, textureTarget, tWidth, tHeight, width, height, - mustFlipVertically); - } - - protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) { - imgTex.getTexture().destroy(gl); - } - - protected void removeAllImageTextures(GL gl) { - if(null != texFrames) { - for(int i=0; i<textureCount; i++) { - final TextureSequence.TextureFrame imgTex = texFrames[i]; - if(null != imgTex) { - destroyTexImage(gl, imgTex); + !isInGLOrientation); + } + + protected void destroyTexFrame(GL gl, TextureFrame frame) { + frame.getTexture().destroy(gl); + } + + @Override + public final TextureFrame getLastTexture() throws IllegalStateException { + if( State.Paused != state && State.Playing != state ) { + throw new IllegalStateException("Instance not paused or playing: "+this); + } + return lastFrame; + } + + private final void removeAllTextureFrames(GL gl) { + final TextureFrame[] texFrames = videoFramesOrig; + videoFramesOrig = null; + videoFramesFree = null; + videoFramesDecoded = null; + lastFrame = null; + if( null != texFrames ) { + for(int i=0; i<texFrames.length; i++) { + final TextureFrame frame = texFrames[i]; + if(null != frame) { + if( null != gl ) { + destroyTexFrame(gl, frame); + } texFrames[i] = null; } + if( DEBUG ) { + System.err.println(Thread.currentThread().getName()+"> Clear TexFrame["+i+"]: "+frame+" -> null"); + } + } + } + } + + protected TextureFrame cachedFrame = null; + protected long lastTimeMillis = 0; + + private final boolean[] stGotVFrame = { false }; + + @Override + public final TextureFrame getNextTexture(GL gl) throws IllegalStateException { + synchronized( stateLock ) { + if( State.Paused != state && State.Playing != state ) { + throw new IllegalStateException("Instance not paused or playing: "+this); + } + if(State.Playing == state) { + boolean dropFrame = false; + try { + do { + final boolean droppedFrame; + if( dropFrame ) { + presentedFrameCount--; + dropFrame = false; + droppedFrame = true; + } else { + droppedFrame = false; + } + final boolean playCached = null != cachedFrame; + final int video_pts; + final boolean hasVideoFrame; + TextureFrame nextFrame; + if( playCached ) { + nextFrame = cachedFrame; + cachedFrame = null; + presentedFrameCount--; + video_pts = nextFrame.getPTS(); + hasVideoFrame = true; + } else { + if( null != videoFramesDecoded ) { + // multi-threaded and video available + nextFrame = videoFramesDecoded.get(); + if( null != nextFrame ) { + video_pts = nextFrame.getPTS(); + hasVideoFrame = true; + } else { + video_pts = TimeFrameI.INVALID_PTS; + hasVideoFrame = false; + } + } else { + // single-threaded or audio-only + video_pts = getNextSingleThreaded(gl, lastFrame, stGotVFrame); + nextFrame = lastFrame; + hasVideoFrame = stGotVFrame[0]; + } + } + final long currentTimeMillis = Platform.currentTimeMillis(); + + if( TimeFrameI.END_OF_STREAM_PTS == video_pts || + ( duration > 0 && duration <= video_pts ) || maxNullFrameCountUntilEOS <= nullFrameCount ) + { + // EOS + if( DEBUG ) { + System.err.println( "AV-EOS (getNextTexture): EOS_PTS "+(TimeFrameI.END_OF_STREAM_PTS == video_pts)+", "+this); + } + pauseImpl(true, GLMediaEventListener.EVENT_CHANGE_EOS); + + } else if( TimeFrameI.INVALID_PTS == video_pts ) { // no audio or video frame + if( null == videoFramesDecoded || !videoFramesDecoded.isEmpty() ) { + nullFrameCount++; + } + if( DEBUG ) { + final int audio_pts = getAudioPTSImpl(); + final int audio_scr = (int) ( ( currentTimeMillis - audio_scr_t0 ) * playSpeed ); + final int d_apts; + if( audio_pts != TimeFrameI.INVALID_PTS ) { + d_apts = audio_pts - audio_scr; + } else { + d_apts = 0; + } + final int video_scr = video_scr_pts + (int) ( ( currentTimeMillis - video_scr_t0 ) * playSpeed ); + final int d_vpts = video_pts - video_scr; + System.err.println( "AV~: dT "+(currentTimeMillis-lastTimeMillis)+", nullFrames "+nullFrameCount+ + getPerfStringImpl( video_scr, video_pts, d_vpts, audio_scr, audio_pts, d_apts, 0 ) + ", droppedFrame "+droppedFrame); + } + } else { // valid pts: has audio or video frame + nullFrameCount=0; + + if( hasVideoFrame ) { // has video frame + presentedFrameCount++; + + final int audio_pts = getAudioPTSImpl(); + final int audio_scr = (int) ( ( currentTimeMillis - audio_scr_t0 ) * playSpeed ); + final int d_apts; + if( audio_pts != TimeFrameI.INVALID_PTS ) { + d_apts = audio_pts - audio_scr; + } else { + d_apts = 0; + } + + final int frame_period_last = video_pts - video_pts_last; // rendering loop interrupted ? + if( videoSCR_reset || frame_period_last > frame_duration*10 ) { + videoSCR_reset = false; + video_scr_t0 = currentTimeMillis; + video_scr_pts = video_pts; + } + final int video_scr = video_scr_pts + (int) ( ( currentTimeMillis - video_scr_t0 ) * playSpeed ); + final int d_vpts = video_pts - video_scr; + // final int d_avpts = d_vpts - d_apts; + if( -VIDEO_DPTS_MAX > d_vpts || d_vpts > VIDEO_DPTS_MAX ) { + // if( -VIDEO_DPTS_MAX > d_avpts || d_avpts > VIDEO_DPTS_MAX ) { + if( DEBUG ) { + System.err.println( "AV*: dT "+(currentTimeMillis-lastTimeMillis)+", "+ + getPerfStringImpl( video_scr, video_pts, d_vpts, audio_scr, audio_pts, d_apts, 0 ) + ", "+nextFrame+", playCached " + playCached+ ", dropFrame "+dropFrame); + } + } else { + final int dpy_den = displayedFrameCount > 0 ? displayedFrameCount : 1; + final int avg_dpy_duration = ( (int) ( currentTimeMillis - video_scr_t0 ) ) / dpy_den ; // ms/f + final int maxVideoDelay = Math.min(avg_dpy_duration, MAXIMUM_VIDEO_ASYNC); + video_dpts_count++; + // video_dpts_cum = d_avpts + VIDEO_DPTS_COEFF * video_dpts_cum; + video_dpts_cum = d_vpts + VIDEO_DPTS_COEFF * video_dpts_cum; + final int video_dpts_avg_diff = video_dpts_count >= VIDEO_DPTS_NUM ? getVideoDPTSAvg() : 0; + final int dt = (int) ( video_dpts_avg_diff / playSpeed + 0.5f ); + // final int dt = (int) ( d_vpts / playSpeed + 0.5f ); + // final int dt = (int) ( d_avpts / playSpeed + 0.5f ); + final TextureFrame _nextFrame = nextFrame; + if( dt > maxVideoDelay ) { + cachedFrame = nextFrame; + nextFrame = null; + } else if ( !droppedFrame && dt < -maxVideoDelay && null != videoFramesDecoded && videoFramesDecoded.size() > 0 ) { + // only drop if prev. frame has not been dropped and + // frame is too late and one decoded frame is already available. + dropFrame = true; + } + video_pts_last = video_pts; + if( DEBUG ) { + System.err.println( "AV_: dT "+(currentTimeMillis-lastTimeMillis)+", "+ + getPerfStringImpl( video_scr, video_pts, d_vpts, + audio_scr, audio_pts, d_apts, + video_dpts_avg_diff ) + + ", avg dpy-fps "+avg_dpy_duration+" ms/f, maxD "+maxVideoDelay+" ms, "+_nextFrame+", playCached " + playCached + ", dropFrame "+dropFrame); + } + } + } // has video frame + } // has audio or video frame + + if( null != videoFramesFree && null != nextFrame ) { + // Had frame and not single threaded ? (TEXTURE_COUNT_MIN < textureCount) + final TextureFrame _lastFrame = lastFrame; + lastFrame = nextFrame; + if( null != _lastFrame ) { + videoFramesFree.putBlocking(_lastFrame); + } + } + lastTimeMillis = currentTimeMillis; + } while( dropFrame ); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + displayedFrameCount++; + return lastFrame; + } + } + protected void preNextTextureImpl(GL gl) {} + protected void postNextTextureImpl(GL gl) {} + /** + * Process stream until the next video frame, i.e. {@link TextureFrame}, has been reached. + * Audio frames, i.e. {@link AudioSink.AudioFrame}, shall be handled in the process. + * <p> + * Video frames shall be ignored, if {@link #getVID()} is {@link #STREAM_ID_NONE}. + * </p> + * <p> + * Audio frames shall be ignored, if {@link #getAID()} is {@link #STREAM_ID_NONE}. + * </p> + * <p> + * Method may be invoked on the <a href="#streamworker"><i>StreamWorker</i> decoding thread</a>. + * </p> + * <p> + * Implementation shall care of OpenGL synchronization as required, e.g. glFinish()/glFlush()! + * </p> + * @param gl valid and current GL instance, shall be <code>null</code> for audio only. + * @param nextFrame the {@link TextureFrame} to store the video PTS and texture data, + * shall be <code>null</code> for audio only. + * @return the last processed video PTS value, maybe {@link TimeFrameI#INVALID_PTS} if video frame is invalid or n/a. + * Will be {@link TimeFrameI#END_OF_STREAM_PTS} if end of stream reached. + */ + protected abstract int getNextTextureImpl(GL gl, TextureFrame nextFrame); + + protected final int getNextSingleThreaded(final GL gl, final TextureFrame nextFrame, boolean[] gotVFrame) throws InterruptedException { + final int pts; + if( STREAM_ID_NONE != vid ) { + preNextTextureImpl(gl); + pts = getNextTextureImpl(gl, nextFrame); + postNextTextureImpl(gl); + if( TimeFrameI.INVALID_PTS != pts ) { + newFrameAvailable(nextFrame, Platform.currentTimeMillis()); + gotVFrame[0] = true; + } else { + gotVFrame[0] = false; + } + } else { + // audio only + pts = getNextTextureImpl(null, null); + gotVFrame[0] = false; + } + return pts; + } + + + /** + * {@inheritDoc} + * <p> + * Note: All {@link AudioSink} operations are performed from {@link GLMediaPlayerImpl}, + * i.e. {@link #play()}, {@link #pause(boolean)}, {@link #seek(int)}, {@link #setPlaySpeed(float)}, {@link #getAudioPTS()}. + * </p> + * <p> + * Implementations using an {@link AudioSink} shall write it's instance to {@link #audioSink} + * from within their {@link #initStreamImpl(int, int)} implementation. + * </p> + */ + @Override + public final AudioSink getAudioSink() { return audioSink; } + + /** + * To be called from implementation at 1st PTS after start + * w/ current pts value in milliseconds. + * @param audio_scr_t0 + */ + protected void setFirstAudioPTS2SCR(int pts) { + if( audioSCR_reset ) { + audio_scr_t0 = Platform.currentTimeMillis() - pts; + audioSCR_reset = false; + } + } + private void flushAllVideoFrames() { + if( null != videoFramesFree ) { + videoFramesFree.resetFull(videoFramesOrig); + lastFrame = videoFramesFree.get(); + if( null == lastFrame ) { throw new InternalError("XXX"); } + videoFramesDecoded.clear(); + } + cachedFrame = null; + } + private void resetAVPTSAndFlush() { + video_dpts_cum = 0; + video_dpts_count = 0; + resetAVPTS(); + flushAllVideoFrames(); + if( null != audioSink ) { + audioSink.flush(); + } + } + private void resetAVPTS() { + nullFrameCount = 0; + presentedFrameCount = 0; + displayedFrameCount = 0; + decodedFrameCount = 0; + audioSCR_reset = true; + videoSCR_reset = true; + } + private final int getVideoDPTSAvg() { + return (int) ( video_dpts_cum * (1.0f - VIDEO_DPTS_COEFF) + 0.5f ); + } + + private final void newFrameAvailable(TextureFrame frame, long currentTimeMillis) { + decodedFrameCount++; + if( 0 == frame.getDuration() ) { // patch frame duration if not set already + frame.setDuration( (int) frame_duration ); + } + synchronized(eventListenersLock) { + for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) { + i.next().newFrameAvailable(this, frame, currentTimeMillis); + } + } + } + + /** + * After {@link GLMediaPlayerImpl#initStreamImpl(int, int) initStreamImpl(..)} is completed via + * {@link GLMediaPlayerImpl#updateAttributes(int, int, int, int, int, int, int, float, int, int, int, String, String) updateAttributes(..)}, + * the latter decides whether StreamWorker is being used. + */ + class StreamWorker extends Thread { + private volatile boolean isRunning = false; + private volatile boolean isActive = false; + private volatile boolean isBlocked = false; + + private volatile boolean shallPause = true; + private volatile boolean shallStop = false; + + private volatile GLContext sharedGLCtx = null; + private boolean sharedGLCtxCurrent = false; + private GLDrawable dummyDrawable = null; + + /** + * Starts this daemon thread, + * <p> + * This thread pauses after it's started! + * </p> + **/ + StreamWorker() { + setDaemon(true); + synchronized(this) { + start(); + while( !isRunning ) { + this.notifyAll(); // wake-up startup-block + try { + this.wait(); // wait until started + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + private void makeCurrent(GLContext ctx) { + if( GLContext.CONTEXT_NOT_CURRENT >= ctx.makeCurrent() ) { + throw new GLException("Couldn't make ctx current: "+ctx); + } + } + + private void destroySharedGL() { + if( null != sharedGLCtx ) { + if( sharedGLCtx.isCreated() ) { + // Catch dispose GLExceptions by GLEventListener, just 'print' them + // so we can continue with the destruction. + try { + sharedGLCtx.destroy(); + } catch (GLException gle) { + gle.printStackTrace(); + } + } + sharedGLCtx = null; + } + if( null != dummyDrawable ) { + final AbstractGraphicsDevice device = dummyDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + dummyDrawable.setRealized(false); + dummyDrawable = null; + device.close(); + } + } + + public final synchronized void initGL(GL gl) { + final GLContext glCtx = gl.getContext(); + final boolean glCtxCurrent = glCtx.isCurrent(); + final GLProfile glp = gl.getGLProfile(); + final GLDrawableFactory factory = GLDrawableFactory.getFactory(glp); + final AbstractGraphicsDevice device = glCtx.getGLDrawable().getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + dummyDrawable = factory.createDummyDrawable(device, true, glCtx.getGLDrawable().getChosenGLCapabilities(), null); // own device! + dummyDrawable.setRealized(true); + sharedGLCtx = dummyDrawable.createContext(glCtx); + makeCurrent(sharedGLCtx); + if( glCtxCurrent ) { + makeCurrent(glCtx); + } else { + sharedGLCtx.release(); + } + } + public final synchronized void doPause() { + if( isActive ) { + shallPause = true; + if( Thread.currentThread() != this ) { + if( isBlocked && isActive ) { + this.interrupt(); + } + while( isActive ) { + try { + this.wait(); // wait until paused + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + public final synchronized void doResume() { + if( isRunning && !isActive ) { + shallPause = false; + if( Thread.currentThread() != this ) { + while( !isActive ) { + this.notifyAll(); // wake-up pause-block + try { + this.wait(); // wait until resumed + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + public final synchronized void doStop() { + if( isRunning ) { + shallStop = true; + if( Thread.currentThread() != this ) { + if( isBlocked && isRunning ) { + this.interrupt(); + } + while( isRunning ) { + this.notifyAll(); // wake-up pause-block (opt) + try { + this.wait(); // wait until stopped + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + public final boolean isRunning() { return isRunning; } + public final boolean isActive() { return isActive; } + + @Override + public final void run() { + setName(getName()+"-StreamWorker_"+StreamWorkerInstanceId); + StreamWorkerInstanceId++; + + synchronized ( this ) { + isRunning = true; + this.notifyAll(); // wake-up ctor() + } + + while( !shallStop ){ + if( shallPause ) { + synchronized ( this ) { + if( sharedGLCtxCurrent ) { + postNextTextureImpl(sharedGLCtx.getGL()); + sharedGLCtx.release(); + } + while( shallPause && !shallStop ) { + isActive = false; + this.notifyAll(); // wake-up doPause() + try { + this.wait(); // wait until resumed + } catch (InterruptedException e) { + if( !shallPause ) { + e.printStackTrace(); + } + } + } + if( sharedGLCtxCurrent ) { + makeCurrent(sharedGLCtx); + preNextTextureImpl(sharedGLCtx.getGL()); + } + isActive = true; + this.notifyAll(); // wake-up doResume() + } + } + if( !sharedGLCtxCurrent && null != sharedGLCtx ) { + synchronized ( this ) { + if( null != sharedGLCtx ) { + makeCurrent( sharedGLCtx ); + preNextTextureImpl(sharedGLCtx.getGL()); + sharedGLCtxCurrent = true; + } + if( null == videoFramesFree ) { + throw new InternalError("XXX videoFramesFree is null"); + } + } + } + + if( !shallStop ) { + TextureFrame nextFrame = null; + try { + isBlocked = true; + final GL gl; + if( STREAM_ID_NONE != vid ) { + nextFrame = videoFramesFree.getBlocking(); + nextFrame.setPTS( TimeFrameI.INVALID_PTS ); // mark invalid until processed! + gl = sharedGLCtx.getGL(); + } else { + gl = null; + } + isBlocked = false; + final int vPTS = getNextTextureImpl(gl, nextFrame); + boolean audioEOS = false; + if( TimeFrameI.INVALID_PTS != vPTS ) { + if( null != nextFrame ) { + if( STREAM_WORKER_DELAY > 0 ) { + Thread.sleep(STREAM_WORKER_DELAY); + } + if( !videoFramesDecoded.put(nextFrame) ) { + throw new InternalError("XXX: free "+videoFramesFree+", decoded "+videoFramesDecoded+", "+GLMediaPlayerImpl.this); + } + newFrameAvailable(nextFrame, Platform.currentTimeMillis()); + nextFrame = null; + } else { + // audio only + if( TimeFrameI.END_OF_STREAM_PTS == vPTS || ( duration > 0 && duration < vPTS ) ) { + audioEOS = true; + } else { + nullFrameCount = 0; + } + } + } else if( null == nextFrame ) { + // audio only + audioEOS = maxNullFrameCountUntilEOS <= nullFrameCount; + if( null == audioSink || 0 == audioSink.getEnqueuedFrameCount() ) { + nullFrameCount++; + } + } + if( audioEOS ) { + // state transition incl. notification + synchronized ( this ) { + shallPause = true; + isActive = false; + this.notifyAll(); // wake-up potential do*() + } + if( DEBUG ) { + System.err.println( "AV-EOS (StreamWorker): EOS_PTS "+(TimeFrameI.END_OF_STREAM_PTS == vPTS)+", "+GLMediaPlayerImpl.this); + } + pauseImpl(true, GLMediaEventListener.EVENT_CHANGE_EOS); + } + } catch (InterruptedException e) { + isBlocked = false; + if( !shallStop && !shallPause ) { + streamErr = new StreamException("InterruptedException while decoding: "+GLMediaPlayerImpl.this.toString(), e); + } + } catch (Throwable t) { + streamErr = new StreamException(t.getClass().getSimpleName()+" while decoding: "+GLMediaPlayerImpl.this.toString(), t); + } finally { + if( null != nextFrame ) { // put back + videoFramesFree.put(nextFrame); + } + if( null != streamErr ) { + if( DEBUG ) { + final Throwable t = null != streamErr.getCause() ? streamErr.getCause() : streamErr; + System.err.println("Caught StreamException: "+t.getMessage()); + t.printStackTrace(); + } + // state transition incl. notification + synchronized ( this ) { + shallPause = true; + isActive = false; + this.notifyAll(); // wake-up potential do*() + } + pauseImpl(true, GLMediaEventListener.EVENT_CHANGE_ERR); + } + } + } + } + synchronized ( this ) { + if( sharedGLCtxCurrent ) { + postNextTextureImpl(sharedGLCtx.getGL()); + } + destroySharedGL(); + isRunning = false; + isActive = false; + this.notifyAll(); // wake-up doStop() + } + } + } + static int StreamWorkerInstanceId = 0; + private volatile StreamWorker streamWorker = null; + private volatile StreamException streamErr = null; + + protected final int addStateEventMask(int event_mask, State newState) { + if( state != newState ) { + switch( newState ) { + case Uninitialized: + event_mask |= GLMediaEventListener.EVENT_CHANGE_UNINIT; + break; + case Initialized: + event_mask |= GLMediaEventListener.EVENT_CHANGE_INIT; + break; + case Playing: + event_mask |= GLMediaEventListener.EVENT_CHANGE_PLAY; + break; + case Paused: + event_mask |= GLMediaEventListener.EVENT_CHANGE_PAUSE; + break; + } + } + return event_mask; + } + + protected final void attributesUpdated(int event_mask) { + if( 0 != event_mask ) { + final long now = Platform.currentTimeMillis(); + synchronized(eventListenersLock) { + for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) { + i.next().attributesChanged(this, event_mask, now); + } } } - texFrameMap.clear(); } - protected final void updateAttributes(int width, int height, int bps_stream, int bps_video, int bps_audio, - float fps, int totalFrames, int duration, - String vcodec, String acodec) { + protected final void changeState(int event_mask, State newState) { + event_mask = addStateEventMask(event_mask, newState); + if( 0 != event_mask ) { + state = newState; + attributesUpdated( event_mask ); + } + } + + protected final void updateAttributes(int vid, int aid, int width, int height, int bps_stream, + int bps_video, int bps_audio, float fps, + int videoFrames, int audioFrames, int duration, String vcodec, String acodec) { int event_mask = 0; + final boolean wasUninitialized = state == State.Uninitialized; + + if( wasUninitialized ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_INIT; + state = State.Initialized; + } + if( STREAM_ID_AUTO == vid ) { + vid = STREAM_ID_NONE; + } + if( this.vid != vid ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_VID; + this.vid = vid; + } + if( STREAM_ID_AUTO == vid ) { + vid = STREAM_ID_NONE; + } + if( this.aid != aid ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_AID; + this.aid = aid; + } if( this.width != width || this.height != height ) { event_mask |= GLMediaEventListener.EVENT_CHANGE_SIZE; this.width = width; this.height = height; - } + } if( this.fps != fps ) { event_mask |= GLMediaEventListener.EVENT_CHANGE_FPS; this.fps = fps; + if( 0 != fps ) { + this.frame_duration = 1000f / fps; + this.maxNullFrameCountUntilEOS = MAX_FRAMELESS_MS_UNTIL_EOS / (int)this.frame_duration; + } else { + this.frame_duration = 0; + this.maxNullFrameCountUntilEOS = MAX_FRAMELESS_UNTIL_EOS_DEFAULT; + } } if( this.bps_stream != bps_stream || this.bps_video != bps_video || this.bps_audio != bps_audio ) { event_mask |= GLMediaEventListener.EVENT_CHANGE_BPS; @@ -427,12 +1402,13 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { this.bps_video = bps_video; this.bps_audio = bps_audio; } - if( this.totalFrames != totalFrames || this.duration != duration ) { + if( this.videoFrames != videoFrames || this.audioFrames != audioFrames || this.duration != duration ) { event_mask |= GLMediaEventListener.EVENT_CHANGE_LENGTH; - this.totalFrames = totalFrames; + this.videoFrames = videoFrames; + this.audioFrames = audioFrames; this.duration = duration; } - if( (null!=acodec && acodec.length()>0 && !this.acodec.equals(acodec)) ) { + if( (null!=acodec && acodec.length()>0 && !this.acodec.equals(acodec)) ) { event_mask |= GLMediaEventListener.EVENT_CHANGE_CODEC; this.acodec = acodec; } @@ -443,97 +1419,152 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { if(0==event_mask) { return; } - attributesUpdated(event_mask); - } - - protected final void attributesUpdated(int event_mask) { - synchronized(eventListenersLock) { - for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) { - i.next().attributesChanges(this, event_mask, System.currentTimeMillis()); + if( wasUninitialized ) { + if( null != streamWorker ) { + throw new InternalError("XXX: StreamWorker not null - "+this); + } + if( TEXTURE_COUNT_MIN < textureCount || STREAM_ID_NONE == vid ) { // Enable StreamWorker for 'audio only' as well (Bug 918). + streamWorker = new StreamWorker(); + } + if( DEBUG ) { + System.err.println("XXX Initialize @ updateAttributes: "+this); } } + attributesUpdated(event_mask); } - protected final void newFrameAvailable() { - frameNumber++; - synchronized(eventListenersLock) { - for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) { - i.next().newFrameAvailable(this, System.currentTimeMillis()); + + protected void setIsGLOriented(boolean isGLOriented) { + if( isInGLOrientation != isGLOriented ) { + if( DEBUG ) { + System.err.println("XXX gl-orient "+isInGLOrientation+" -> "+isGLOriented); } + isInGLOrientation = isGLOriented; + for(int i=0; i<videoFramesOrig.length; i++) { + videoFramesOrig[i].getTexture().setMustFlipVertically(!isGLOriented); + } + attributesUpdated(GLMediaEventListener.EVENT_CHANGE_SIZE); } } - + @Override - public final synchronized State destroy(GL gl) { - destroyImpl(gl); - removeAllImageTextures(gl); - state = State.Uninitialized; - return state; + public final URI getURI() { + return streamLoc; } - protected abstract void destroyImpl(GL gl); @Override - public final synchronized URLConnection getURLConnection() { - return urlConn; - } + public final int getVID() { return vid; } + + @Override + public final int getAID() { return aid; } @Override - public final synchronized String getVideoCodec() { + public final String getVideoCodec() { return vcodec; } @Override - public final synchronized String getAudioCodec() { + public final String getAudioCodec() { return acodec; } @Override - public final synchronized long getTotalFrames() { - return totalFrames; + public final int getVideoFrames() { + return videoFrames; + } + + @Override + public final int getAudioFrames() { + return audioFrames; } @Override - public final synchronized int getDuration() { + public final int getDuration() { return duration; } - + @Override - public final synchronized long getStreamBitrate() { + public final long getStreamBitrate() { return bps_stream; } @Override - public final synchronized int getVideoBitrate() { + public final int getVideoBitrate() { return bps_video; } - + @Override - public final synchronized int getAudioBitrate() { + public final int getAudioBitrate() { return bps_audio; } - + @Override - public final synchronized float getFramerate() { + public final float getFramerate() { return fps; } @Override - public final synchronized int getWidth() { + public final boolean isGLOriented() { + return isInGLOrientation; + } + + @Override + public final int getWidth() { return width; } @Override - public final synchronized int getHeight() { + public final int getHeight() { return height; } @Override - public final synchronized String toString() { - final float ct = getCurrentPosition() / 1000.0f, tt = getDuration() / 1000.0f; - final String loc = ( null != urlConn ) ? urlConn.getURL().toExternalForm() : "<undefined stream>" ; - return "GLMediaPlayer["+state+", "+frameNumber+"/"+totalFrames+" frames, "+ct+"/"+tt+"s, speed "+playSpeed+", "+bps_stream+" bps, "+ - "Texture[count "+textureCount+", target "+toHexString(textureTarget)+", format "+toHexString(textureFormat)+", type "+toHexString(textureType)+"], "+ - "Stream[Video[<"+vcodec+">, "+width+"x"+height+", "+fps+" fps, "+bps_video+" bsp], "+ - "Audio[<"+acodec+">, "+bps_audio+" bsp]], "+loc+"]"; + public final String toString() { + final float tt = getDuration() / 1000.0f; + final String loc = ( null != streamLoc ) ? streamLoc.toString() : "<undefined stream>" ; + final int freeVideoFrames = null != videoFramesFree ? videoFramesFree.size() : 0; + final int decVideoFrames = null != videoFramesDecoded ? videoFramesDecoded.size() : 0; + final int video_scr = video_scr_pts + (int) ( ( Platform.currentTimeMillis() - video_scr_t0 ) * playSpeed ); + final String camPath = null != cameraPath ? ", camera: "+cameraPath : ""; + return "GLMediaPlayer["+state+", vSCR "+video_scr+", frames[p "+presentedFrameCount+", d "+decodedFrameCount+", t "+videoFrames+" ("+tt+" s), z "+nullFrameCount+" / "+maxNullFrameCountUntilEOS+"], "+ + "speed "+playSpeed+", "+bps_stream+" bps, hasSW "+(null!=streamWorker)+ + ", Texture[count "+textureCount+", free "+freeVideoFrames+", dec "+decVideoFrames+", tagt "+toHexString(textureTarget)+", ifmt "+toHexString(textureInternalFormat)+", fmt "+toHexString(textureFormat)+", type "+toHexString(textureType)+"], "+ + "Video[id "+vid+", <"+vcodec+">, "+width+"x"+height+", glOrient "+isInGLOrientation+", "+fps+" fps, "+frame_duration+" fdur, "+bps_video+" bps], "+ + "Audio[id "+aid+", <"+acodec+">, "+bps_audio+" bps, "+audioFrames+" frames], uri "+loc+camPath+"]"; + } + + @Override + public final String getPerfString() { + final long currentTimeMillis = Platform.currentTimeMillis(); + final int video_scr = video_scr_pts + (int) ( ( currentTimeMillis - video_scr_t0 ) * playSpeed ); + final int d_vpts = video_pts_last - video_scr; + final int audio_scr = (int) ( ( currentTimeMillis - audio_scr_t0 ) * playSpeed ); + final int audio_pts = getAudioPTSImpl(); + final int d_apts = audio_pts - audio_scr; + return getPerfStringImpl( video_scr, video_pts_last, d_vpts, audio_scr, audio_pts, d_apts, getVideoDPTSAvg() ); + } + private final String getPerfStringImpl(final int video_scr, final int video_pts, final int d_vpts, + final int audio_scr, final int audio_pts, final int d_apts, + final int video_dpts_avg_diff) { + final float tt = getDuration() / 1000.0f; + final String audioSinkInfo; + final AudioSink audioSink = getAudioSink(); + if( null != audioSink ) { + audioSinkInfo = "AudioSink[frames [p "+audioSink.getEnqueuedFrameCount()+", q "+audioSink.getQueuedFrameCount()+", f "+audioSink.getFreeFrameCount()+", c "+audioSink.getFrameCount()+"], time "+audioSink.getQueuedTime()+", bytes "+audioSink.getQueuedByteCount()+"]"; + } else { + audioSinkInfo = ""; + } + final int freeVideoFrames, decVideoFrames; + if( null != videoFramesFree ) { + freeVideoFrames = videoFramesFree.size(); + decVideoFrames = videoFramesDecoded.size(); + } else { + freeVideoFrames = 0; + decVideoFrames = 0; + } + return state+", frames[(p "+presentedFrameCount+", d "+decodedFrameCount+") / "+videoFrames+", "+tt+" s, z "+nullFrameCount+" / "+maxNullFrameCountUntilEOS+"], "+ + "speed " + playSpeed+", dAV "+( d_vpts - d_apts )+", vSCR "+video_scr+", vpts "+video_pts+", dSCR["+d_vpts+", avrg "+video_dpts_avg_diff+"], "+ + "aSCR "+audio_scr+", apts "+audio_pts+" ( "+d_apts+" ), "+audioSinkInfo+ + ", Texture[count "+textureCount+", free "+freeVideoFrames+", dec "+decVideoFrames+"]"; } @Override @@ -557,13 +1588,30 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } @Override - public final synchronized GLMediaEventListener[] getEventListeners() { + public final GLMediaEventListener[] getEventListeners() { synchronized(eventListenersLock) { return eventListeners.toArray(new GLMediaEventListener[eventListeners.size()]); } } - private Object eventListenersLock = new Object(); + private final Object eventListenersLock = new Object(); + + @Override + public final Object getAttachedObject(String name) { + return attachedObjects.get(name); + } + + @Override + public final Object attachObject(String name, Object obj) { + return attachedObjects.put(name, obj); + } + + @Override + public final Object detachObject(String name) { + return attachedObjects.remove(name); + } + + private final HashMap<String, Object> attachedObjects = new HashMap<String, Object>(); protected static final String toHexString(long v) { return "0x"+Long.toHexString(v); @@ -571,5 +1619,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { protected static final String toHexString(int v) { return "0x"+Integer.toHexString(v); } - -}
\ No newline at end of file + protected static final int getPropIntVal(Map<String, String> props, String key) { + final String val = props.get(key); + try { + return Integer.valueOf(val).intValue(); + } catch (NumberFormatException nfe) { + if(DEBUG) { + System.err.println("Not a valid integer for <"+key+">: <"+val+">"); + } + } + return 0; + } +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java new file mode 100644 index 000000000..6e006d9c0 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/JavaSoundAudioSink.java @@ -0,0 +1,229 @@ +package jogamp.opengl.util.av; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.SourceDataLine; + +import com.jogamp.opengl.util.av.AudioSink; + +/*** + * JavaSound Audio Sink + * <p> + * FIXME: Parameterize .. all configs .. best via an init-method, passing requested + * audio capabilities + * </p> + */ +public class JavaSoundAudioSink implements AudioSink { + + // Chunk of audio processed at one time + public static final int BUFFER_SIZE = 1000; + public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2; + private static final boolean staticAvailable; + + // Sample time values + // public static final double SAMPLE_TIME_IN_SECS = 1.0 / DEFAULT_SAMPLE_RATE; + // public static final double BUFFER_TIME_IN_SECS = SAMPLE_TIME_IN_SECS * SAMPLES_PER_BUFFER; + + private javax.sound.sampled.AudioFormat format; + private DataLine.Info info; + private SourceDataLine auline; + private int bufferCount; + private byte [] sampleData = new byte[BUFFER_SIZE]; + private boolean initialized = false; + private AudioSink.AudioFormat chosenFormat = null; + + private volatile boolean playRequested = false; + private float volume = 1.0f; + + static { + boolean ok = false; + try { + AudioSystem.getAudioFileTypes(); + ok = true; + } catch (Throwable t) { + + } + staticAvailable=ok; + } + + @Override + public String toString() { + return "JavaSoundSink[init "+initialized+", dataLine "+info+", source "+auline+", bufferCount "+bufferCount+ + ", chosen "+chosenFormat+", jsFormat "+format; + } + + @Override + public final float getPlaySpeed() { return 1.0f; } // FIXME + + @Override + public final boolean setPlaySpeed(float rate) { + return false; // FIXME + } + + @Override + public final float getVolume() { + // FIXME + return volume; + } + + @Override + public final boolean setVolume(float v) { + // FIXME + volume = v; + return true; + } + + @Override + public AudioSink.AudioFormat getPreferredFormat() { + return DefaultFormat; + } + + @Override + public final int getMaxSupportedChannels() { + return 2; + } + + @Override + public final boolean isSupported(AudioSink.AudioFormat format) { + return true; + } + + @Override + public boolean init(AudioSink.AudioFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { + if( !staticAvailable ) { + return false; + } + // Create the audio format we wish to use + format = new javax.sound.sampled.AudioFormat(requestedFormat.sampleRate, requestedFormat.sampleSize, requestedFormat.channelCount, requestedFormat.signed, !requestedFormat.littleEndian); + + // Create dataline info object describing line format + info = new DataLine.Info(SourceDataLine.class, format); + + // Clear buffer initially + Arrays.fill(sampleData, (byte) 0); + try{ + // Get line to write data to + auline = (SourceDataLine) AudioSystem.getLine(info); + auline.open(format); + auline.start(); + System.out.println("JavaSound audio sink"); + initialized=true; + chosenFormat = requestedFormat; + } catch (Exception e) { + initialized=false; + } + return true; + } + + @Override + public boolean isPlaying() { + return playRequested && auline.isRunning(); + } + + @Override + public void play() { + if( null != auline ) { + playRequested = true; + playImpl(); + } + } + private void playImpl() { + if( playRequested && !auline.isRunning() ) { + auline.start(); + } + } + + @Override + public void pause() { + if( null != auline ) { + playRequested = false; + auline.stop(); + } + } + + @Override + public void flush() { + if( null != auline ) { + playRequested = false; + auline.stop(); + auline.flush(); + } + } + + @Override + public final int getEnqueuedFrameCount() { + return 0; // FIXME + } + + @Override + public int getFrameCount() { + return 1; + } + + @Override + public int getQueuedFrameCount() { + return 0; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void destroy() { + initialized = false; + chosenFormat = null; + // FIXEM: complete code! + } + + @Override + public AudioFrame enqueueData(AudioDataFrame audioDataFrame) { + int byteSize = audioDataFrame.getByteSize(); + final ByteBuffer byteBuffer = audioDataFrame.getData(); + final byte[] bytes = new byte[byteSize]; + final int p = byteBuffer.position(); + byteBuffer.get(bytes, 0, byteSize); + byteBuffer.position(p); + + int written = 0; + int len; + while (byteSize > 0) { + len = auline.write(bytes, written, byteSize); + byteSize -= len; + written += len; + } + playImpl(); + return audioDataFrame; + } + + @Override + public AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount) { + return enqueueData(new AudioDataFrame(pts, chosenFormat.getBytesDuration(byteCount), bytes, byteCount)); + } + + @Override + public int getQueuedByteCount() { + return auline.getBufferSize() - auline.available(); + } + + @Override + public int getFreeFrameCount() { + return auline.available(); + } + + @Override + public int getQueuedTime() { + return getQueuedTimeImpl( getQueuedByteCount() ); + } + private final int getQueuedTimeImpl(int byteCount) { + final int bytesPerSample = chosenFormat.sampleSize >>> 3; // /8 + return byteCount / ( chosenFormat.channelCount * bytesPerSample * ( chosenFormat.sampleRate / 1000 ) ); + } + + @Override + public final int getPTS() { return 0; } // FIXME +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java b/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java new file mode 100644 index 000000000..8d3dbdf44 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/NullAudioSink.java @@ -0,0 +1,129 @@ +package jogamp.opengl.util.av; + + +import java.nio.ByteBuffer; + +import com.jogamp.opengl.util.av.AudioSink; + +public class NullAudioSink implements AudioSink { + + @Override + public boolean isInitialized() { + return true; + } + + private volatile float playSpeed = 1.0f; + private volatile boolean playRequested = false; + private float volume = 1.0f; + + @Override + public final float getPlaySpeed() { return playSpeed; } + + @Override + public final boolean setPlaySpeed(float rate) { + if( Math.abs(1.0f - rate) < 0.01f ) { + rate = 1.0f; + } + playSpeed = rate; + return true; + } + + @Override + public final float getVolume() { + // FIXME + return volume; + } + + @Override + public final boolean setVolume(float v) { + // FIXME + volume = v; + return true; + } + + @Override + public AudioFormat getPreferredFormat() { + return DefaultFormat; + } + + @Override + public final int getMaxSupportedChannels() { + return 8; + } + + @Override + public final boolean isSupported(AudioFormat format) { + return true; + } + + @Override + public boolean init(AudioFormat requestedFormat, float frameDuration, int initialQueueSize, int queueGrowAmount, int queueLimit) { + return true; + } + + @Override + public boolean isPlaying() { + return playRequested; + } + + @Override + public void play() { + playRequested = true; + } + + @Override + public void pause() { + playRequested = false; + } + + @Override + public void flush() { + } + + @Override + public void destroy() { + } + + @Override + public final int getEnqueuedFrameCount() { + return 0; + } + + @Override + public int getFrameCount() { + return 0; + } + + @Override + public int getQueuedFrameCount() { + return 0; + } + + @Override + public int getQueuedByteCount() { + return 0; + } + + @Override + public int getQueuedTime() { + return 0; + } + + @Override + public final int getPTS() { return 0; } + + @Override + public int getFreeFrameCount() { + return 1; + } + + @Override + public AudioFrame enqueueData(AudioDataFrame audioDataFrame) { + return null; + } + + @Override + public AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount) { + return null; + } +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java index 3d740d6b2..79129e563 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -32,12 +32,15 @@ import java.net.URLConnection; import java.nio.ByteBuffer; import javax.media.opengl.GL; +import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import jogamp.opengl.util.av.GLMediaPlayerImpl; import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.Platform; import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; import com.jogamp.opengl.util.texture.TextureIO; @@ -49,114 +52,126 @@ import com.jogamp.opengl.util.texture.TextureSequence; */ public class NullGLMediaPlayer extends GLMediaPlayerImpl { private TextureData texData = null; - private TextureSequence.TextureFrame frame = null; private int pos_ms = 0; - private int pos_start = 0; - + private long pos_start = 0; + public NullGLMediaPlayer() { super(); - this.setTextureCount(1); + } @Override - protected boolean setPlaySpeedImpl(float rate) { + protected final boolean setPlaySpeedImpl(float rate) { return false; } @Override - protected boolean startImpl() { - pos_start = (int)System.currentTimeMillis(); + protected final boolean playImpl() { + pos_start = Platform.currentTimeMillis(); return true; } @Override - protected boolean pauseImpl() { + protected final boolean pauseImpl() { return true; } @Override - protected boolean stopImpl() { - return true; - } - - @Override - protected int seekImpl(int msec) { + protected final int seekImpl(int msec) { pos_ms = msec; validatePos(); return pos_ms; } - - @Override - protected TextureSequence.TextureFrame getLastTextureImpl() { - return frame; - } @Override - protected TextureSequence.TextureFrame getNextTextureImpl(GL gl, boolean blocking) { - return frame; + protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) { + final int pts = getAudioPTSImpl(); + nextFrame.setPTS( pts ); + return pts; } - + @Override - protected int getCurrentPositionImpl() { - pos_ms = (int)System.currentTimeMillis() - pos_start; + protected final int getAudioPTSImpl() { + pos_ms = (int) ( Platform.currentTimeMillis() - pos_start ); validatePos(); return pos_ms; } @Override - protected void destroyImpl(GL gl) { + protected final void destroyImpl(GL gl) { + if(null != texData) { + texData.destroy(); + texData = null; + } } - - @Override - protected void initGLStreamImpl(GL gl, int[] texNames) throws IOException { + + public final static TextureData createTestTextureData() { + TextureData res = null; try { - URLConnection urlConn = IOUtil.getResource("jogl/util/data/av/test-ntsc01-160x90.png", NullGLMediaPlayer.class.getClassLoader()); + URLConnection urlConn = IOUtil.getResource("jogl/util/data/av/test-ntsc01-57x32.png", NullGLMediaPlayer.class.getClassLoader()); if(null != urlConn) { - texData = TextureIO.newTextureData(GLProfile.getGL2ES2(), urlConn.getInputStream(), false, TextureIO.PNG); + res = TextureIO.newTextureData(GLProfile.getGL2ES2(), urlConn.getInputStream(), false, TextureIO.PNG); } } catch (Exception e) { e.printStackTrace(); } - if(null != texData) { - width = texData.getWidth(); - height = texData.getHeight(); - } else { - width = 640; - height = 480; - ByteBuffer buffer = Buffers.newDirectByteBuffer(width*height*4); + if(null == res) { + final int w = 160; + final int h = 90; + ByteBuffer buffer = Buffers.newDirectByteBuffer(w*h*4); while(buffer.hasRemaining()) { buffer.put((byte) 0xEA); buffer.put((byte) 0xEA); buffer.put((byte) 0xEA); buffer.put((byte) 0xEA); } buffer.rewind(); - texData = new TextureData(GLProfile.getGL2ES2(), - GL.GL_RGBA, width, height, 0, - GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false, + res = new TextureData(GLProfile.getGL2ES2(), + GL.GL_RGBA, w, h, 0, + GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false, false, false, buffer, null); } - fps = 24f; - duration = 10*60*1000; // msec - totalFrames = (int) ( (duration/1000)*fps ); - vcodec = "png-static"; + return res; + } + + @Override + protected final void initStreamImpl(int vid, int aid) throws IOException { + texData = createTestTextureData(); + final float _fps = 24f; + final int _duration = 10*60*1000; // msec + final int _totalFrames = (int) ( (_duration/1000)*_fps ); + updateAttributes(0 /* fake */, GLMediaPlayer.STREAM_ID_NONE, + texData.getWidth(), texData.getHeight(), 0, + 0, 0, _fps, + _totalFrames, 0, _duration, "png-static", null); + } + @Override + protected final void initGLImpl(GL gl) throws IOException, GLException { + isInGLOrientation = true; + } + + /** + * {@inheritDoc} + * <p> + * Returns {@link GLMediaPlayer#TEXTURE_COUNT_MIN}. + * </p> + */ + @Override + protected int validateTextureCount(int desiredTextureCount) { + return TEXTURE_COUNT_MIN; } - + @Override - protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) { - Texture texture = super.createTexImageImpl(gl, idx, tex, width, height, false); + protected final TextureSequence.TextureFrame createTexImage(GL gl, int texName) { + final Texture texture = super.createTexImageImpl(gl, texName, width, height); if(null != texData) { texture.updateImage(gl, texData); - texData.destroy(); - texData = null; - } - frame = new TextureSequence.TextureFrame( texture ); - return frame; + } + return new TextureSequence.TextureFrame( texture ); } - + @Override - protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) { - frame = null; - super.destroyTexImage(gl, imgTex); + protected final void destroyTexFrame(GL gl, TextureSequence.TextureFrame frame) { + super.destroyTexFrame(gl, frame); } - + private void validatePos() { boolean considerPausing = false; if( 0 > pos_ms) { 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 ce9df21cf..a6a6fba97 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,20 +20,20 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import javax.media.opengl.GLProfile; @@ -41,208 +41,377 @@ import javax.media.opengl.GLProfile; import com.jogamp.common.os.DynamicLibraryBundle; import com.jogamp.common.os.DynamicLibraryBundleInfo; import com.jogamp.common.util.RunnableExecutor; +import com.jogamp.common.util.VersionNumber; /** - * FIXME: We need native structure access methods to deal with API changes - * in the libav headers, which break binary compatibility! - * Currently we are binary compatible w/ [0.6 ?, ] 0.7 and 0.8 but not w/ trunk. - * - * ChangeList for trunk: - * Thu Jan 12 11:21:02 2012 a17479dfce67fbea2d0a1bf303010dce1e79059f major 53 -> 54 - * Mon Feb 27 22:40:11 2012 ee42df8a35c2b795f524c856834d0823dbd4e75d reorder AVStream and AVFormatContext - * Tue Feb 28 12:07:53 2012 322537478b63c6bc01e640643550ff539864d790 minor 1 -> 2 + * See {@link FFMPEGMediaPlayer#compatibility}. */ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { - private static List<String> glueLibNames = new ArrayList<String>(); // none - - private static final int symbolCount = 31; - private static String[] symbolNames = { - "avcodec_version", + private static final boolean DEBUG = FFMPEGMediaPlayer.DEBUG || DynamicLibraryBundleInfo.DEBUG; + + private static final List<String> glueLibNames = new ArrayList<String>(); // none + + private static final int symbolCount = 65; + private static final String[] symbolNames = { + "avutil_version", "avformat_version", -/* 3 */ "avutil_version", - + "avcodec_version", + "avresample_version", +/* 5 */ "swresample_version", + // libavcodec - "avcodec_close", - "avcodec_string", - "avcodec_find_decoder", - "avcodec_open2", // 53.6.0 (opt) - "avcodec_open", - "avcodec_alloc_frame", - "avcodec_default_get_buffer", - "avcodec_default_release_buffer", - "av_free_packet", + "avcodec_register_all", + "avcodec_close", + "avcodec_string", + "avcodec_find_decoder", + "avcodec_open2", // 53.6.0 (opt) + "avcodec_alloc_frame", + "avcodec_get_frame_defaults", + "avcodec_free_frame", // 54.28.0 (opt) + "avcodec_default_get_buffer", // <= 54 (opt), else sp_avcodec_default_get_buffer2 + "avcodec_default_release_buffer", // <= 54 (opt), else sp_av_frame_unref + "avcodec_default_get_buffer2", // 55 (opt) + "avcodec_get_edge_width", + "av_image_fill_linesizes", + "avcodec_align_dimensions", + "avcodec_align_dimensions2", + "avcodec_flush_buffers", + "av_init_packet", + "av_new_packet", + "av_destruct_packet", + "av_free_packet", "avcodec_decode_audio4", // 53.25.0 (opt) - "avcodec_decode_audio3", // 52.23.0 -/* 15 */ "avcodec_decode_video2", // 52.23.0 - +/* 27 */ "avcodec_decode_video2", // 52.23.0 + // libavutil - "av_pix_fmt_descriptors", - "av_free", -/* 18 */ "av_get_bits_per_pixel", - + "av_pix_fmt_descriptors", + "av_frame_unref", // 55.0.0 (opt) + "av_realloc", + "av_free", + "av_get_bits_per_pixel", + "av_samples_get_buffer_size", + "av_get_bytes_per_sample", // 51.4.0 + "av_opt_set_int", // 51.12.0 + "av_dict_get", + "av_dict_count", // 54.* (opt) + "av_dict_set", +/* 28 */ "av_dict_free", + // libavformat "avformat_alloc_context", "avformat_free_context", // 52.96.0 (opt) "avformat_close_input", // 53.17.0 (opt) - "av_close_input_file", - "av_register_all", - "avformat_open_input", - "av_dump_format", + "av_register_all", + "av_find_input_format", + "avformat_open_input", + "av_dump_format", "av_read_frame", "av_seek_frame", + "avformat_seek_file", // ??? (opt) + "av_read_play", + "av_read_pause", "avformat_network_init", // 53.13.0 (opt) "avformat_network_deinit", // 53.13.0 (opt) - "avformat_find_stream_info", // 53.3.0 (opt) -/* 29 */ "av_find_stream_info", - }; - - // alternate symbol names - private static 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 - { "av_find_stream_info", "avformat_find_stream_info" }, // old, 53.3.0 +/* 54 */ "avformat_find_stream_info", // 53.3.0 (opt) + + // libavdevice +/* 55 */ "avdevice_register_all", // ??? + + // libavresample + "avresample_alloc_context", // 1.0.1 + "avresample_open", + "avresample_close", + "avresample_free", +/* 60 */ "avresample_convert", + + // libavresample + "av_opt_set_sample_fmt", // actually lavu .. but exist only w/ swresample! + "swr_alloc", + "swr_init", + "swr_free", +/* 65 */ "swr_convert", + }; - + // optional symbol names - private static String[] optionalSymbolNames = { - "avformat_free_context", // 52.96.0 (opt) - "avformat_network_init", // 53.13.0 (opt) - "avformat_network_deinit", // 53.13.0 (opt) + private static final String[] optionalSymbolNames = { + "avformat_seek_file", // ??? (opt) + "avcodec_free_frame", // 54.28.0 (opt) + "av_frame_unref", // 55.0.0 (opt) + "av_dict_count", // 54.* (opt) + "avcodec_default_get_buffer", // <= 54 (opt), else sp_avcodec_default_get_buffer2 + "avcodec_default_release_buffer", // <= 54 (opt), else sp_av_frame_unref + "avcodec_default_get_buffer2", // 55 (opt) + + // libavdevice + "avdevice_register_all", // 53.0.0 (opt) + + // libavresample + "avresample_version", // 1.0.1 + "avresample_alloc_context", // 1.0.1 + "avresample_open", + "avresample_close", + "avresample_free", + "avresample_convert", + + // libavresample + "av_opt_set_sample_fmt", // actually lavu .. but exist only w/ swresample! + "swresample_version", // 0 + "swr_alloc", + "swr_init", + "swr_free", + "swr_convert", }; - - private static long[] symbolAddr; + + private static final long[] symbolAddr = new long[symbolCount]; private static final boolean ready; - + private static final boolean libsUFCLoaded; + private static final boolean avresampleLoaded; // optional + private static final boolean swresampleLoaded; // optional + private static final boolean avdeviceLoaded; // optional + static final VersionNumber avCodecVersion; + static final VersionNumber avFormatVersion; + static final VersionNumber avUtilVersion; + static final VersionNumber avResampleVersion; + static final VersionNumber swResampleVersion; + private static final FFMPEGNatives natives; + + private static final int LIB_IDX_UTI = 0; + private static final int LIB_IDX_FMT = 1; + private static final int LIB_IDX_COD = 2; + private static final int LIB_IDX_DEV = 3; + private static final int LIB_IDX_AVR = 4; + private static final int LIB_IDX_SWR = 5; + static { - // native ffmpeg media player implementation is included in jogl_desktop and jogl_mobile + // native ffmpeg media player implementation is included in jogl_desktop and jogl_mobile GLProfile.initSingleton(); boolean _ready = false; + /** util, format, codec, device, avresample, swresample */ + boolean[] _loaded= new boolean[6]; + /** util, format, codec, avresample, swresample */ + VersionNumber[] _versions = new VersionNumber[5]; try { - _ready = initSymbols(); + _ready = initSymbols(_loaded, _versions); } catch (Throwable t) { t.printStackTrace(); } - ready = _ready; - if(!ready) { - System.err.println("FFMPEG: Not Available"); + libsUFCLoaded = _loaded[LIB_IDX_UTI] && _loaded[LIB_IDX_FMT] && _loaded[LIB_IDX_COD]; + avdeviceLoaded = _loaded[LIB_IDX_DEV]; + avresampleLoaded = _loaded[LIB_IDX_AVR]; + swresampleLoaded = _loaded[LIB_IDX_SWR]; + avUtilVersion = _versions[0]; + avFormatVersion = _versions[1]; + avCodecVersion = _versions[2]; + avResampleVersion = _versions[3]; + swResampleVersion = _versions[4]; + if(!libsUFCLoaded) { + System.err.println("LIB_AV Not Available: lavu, lavc, lavu"); + natives = null; + ready = false; + } else if(!_ready) { + System.err.println("LIB_AV Not Matching"); + natives = null; + ready = false; + } else { + if( avCodecVersion.getMajor() == 53 && avFormatVersion.getMajor() == 53 && avUtilVersion.getMajor() == 51 ) { + // lavc53.lavf53.lavu51 + natives = new FFMPEGv08Natives(); + } else if( avCodecVersion.getMajor() == 54 && avFormatVersion.getMajor() == 54 && avUtilVersion.getMajor() == 52 ) { + // lavc54.lavf54.lavu52.lavr01 + natives = new FFMPEGv09Natives(); + } else if( avCodecVersion.getMajor() == 55 && avFormatVersion.getMajor() == 55 && avUtilVersion.getMajor() == 52 ) { + // lavc55.lavf55.lavu52.lavr01 + natives = new FFMPEGv10Natives(); + } else { + System.err.println("LIB_AV No Version/Native-Impl Match"); + natives = null; + } + if( null != natives && FFMPEGStaticNatives.initIDs0() ) { + ready = natives.initSymbols0(symbolAddr, symbolCount); + } else { + ready = false; + } } } - + + static boolean libsLoaded() { return libsUFCLoaded; } + static boolean avDeviceLoaded() { return avdeviceLoaded; } + static boolean avResampleLoaded() { return avresampleLoaded; } + static boolean swResampleLoaded() { return swresampleLoaded; } + static FFMPEGNatives getNatives() { return natives; } static boolean initSingleton() { return ready; } - - private static boolean initSymbols() { - final DynamicLibraryBundle dl = new DynamicLibraryBundle(new FFMPEGDynamicLibraryBundleInfo()); - final boolean avutilLoaded = dl.isToolLibLoaded(0); - final boolean avformatLoaded = dl.isToolLibLoaded(1); - final boolean avcodecLoaded = dl.isToolLibLoaded(2); - if(!avutilLoaded || !avformatLoaded || !avcodecLoaded) { - throw new RuntimeException("FFMPEG Tool library incomplete: [ avutil "+avutilLoaded+", avformat "+avformatLoaded+", avcodec "+avcodecLoaded+"]"); - } - if(!dl.isToolLibComplete()) { - throw new RuntimeException("FFMPEG Tool libraries incomplete"); + + /** + * @param loaded 6: util, format, codec, device, avresample, swresample + * @param versions 5: util, format, codec, avresample, swresample + * @return + */ + private static final boolean initSymbols(boolean[] loaded, VersionNumber[] versions) { + for(int i=0; i<6; i++) { + loaded[i] = false; + } + final DynamicLibraryBundle dl = AccessController.doPrivileged(new PrivilegedAction<DynamicLibraryBundle>() { + @Override + public DynamicLibraryBundle run() { + return new DynamicLibraryBundle(new FFMPEGDynamicLibraryBundleInfo()); + } } ); + dl.toString(); + for(int i=0; i<6; i++) { + loaded[i] = dl.isToolLibLoaded(i); + } + if( !loaded[LIB_IDX_UTI] || !loaded[LIB_IDX_FMT] || !loaded[LIB_IDX_COD] ) { + throw new RuntimeException("FFMPEG Tool library incomplete: [ avutil "+loaded[LIB_IDX_UTI]+", avformat "+loaded[LIB_IDX_FMT]+", avcodec "+loaded[LIB_IDX_COD]+"]"); } if(symbolNames.length != symbolCount) { throw new InternalError("XXX0 "+symbolNames.length+" != "+symbolCount); } - symbolAddr = new long[symbolCount]; - + // optional symbol name set final Set<String> optionalSymbolNameSet = new HashSet<String>(); optionalSymbolNameSet.addAll(Arrays.asList(optionalSymbolNames)); - - // alternate symbol name mapping to indexed array - final Map<String, Integer> mAltSymbolNames = new HashMap<String, Integer>(); - final int[][] iAltSymbolNames = new int[altSymbolNames.length][]; - { - final List<String> symbolNameList = Arrays.asList(symbolNames); - for(int i=0; i<altSymbolNames.length; i++) { - iAltSymbolNames[i] = new int[altSymbolNames[i].length]; - for(int j=0; j<altSymbolNames[i].length; j++) { - mAltSymbolNames.put(altSymbolNames[i][j], new Integer(i)); - iAltSymbolNames[i][j] = symbolNameList.indexOf(altSymbolNames[i][j]); - } - } - } - + // lookup - for(int i = 0; i<symbolCount; i++) { - symbolAddr[i] = dl.dynamicLookupFunction(symbolNames[i]); - } - + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + for(int i = 0; i<symbolCount; i++) { + symbolAddr[i] = dl.dynamicLookupFunction(symbolNames[i]); + } + return null; + } } ); + // validate results + boolean res = true; for(int i = 0; i<symbolCount; i++) { if( 0 == symbolAddr[i] ) { // no symbol, check optional and alternative symbols final String symbol = symbolNames[i]; if ( !optionalSymbolNameSet.contains(symbol) ) { - // check for API changed symbols - boolean ok = false; - final Integer cI = mAltSymbolNames.get(symbol); - if ( null != cI ) { - // check whether alternative symbol is available - final int ci = cI.intValue(); - for(int j=0; !ok && j<iAltSymbolNames[ci].length; j++) { - final int si = iAltSymbolNames[ci][j]; - ok = 0 != symbolAddr[si]; - if(ok && (true || DEBUG )) { // keep it verbose per default for now .. - System.err.println("OK: Unresolved symbol <"+symbol+">, but has alternative <"+symbolNames[si]+">"); - } - } - } - if(!ok) { - System.err.println("Fail: Could not resolve symbol <"+symbolNames[i]+">: not optional, no alternatives."); - return false; - } - } else if(true || DEBUG ) { // keep it verbose per default for now .. + System.err.println("Fail: Could not resolve symbol <"+symbolNames[i]+">: not optional, no alternatives."); + res = false; + } else if(DEBUG) { System.err.println("OK: Unresolved optional symbol <"+symbolNames[i]+">"); } } } - return initSymbols0(symbolAddr, symbolCount); + versions[0] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[0])); + versions[1] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[1])); + versions[2] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[2])); + versions[3] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[3])); + versions[4] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[4])); + + return res; } - + protected FFMPEGDynamicLibraryBundleInfo() { } @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() { return glueLibNames; } @Override - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { List<List<String>> libsList = new ArrayList<List<String>>(); + // 6: util, format, codec, device, avresample, swresample + final List<String> avutil = new ArrayList<String>(); avutil.add("avutil"); // default - avutil.add("avutil-52"); // dummy future proof + + avutil.add("libavutil.so.53"); // dummy future proof + avutil.add("libavutil.so.52"); // ffmpeg 1.2 + 2 / libav 9 + 10 + avutil.add("libavutil.so.51"); // 0.8 + avutil.add("libavutil.so.50"); // 0.7 + + avutil.add("avutil-53"); // dummy future proof + avutil.add("avutil-52"); // ffmpeg 1.2 + 2 / libav 9 + 10 avutil.add("avutil-51"); // 0.8 avutil.add("avutil-50"); // 0.7 libsList.add(avutil); - + final List<String> avformat = new ArrayList<String>(); avformat.add("avformat"); // default - avformat.add("avformat-55"); // dummy future proof - avformat.add("avformat-54"); // 0.? + + avformat.add("libavformat.so.56"); // dummy future proof + avformat.add("libavformat.so.55"); // ffmpeg 2 / libav 10 + avformat.add("libavformat.so.54"); // ffmpeg 1.2 / libav 9 + avformat.add("libavformat.so.53"); // 0.8 + avformat.add("libavformat.so.52"); // 0.7 + + avformat.add("avformat-56"); // dummy future proof + avformat.add("avformat-55"); // ffmpeg 2 / libav 10 + avformat.add("avformat-54"); // ffmpeg 1.2 / libav 9 avformat.add("avformat-53"); // 0.8 avformat.add("avformat-52"); // 0.7 libsList.add(avformat); - + final List<String> avcodec = new ArrayList<String>(); avcodec.add("avcodec"); // default - avcodec.add("avcodec-55"); // dummy future proof - avcodec.add("avcodec-54"); // 0.? + + avcodec.add("libavcodec.so.56"); // dummy future proof + avcodec.add("libavcodec.so.55"); // ffmpeg 2/ libav 10 + avcodec.add("libavcodec.so.54"); // ffmpeg 1.2 / libav 9 + avcodec.add("libavcodec.so.53"); // 0.8 + avcodec.add("libavcodec.so.52"); // 0.7 + + avcodec.add("avcodec-56"); // dummy future proof + avcodec.add("avcodec-55"); // ffmpeg 2/ libav 10 + avcodec.add("avcodec-54"); // ffmpeg 1.2 / libav 9 avcodec.add("avcodec-53"); // 0.8 avcodec.add("avcodec-52"); // 0.7 libsList.add(avcodec); - + + final List<String> avdevice = new ArrayList<String>(); + avdevice.add("avdevice"); // default + + avdevice.add("libavdevice.so.56"); // dummy future proof + avdevice.add("libavdevice.so.55"); // ffmpeg 2 + avdevice.add("libavdevice.so.54"); // ffmpeg 1.2 / libav 10 + avdevice.add("libavdevice.so.53"); // 0.8 && libav 9 + + avdevice.add("avdevice-56"); // dummy future proof + avdevice.add("avdevice-55"); // ffmpeg 2 + avdevice.add("avdevice-54"); // ffmpeg 1.2 / libav 10 + avdevice.add("avdevice-53"); // 0.8 && libav 9 + libsList.add(avdevice); + + final List<String> avresample = new ArrayList<String>(); + avresample.add("avresample"); // default + + avresample.add("libavresample.so.2"); // dummy future proof + avresample.add("libavresample.so.1"); // libav 9 + 10 + + avresample.add("avresample-2"); // dummy future proof + avresample.add("avresample-1"); // libav 9 + 10 + libsList.add(avresample); + + final List<String> swresample = new ArrayList<String>(); + swresample.add("swresample"); // default + + swresample.add("libswresample.so.1"); // dummy future proof + swresample.add("libswresample.so.0"); // ffmpeg 1.2 + 2.x + + swresample.add("swresample-1"); // dummy future proof + swresample.add("swresample-0"); // ffmpeg 1.2 + 2.x + libsList.add(swresample); + return libsList; } @@ -257,14 +426,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(); - } - - private static native boolean initSymbols0(long[] symbols, int count); + } } 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 7d10cff4d..344ba48ee 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -29,514 +29,797 @@ 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; import javax.media.opengl.GLException; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.VersionNumber; import com.jogamp.gluegen.runtime.ProcAddressTable; +import com.jogamp.opengl.util.TimeFrameI; import com.jogamp.opengl.util.GLPixelStorageModes; +import com.jogamp.opengl.util.av.AudioSink; +import com.jogamp.opengl.util.av.AudioSink.AudioFormat; +import com.jogamp.opengl.util.av.AudioSinkFactory; +import com.jogamp.opengl.util.av.GLMediaPlayer; 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; +import jogamp.opengl.util.av.GLMediaPlayerImpl; +import jogamp.opengl.util.av.impl.FFMPEGNatives.PixelFormat; +import jogamp.opengl.util.av.impl.FFMPEGNatives.SampleFormat; /*** * Implementation utilizes <a href="http://libav.org/">Libav</a> - * or <a href="http://ffmpeg.org/">FFmpeg</a> which is ubiquitous - * available and usually pre-installed on Unix platforms. Due to legal - * reasons we cannot deploy binaries of it, which contains patented codecs. + * or <a href="http://ffmpeg.org/">FFmpeg</a> which are ubiquitous + * available and usually pre-installed on Unix platforms. + * <p> + * Due to legal reasons we cannot deploy binaries of it, which contains patented codecs. + * </p> + * <p> * Besides the default BSD/Linux/.. repositories and installations, - * precompiled binaries can be found at the listed location below. + * precompiled binaries can be found at the + * <a href="#libavavail">listed location below</a>. + * </p> + * + * <a name="implspecifics"><h5>Implementation specifics</h5></a> * <p> - * Implements YUV420P to RGB fragment shader conversion - * and the usual packed RGB formats. - * The decoded video frame is written directly into an OpenGL texture - * on the GPU in it's native format. A custom fragment shader converts - * the native pixelformat to a usable RGB format if required. - * Hence only 1 copy is required before bloating the picture - * from YUV to RGB, for example. - * </p> + * The decoded video frame is written directly into an OpenGL texture + * on the GPU in it's native format. A custom fragment shader converts + * the native pixelformat to a usable <i>RGB</i> format if required. + * Hence only 1 copy is required before bloating the picture + * from <i>YUV*</i> to <i>RGB</i>, for example. + * </p> * <p> - * Utilizes a slim dynamic and native binding to the Lib_av + * Implements pixel format conversion to <i>RGB</i> via + * fragment shader texture-lookup functions: + * <ul> + * <li>{@link PixelFormat#YUV420P}</li> + * <li>{@link PixelFormat#YUVJ420P}</li> + * <li>{@link PixelFormat#YUV422P}</li> + * <li>{@link PixelFormat#YUVJ422P}</li> + * <li>{@link PixelFormat#YUYV422}</li> + * <li>{@link PixelFormat#BGR24}</li> + * </ul> + * </p> + * <p> + * + * <a name="libavspecifics"><h5>Libav Specifics</h5></a> + * <p> + * Utilizes a slim dynamic and native binding to the Lib_av * libraries: * <ul> - * <li>libavutil</li> - * <li>libavformat</li> * <li>libavcodec</li> - * </ul> + * <li>libavformat</li> + * <li>libavutil</li> + * <li>libavresample (opt)</li> + * <li>libavdevice (opt)</li> + * </ul> + * </p> + * + * <a name="compatibility"><h5>LibAV Compatibility</h5></a> + * <p> + * Currently we are binary compatible w/: + * <table border="1"> + * <tr><th>libav / ffmpeg</th><th>lavc</th><th>lavf</th><th>lavu</th><th>lavr</th> <th>FFMPEG* class</th></tr> + * <tr><td>0.8</td> <td>53</td> <td>53</td> <td>51</td> <td></td> <td>FFMPEGv08</td></tr> + * <tr><td>9.0 / 1.2</td> <td>54</td> <td>54</td> <td>52</td> <td>01/00</td> <td>FFMPEGv09</td></tr> + * <tr><td>10 / 2</td> <td>55</td> <td>55</td> <td>52</td> <td>01/00</td> <td>FFMPEGv10</td></tr> + * </table> * </p> * <p> - * http://libav.org/ + * See http://upstream-tracker.org/versions/libav.html * </p> - * <p> - * Check tag 'FIXME: Add more planar formats !' + * <p> + * Check tag 'FIXME: Add more planar formats !' * here and in the corresponding native code - * <code>jogl/src/jogl/native/ffmpeg/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c</code> + * <code>jogl/src/jogl/native/libav/ffmpeg_impl_template.c</code> * </p> + * + * + * <a name="todo"><h5>TODO:</h5></a> * <p> - * TODO: * <ul> - * <li>Audio Output</li> - * <li>Off thread <i>next frame</i> processing using multiple target textures</li> - * <li>better pts sync handling</li> - * <li>fix seek</li> - * </ul> + * <li>better audio synchronization handling? (video is synchronized)</li> + * </ul> * </p> - * Pre-compiled Libav / FFmpeg packages: + * + * <a name="libavavail"><h5>FFMPEG / LibAV Availability</h5></a> + * <p> * <ul> - * <li>Windows: http://ffmpeg.zeranoe.com/builds/</li> - * <li>MacOSX: http://www.ffmpegx.com/</li> + * <li>GNU/Linux: ffmpeg or libav are deployed in most distributions.</li> + * <li>Windows: + * <ul> + * <li>http://ffmpeg.zeranoe.com/builds/ (ffmpeg) <i>recommended, works w/ dshow</i></li> + * <li>http://win32.libav.org/releases/ (libav)</li> + * </ul></li> + * <li>MacOSX: http://ffmpegmac.net/</li> * <li>OpenIndiana/Solaris:<pre> * pkg set-publisher -p http://pkg.openindiana.org/sfe-encumbered. * pkt install pkg:/video/ffmpeg * </pre></li> - * </ul> + * </ul> + * </p> */ -public class FFMPEGMediaPlayer extends EGLMediaPlayerImpl { - public static final VersionNumber avUtilVersion; - public static final VersionNumber avFormatVersion; - public static final VersionNumber avCodecVersion; - static final boolean available; - +public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { + + /** POSIX ENOSYS {@value}: Function not implemented. FIXME: Move to GlueGen ?!*/ + private static final int ENOSYS = 38; + + // Instance data + private static final FFMPEGNatives natives; + private static final int avUtilMajorVersionCC; + private static final int avFormatMajorVersionCC; + private static final int avCodecMajorVersionCC; + private static final int avResampleMajorVersionCC; + private static final int swResampleMajorVersionCC; + private static final boolean available; + static { - if(FFMPEGDynamicLibraryBundleInfo.initSingleton()) { - avUtilVersion = getAVVersion(getAvUtilVersion0()); - avFormatVersion = getAVVersion(getAvFormatVersion0()); - avCodecVersion = getAVVersion(getAvCodecVersion0()); - System.err.println("LIB_AV Util : "+avUtilVersion); - System.err.println("LIB_AV Format: "+avFormatVersion); - System.err.println("LIB_AV Codec : "+avCodecVersion); - available = initIDs0(); + final boolean libAVGood = FFMPEGDynamicLibraryBundleInfo.initSingleton(); + final boolean libAVVersionGood; + if( FFMPEGDynamicLibraryBundleInfo.libsLoaded() ) { + natives = FFMPEGDynamicLibraryBundleInfo.getNatives(); + if( null != natives ) { + avCodecMajorVersionCC = natives.getAvCodecMajorVersionCC0(); + avFormatMajorVersionCC = natives.getAvFormatMajorVersionCC0(); + avUtilMajorVersionCC = natives.getAvUtilMajorVersionCC0(); + avResampleMajorVersionCC = natives.getAvResampleMajorVersionCC0(); + swResampleMajorVersionCC = natives.getSwResampleMajorVersionCC0(); + } else { + avUtilMajorVersionCC = 0; + avFormatMajorVersionCC = 0; + avCodecMajorVersionCC = 0; + avResampleMajorVersionCC = 0; + swResampleMajorVersionCC = 0; + } + final VersionNumber avCodecVersion = FFMPEGDynamicLibraryBundleInfo.avCodecVersion; + final VersionNumber avFormatVersion = FFMPEGDynamicLibraryBundleInfo.avFormatVersion; + final VersionNumber avUtilVersion = FFMPEGDynamicLibraryBundleInfo.avUtilVersion; + final VersionNumber avResampleVersion = FFMPEGDynamicLibraryBundleInfo.avResampleVersion; + final boolean avResampleLoaded = FFMPEGDynamicLibraryBundleInfo.avResampleLoaded(); + final VersionNumber swResampleVersion = FFMPEGDynamicLibraryBundleInfo.swResampleVersion; + final boolean swResampleLoaded = FFMPEGDynamicLibraryBundleInfo.swResampleLoaded(); + if( DEBUG ) { + System.err.println("LIB_AV Codec : "+avCodecVersion+" [cc "+avCodecMajorVersionCC+"]"); + System.err.println("LIB_AV Format : "+avFormatVersion+" [cc "+avFormatMajorVersionCC+"]"); + System.err.println("LIB_AV Util : "+avUtilVersion+" [cc "+avUtilMajorVersionCC+"]"); + System.err.println("LIB_AV Resample: "+avResampleVersion+" [cc "+avResampleMajorVersionCC+", loaded "+avResampleLoaded+"]"); + System.err.println("LIB_SW Resample: "+swResampleVersion+" [cc "+swResampleMajorVersionCC+", loaded "+swResampleLoaded+"]"); + System.err.println("LIB_AV Device : [loaded "+FFMPEGDynamicLibraryBundleInfo.avDeviceLoaded()+"]"); + System.err.println("LIB_AV Class : "+(null!= natives ? natives.getClass().getSimpleName() : "n/a")); + } + libAVVersionGood = avCodecMajorVersionCC == avCodecVersion.getMajor() && + avFormatMajorVersionCC == avFormatVersion.getMajor() && + avUtilMajorVersionCC == avUtilVersion.getMajor() && + ( !avResampleLoaded || avResampleMajorVersionCC < 0 || avResampleMajorVersionCC == avResampleVersion.getMajor() ) && + ( !swResampleLoaded || swResampleMajorVersionCC < 0 || swResampleMajorVersionCC == swResampleVersion.getMajor() ) ; + if( !libAVVersionGood ) { + System.err.println("LIB_AV Not Matching Compile-Time / Runtime Major-Version"); + } } else { - avUtilVersion = null; - avFormatVersion = null; - avCodecVersion = null; - available = false; + natives = null; + avUtilMajorVersionCC = 0; + avFormatMajorVersionCC = 0; + avCodecMajorVersionCC = 0; + avResampleMajorVersionCC = 0; + swResampleMajorVersionCC = 0; + libAVVersionGood = false; } + available = libAVGood && libAVVersionGood && null != natives; } - + public static final boolean isAvailable() { return available; } - private static VersionNumber getAVVersion(int vers) { - return new VersionNumber( ( vers >> 16 ) & 0xFF, - ( vers >> 8 ) & 0xFF, - ( vers >> 0 ) & 0xFF ); - } - - protected long moviePtr = 0; - protected long procAddrGLTexSubImage2D = 0; - protected EGLMediaPlayerImpl.EGLTextureFrame lastTex = null; - protected GLPixelStorageModes psm; - protected PixelFormat vPixelFmt = null; - protected int vPlanes = 0; - protected int vBitsPerPixel = 0; - protected int vBytesPerPixelPerPlane = 0; - protected int[] vLinesize = { 0, 0, 0 }; // per plane - protected int[] vTexWidth = { 0, 0, 0 }; // per plane - protected int texWidth, texHeight; // overall (stuffing planes in one texture) - protected ByteBuffer texCopy; + // + // General + // + + private long moviePtr = 0; + + // + // Video + // + + private String texLookupFuncName = "ffmpegTexture2D"; + private boolean usesTexLookupShader = false; + private PixelFormat vPixelFmt = null; + private int vPlanes = 0; + private int vBitsPerPixel = 0; + private int vBytesPerPixelPerPlane = 0; + private int texWidth, texHeight; // overall (stuffing planes in one texture) + private String singleTexComp = "r"; + private final GLPixelStorageModes psm; + + // + // Audio + // + + private AudioSink.AudioFormat avChosenAudioFormat; + private int audioSamplesPerFrameAndChannel = 0; public FFMPEGMediaPlayer() { - super(TextureType.GL, false); if(!available) { throw new RuntimeException("FFMPEGMediaPlayer not available"); } - setTextureCount(1); - moviePtr = createInstance0(true); + moviePtr = natives.createInstance0(this, DEBUG_NATIVE); if(0==moviePtr) { throw new GLException("Couldn't create FFMPEGInstance"); } psm = new GLPixelStorageModes(); + audioSink = null; } - + @Override - protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) { - if(TextureType.GL == texType) { - final Texture texture = super.createTexImageImpl(gl, idx, tex, texWidth, texHeight, true); - lastTex = new EGLTextureFrame(null, texture, 0, 0); - } else { - throw new InternalError("n/a"); + protected final void destroyImpl(GL gl) { + if (moviePtr != 0) { + natives.destroyInstance0(moviePtr); + moviePtr = 0; } - return lastTex; + destroyAudioSink(); } - - @Override - protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) { - lastTex = null; - super.destroyTexImage(gl, imgTex); + private final void destroyAudioSink() { + final AudioSink _audioSink = audioSink; + if( null != _audioSink ) { + audioSink = null; + _audioSink.destroy(); + } } - + + public static final String dev_video_linux = "/dev/video"; + @Override - protected void destroyImpl(GL gl) { - if (moviePtr != 0) { - destroyInstance0(moviePtr); - moviePtr = 0; + protected final void initStreamImpl(int vid, int aid) throws IOException { + if(0==moviePtr) { + throw new GLException("FFMPEG native instance null"); } + if(DEBUG) { + System.err.println("initStream: p1 "+this); + } + + final String streamLocS = IOUtil.decodeURIIfFilePath(streamLoc); + destroyAudioSink(); + if( GLMediaPlayer.STREAM_ID_NONE == aid ) { + audioSink = AudioSinkFactory.createNull(); + } else { + audioSink = AudioSinkFactory.createDefault(); + } + final AudioFormat preferredAudioFormat = audioSink.getPreferredFormat(); + if(DEBUG) { + System.err.println("initStream: p2 preferred "+preferredAudioFormat+", "+this); + } + + final boolean isCameraInput = null != cameraPath; + final String resStreamLocS; + // int rw=640, rh=480, rr=15; + int rw=-1, rh=-1, rr=-1; + String sizes = null; + if( isCameraInput ) { + switch(Platform.OS_TYPE) { + case ANDROID: + // ?? + case FREEBSD: + case HPUX: + case LINUX: + case SUNOS: + resStreamLocS = dev_video_linux + cameraPath; + break; + case WINDOWS: + resStreamLocS = cameraPath; + break; + case MACOS: + case OPENKODE: + default: + resStreamLocS = streamLocS; // FIXME: ?? + break; + } + if( null != cameraProps ) { + sizes = cameraProps.get(CameraPropSizeS); + int v = getPropIntVal(cameraProps, CameraPropWidth); + if( v > 0 ) { rw = v; } + v = getPropIntVal(cameraProps, CameraPropHeight); + if( v > 0 ) { rh = v; } + v = getPropIntVal(cameraProps, CameraPropRate); + if( v > 0 ) { rr = v; } + } + } else { + resStreamLocS = streamLocS; + } + final int aMaxChannelCount = audioSink.getMaxSupportedChannels(); + final int aPrefSampleRate = preferredAudioFormat.sampleRate; + // setStream(..) issues updateAttributes*(..), and defines avChosenAudioFormat, vid, aid, .. etc + if(DEBUG) { + System.err.println("initStream: p3 cameraPath "+cameraPath+", isCameraInput "+isCameraInput); + System.err.println("initStream: p3 stream "+streamLoc+" -> "+streamLocS+" -> "+resStreamLocS); + System.err.println("initStream: p3 vid "+vid+", sizes "+sizes+", reqVideo "+rw+"x"+rh+"@"+rr+", aid "+aid+", aMaxChannelCount "+aMaxChannelCount+", aPrefSampleRate "+aPrefSampleRate); + } + natives.setStream0(moviePtr, resStreamLocS, isCameraInput, vid, sizes, rw, rh, rr, aid, aMaxChannelCount, aPrefSampleRate); } - + @Override - protected void initGLStreamImpl(GL gl, int[] texNames) throws IOException { + protected final void initGLImpl(GL gl) throws IOException, GLException { if(0==moviePtr) { throw new GLException("FFMPEG native instance null"); } - final String urlS=urlConn.getURL().toExternalForm(); - - System.out.println("setURL: p1 "+this); - setStream0(moviePtr, urlS, -1, -1); - System.out.println("setURL: p2 "+this); - int tf; - switch(vBytesPerPixelPerPlane) { - case 1: tf = GL2ES2.GL_RED; break; - case 3: tf = GL2ES2.GL_RGB; break; - case 4: tf = GL2ES2.GL_RGBA; break; - default: throw new RuntimeException("Unsupported bytes-per-pixel / plane "+vBytesPerPixelPerPlane); - } - setTextureFormat(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; + if(null == audioSink) { + throw new GLException("AudioSink null"); + } + final int audioQueueLimit; + if( null != gl && STREAM_ID_NONE != vid ) { + final GLContextImpl ctx = (GLContextImpl)gl.getContext(); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + final ProcAddressTable pt = ctx.getGLProcAddressTable(); + final long procAddrGLTexSubImage2D = pt.getAddressFor("glTexSubImage2D"); + final long procAddrGLGetError = pt.getAddressFor("glGetError"); + final long procAddrGLFlush = pt.getAddressFor("glFlush"); + final long procAddrGLFinish = pt.getAddressFor("glFinish"); + natives.setGLFuncs0(moviePtr, procAddrGLTexSubImage2D, procAddrGLGetError, procAddrGLFlush, procAddrGLFinish); + return null; + } } ); + audioQueueLimit = AudioSink.DefaultQueueLimitWithVideo; + } else { + audioQueueLimit = AudioSink.DefaultQueueLimitAudioOnly; + } + if(DEBUG) { + System.err.println("initGL: p3 avChosen "+avChosenAudioFormat); + } + + if( STREAM_ID_NONE == aid ) { + audioSink.destroy(); + audioSink = AudioSinkFactory.createNull(); + audioSink.init(AudioSink.DefaultFormat, 0, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); } else { - throw new InternalError("Unknown ProcAddressTable: "+pt.getClass().getName()+" of "+ctx.getClass().getName()); + final float frameDuration; + if( audioSamplesPerFrameAndChannel > 0 ) { + frameDuration= avChosenAudioFormat.getSamplesDuration(audioSamplesPerFrameAndChannel); + } else { + frameDuration = AudioSink.DefaultFrameDuration; + } + final boolean audioSinkOK = audioSink.init(avChosenAudioFormat, frameDuration, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); + if( !audioSinkOK ) { + System.err.println("AudioSink "+audioSink.getClass().getName()+" does not support "+avChosenAudioFormat+", using Null"); + audioSink.destroy(); + audioSink = AudioSinkFactory.createNull(); + audioSink.init(avChosenAudioFormat, 0, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit); + } + } + if(DEBUG) { + System.err.println("initGL: p4 chosen "+avChosenAudioFormat); + System.err.println("initGL: p4 chosen "+audioSink); + } + + if( null != gl && STREAM_ID_NONE != vid ) { + int tf, tif=GL.GL_RGBA; // texture format and internal format + int tt = GL.GL_UNSIGNED_BYTE; + switch(vBytesPerPixelPerPlane) { + case 1: + if( gl.isGL3ES3() ) { + // RED is supported on ES3 and >= GL3 [core]; ALPHA is deprecated on core + tf = GL2ES2.GL_RED; tif=GL2ES2.GL_RED; singleTexComp = "r"; + } else { + // ALPHA is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility + tf = GL2ES2.GL_ALPHA; tif=GL2ES2.GL_ALPHA; singleTexComp = "a"; + } + break; + + case 2: if( vPixelFmt == PixelFormat.YUYV422 ) { + // YUYV422: // < packed YUV 4:2:2, 2x 16bpp, Y0 Cb Y1 Cr + // Stuffed into RGBA half width texture + tf = GL2ES2.GL_RGBA; tif=GL2ES2.GL_RGBA; break; + } else { + tf = GL2ES2.GL_RG; tif=GL2ES2.GL_RG; break; + } + case 3: tf = GL2ES2.GL_RGB; tif=GL.GL_RGB; break; + case 4: if( vPixelFmt == PixelFormat.BGRA ) { + tf = GL2ES2.GL_BGRA; tif=GL.GL_RGBA; break; + } else { + tf = GL2ES2.GL_RGBA; tif=GL.GL_RGBA; break; + } + default: throw new RuntimeException("Unsupported bytes-per-pixel / plane "+vBytesPerPixelPerPlane); + } + setTextureFormat(tif, tf); + setTextureType(tt); + if(DEBUG) { + System.err.println("initGL: p5: video "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane+ + ", tex "+texWidth+"x"+texHeight+", usesTexLookupShader "+usesTexLookupShader); + } } } - private void updateAttributes2(int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane, - int lSz0, int lSz1, int lSz2, - int tWd0, int tWd1, int tWd2) { - vPixelFmt = PixelFormat.valueOf(pixFmt); - vPlanes = planes; - vBitsPerPixel = bitsPerPixel; - vBytesPerPixelPerPlane = bytesPerPixelPerPlane; - vLinesize[0] = lSz0; vLinesize[1] = lSz1; vLinesize[2] = lSz2; - vTexWidth[0] = tWd0; vTexWidth[1] = tWd1; vTexWidth[2] = tWd2; - - switch(vPixelFmt) { - case YUV420P: - // YUV420P: Adding U+V on right side of fixed height texture, - // since width is already aligned by decoder. - // Y=w*h, Y=w/2*h/2, U=w/2*h/2 - // w*h + 2 ( w/2 * h/2 ) - // w*h + w*h/2 - // 2*w/2 * h - texWidth = vTexWidth[0] + vTexWidth[1]; texHeight = height; + @Override + protected final TextureFrame createTexImage(GL gl, int texName) { + return new TextureFrame( createTexImageImpl(gl, texName, texWidth, texHeight) ); + } + + /** + * @param sampleRate sample rate in Hz (1/s) + * @param sampleSize sample size in bits + * @param channelCount number of channels + * @param signed true if signed number, false for unsigned + * @param fixedP true for fixed point value, false for unsigned floating point value with a sampleSize of 32 (float) or 64 (double) + * @param planar true for planar data package (each channel in own data buffer), false for packed data channels interleaved in one buffer. + * @param littleEndian true for little-endian, false for big endian + * @return + */ + + /** + * Native callback + * Converts the given libav/ffmpeg values to {@link AudioFormat} and returns {@link AudioSink#isSupported(AudioFormat)}. + * @param audioSampleFmt ffmpeg/libav audio-sample-format, see {@link SampleFormat}. + * @param audioSampleRate sample rate in Hz (1/s) + * @param audioChannels number of channels + */ + final boolean isAudioFormatSupported(int audioSampleFmt, int audioSampleRate, int audioChannels) { + final SampleFormat avFmt = SampleFormat.valueOf(audioSampleFmt); + final AudioFormat audioFormat = avAudioFormat2Local(avFmt, audioSampleRate, audioChannels); + final boolean res = audioSink.isSupported(audioFormat); + if( DEBUG ) { + System.err.println("AudioSink.isSupported: "+res+": av[fmt "+avFmt+", rate "+audioSampleRate+", chan "+audioChannels+"] -> "+audioFormat); + } + return res; + } + + /** + * Returns {@link AudioFormat} as converted from the given libav/ffmpeg values. + * @param audioSampleFmt ffmpeg/libav audio-sample-format, see {@link SampleFormat}. + * @param audioSampleRate sample rate in Hz (1/s) + * @param audioChannels number of channels + */ + private final AudioFormat avAudioFormat2Local(SampleFormat audioSampleFmt, int audioSampleRate, int audioChannels) { + final int sampleSize; + boolean planar = true; + boolean fixedP = true; + final boolean signed; + switch( audioSampleFmt ) { + case S32: + planar = false; + case S32P: + sampleSize = 32; + signed = true; + break; + case S16: + planar = false; + case S16P: + sampleSize = 16; + signed = true; + break; + case U8: + planar = false; + case U8P: + sampleSize = 8; + signed = false; + break; + case DBL: + planar = false; + case DBLP: + sampleSize = 64; + signed = true; + fixedP = false; break; - // case PIX_FMT_YUYV422: - case RGB24: - case BGR24: - case ARGB: - case RGBA: - case ABGR: - case BGRA: - texWidth = vTexWidth[0]; texHeight = height; + case FLT: + planar = false; + case FLTP: + sampleSize = 32; + signed = true; + fixedP = false; break; - default: // FIXME: Add more planar formats ! - throw new RuntimeException("Unsupported pixelformat: "+vPixelFmt); + default: // FIXME: Add more formats ! + throw new IllegalArgumentException("Unsupported sampleformat: "+audioSampleFmt); + } + return new AudioFormat(audioSampleRate, sampleSize, audioChannels, signed, fixedP, planar, true /* littleEndian */); + } + + /** + * Native callback + * @param vid + * @param pixFmt + * @param planes + * @param bitsPerPixel + * @param bytesPerPixelPerPlane + * @param tWd0 + * @param tWd1 + * @param tWd2 + * @param aid + * @param audioSampleFmt + * @param audioSampleRate + * @param audioChannels + * @param audioSamplesPerFrameAndChannel in audio samples per frame and channel + */ + void setupFFAttributes(int vid, int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane, + int tWd0, int tWd1, int tWd2, int vW, int vH, + int aid, int audioSampleFmt, int audioSampleRate, + int audioChannels, int audioSamplesPerFrameAndChannel) { + // defaults .. + vPixelFmt = null; + vPlanes = 0; + vBitsPerPixel = 0; + vBytesPerPixelPerPlane = 0; + usesTexLookupShader = false; + texWidth = 0; texHeight = 0; + + final int[] vTexWidth = { 0, 0, 0 }; // per plane + + if( STREAM_ID_NONE != vid ) { + vPixelFmt = PixelFormat.valueOf(pixFmt); + vPlanes = planes; + vBitsPerPixel = bitsPerPixel; + vBytesPerPixelPerPlane = bytesPerPixelPerPlane; + vTexWidth[0] = tWd0; vTexWidth[1] = tWd1; vTexWidth[2] = tWd2; + + switch(vPixelFmt) { + case YUVJ420P: + case YUV420P: // < planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + usesTexLookupShader = true; + // YUV420P: Adding U+V on right side of fixed height texture, + // since width is already aligned by decoder. + // Splitting texture to 4 quadrants: + // Y covers left top/low quadrant + // U on top-right quadrant. + // V on low-right quadrant. + // Y=w*h, U=w/2*h/2, V=w/2*h/2 + // w*h + 2 ( w/2 * h/2 ) + // w*h + w*h/2 + texWidth = vTexWidth[0] + vTexWidth[1]; texHeight = vH; + break; + case YUVJ422P: + case YUV422P: + usesTexLookupShader = true; + // YUV422P: Adding U+V on right side of fixed height texture, + // since width is already aligned by decoder. + // Splitting texture to 4 columns + // Y covers columns 1+2 + // U covers columns 3 + // V covers columns 4 + texWidth = vTexWidth[0] + vTexWidth[1] + vTexWidth[2]; texHeight = vH; + break; + case YUYV422: // < packed YUV 4:2:2, 2x 16bpp, Y0 Cb Y1 Cr - stuffed into RGBA half width texture + case BGR24: + usesTexLookupShader = true; + texWidth = vTexWidth[0]; texHeight = vH; + break; + + case RGB24: + case ARGB: + case RGBA: + case ABGR: + case BGRA: + usesTexLookupShader = false; + texWidth = vTexWidth[0]; texHeight = vH; + break; + default: // FIXME: Add more formats ! + throw new RuntimeException("Unsupported pixelformat: "+vPixelFmt); + } } + + // defaults .. + final SampleFormat aSampleFmt; + avChosenAudioFormat = null;; + this.audioSamplesPerFrameAndChannel = 0; + + if( STREAM_ID_NONE != aid ) { + aSampleFmt = SampleFormat.valueOf(audioSampleFmt); + avChosenAudioFormat = avAudioFormat2Local(aSampleFmt, audioSampleRate, audioChannels); + this.audioSamplesPerFrameAndChannel = audioSamplesPerFrameAndChannel; + } else { + aSampleFmt = null; + } + if(DEBUG) { - System.err.println("XXX0: fmt "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane); + System.err.println("audio: id "+aid+", fmt "+aSampleFmt+", "+avChosenAudioFormat+", aFrameSize/fc "+audioSamplesPerFrameAndChannel); + System.err.println("video: id "+vid+", fmt "+vW+"x"+vH+", "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane+", usesTexLookupShader "+usesTexLookupShader); for(int i=0; i<3; i++) { - System.err.println("XXX0 "+i+": "+vTexWidth[i]+"/"+vLinesize[i]); + System.err.println("video: p["+i+"]: "+vTexWidth[i]); } - System.err.println("XXX0 total tex "+texWidth+"x"+texHeight); + System.err.println("video: total tex "+texWidth+"x"+texHeight); + System.err.println(this.toString()); } } - + + /** + * Native callback + * @param isInGLOrientation + * @param pixFmt + * @param planes + * @param bitsPerPixel + * @param bytesPerPixelPerPlane + * @param tWd0 + * @param tWd1 + * @param tWd2 + */ + void updateVidAttributes(boolean isInGLOrientation, int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane, + int tWd0, int tWd1, int tWd2, int vW, int vH) { + } + /** * {@inheritDoc} - * + * * If this implementation generates a specialized shader, * it allows the user to override the default function name <code>ffmpegTexture2D</code>. * Otherwise the call is delegated to it's super class. */ @Override - public String getTextureLookupFunctionName(String desiredFuncName) throws IllegalStateException { + public final String getTextureLookupFunctionName(String desiredFuncName) throws IllegalStateException { if(State.Uninitialized == state) { throw new IllegalStateException("Instance not initialized: "+this); } - if(PixelFormat.YUV420P == vPixelFmt) { + if( usesTexLookupShader ) { if(null != desiredFuncName && desiredFuncName.length()>0) { - textureLookupFunctionName = desiredFuncName; + texLookupFuncName = desiredFuncName; } - return textureLookupFunctionName; + return texLookupFuncName; } - return super.getTextureLookupFunctionName(desiredFuncName); + return super.getTextureLookupFunctionName(desiredFuncName); } - private String textureLookupFunctionName = "ffmpegTexture2D"; - + /** * {@inheritDoc} - * + * * Depending on the pixelformat, a specific conversion shader is being created, - * e.g. YUV420P to RGB. Otherwise the call is delegated to it's super class. - */ + * e.g. YUV420P to RGB. Otherwise the call is delegated to it's super class. + */ @Override - public String getTextureLookupFragmentShaderImpl() throws IllegalStateException { + public final String getTextureLookupFragmentShaderImpl() throws IllegalStateException { if(State.Uninitialized == state) { throw new IllegalStateException("Instance not initialized: "+this); } + if( !usesTexLookupShader ) { + return super.getTextureLookupFragmentShaderImpl(); + } final float tc_w_1 = (float)getWidth() / (float)texWidth; switch(vPixelFmt) { - case YUV420P: - return - "vec4 "+textureLookupFunctionName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+ + case YUVJ420P: + case YUV420P: // < planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + return + "vec4 "+texLookupFuncName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+ " vec2 u_off = vec2("+tc_w_1+", 0.0);\n"+ " 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).r;\n"+ - " u = texture2D(image, u_off+tc_half).r;\n"+ - " v = texture2D(image, v_off+tc_half).r;\n"+ + " y = texture2D(image, texCoord)."+singleTexComp+";\n"+ + " u = texture2D(image, u_off+tc_half)."+singleTexComp+";\n"+ + " v = texture2D(image, v_off+tc_half)."+singleTexComp+";\n"+ " y = 1.1643*(y-0.0625);\n"+ " u = u-0.5;\n"+ " v = v-0.5;\n"+ " r = y+1.5958*v;\n"+ " g = y-0.39173*u-0.81290*v;\n"+ - " b = y+2.017*u;\n"+ + " b = y+2.017*u;\n"+ " return vec4(r, g, b, 1);\n"+ "}\n" - ; - default: // FIXME: Add more planar formats ! - return super.getTextureLookupFragmentShaderImpl(); - } - } - - @Override - protected synchronized int getCurrentPositionImpl() { - return 0!=moviePtr ? getVideoPTS0(moviePtr) : 0; - } + ; - @Override - protected synchronized boolean setPlaySpeedImpl(float rate) { - return true; + case YUVJ422P: + case YUV422P: ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + return + "vec4 "+texLookupFuncName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+ + " vec2 u_off = vec2("+tc_w_1+" , 0.0);\n"+ + " vec2 v_off = vec2("+tc_w_1+" * 1.5, 0.0);\n"+ + " vec2 tc_halfw = vec2(texCoord.x*0.5, texCoord.y);\n"+ + " float y,u,v,r,g,b;\n"+ + " y = texture2D(image, texCoord)."+singleTexComp+";\n"+ + " u = texture2D(image, u_off+tc_halfw)."+singleTexComp+";\n"+ + " v = texture2D(image, v_off+tc_halfw)."+singleTexComp+";\n"+ + " y = 1.1643*(y-0.0625);\n"+ + " u = u-0.5;\n"+ + " v = v-0.5;\n"+ + " r = y+1.5958*v;\n"+ + " g = y-0.39173*u-0.81290*v;\n"+ + " b = y+2.017*u;\n"+ + " return vec4(r, g, b, 1);\n"+ + "}\n" + ; + + case YUYV422: // < packed YUV 4:2:2, 2 x 16bpp, [Y0 Cb] [Y1 Cr] + // Stuffed into RGBA half width texture + return + "vec4 "+texLookupFuncName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+ + " "+ + " float y1,u,y2,v,y,r,g,b;\n"+ + " vec2 tc_halfw = vec2(texCoord.x*0.5, texCoord.y);\n"+ + " vec4 yuyv = texture2D(image, tc_halfw).rgba;\n"+ + " y1 = yuyv.r;\n"+ + " u = yuyv.g;\n"+ + " y2 = yuyv.b;\n"+ + " v = yuyv.a;\n"+ + " y = mix( y1, y2, mod(gl_FragCoord.x, 2) ); /* avoid branching! */\n"+ + " y = 1.1643*(y-0.0625);\n"+ + " u = u-0.5;\n"+ + " v = v-0.5;\n"+ + " r = y+1.5958*v;\n"+ + " g = y-0.39173*u-0.81290*v;\n"+ + " b = y+2.017*u;\n"+ + " return vec4(r, g, b, 1);\n"+ + "}\n" + ; + case BGR24: + return + "vec4 "+texLookupFuncName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+ + " "+ + " vec3 bgr = texture2D(image, texCoord).rgb;\n"+ + " return vec4(bgr.b, bgr.g, bgr.r, 1);\n"+ /* just swizzle */ + "}\n" + ; + + default: // FIXME: Add more formats ! + throw new InternalError("Add proper mapping of: vPixelFmt "+vPixelFmt+", usesTexLookupShader "+usesTexLookupShader); + } } @Override - public synchronized boolean startImpl() { + public final boolean playImpl() { if(0==moviePtr) { return false; } + final int errno = natives.play0(moviePtr); + if( DEBUG_NATIVE && errno != 0 && errno != -ENOSYS) { + System.err.println("libav play err: "+errno); + } return true; } - /** @return time position after issuing the command */ @Override - public synchronized boolean pauseImpl() { + public final boolean pauseImpl() { if(0==moviePtr) { return false; } + final int errno = natives.pause0(moviePtr); + if( DEBUG_NATIVE && errno != 0 && errno != -ENOSYS) { + System.err.println("libav pause err: "+errno); + } return true; } - /** @return time position after issuing the command */ @Override - public synchronized boolean stopImpl() { + protected final synchronized int seekImpl(int msec) { if(0==moviePtr) { - return false; + throw new GLException("FFMPEG native instance null"); } - return true; + return natives.seek0(moviePtr, msec); } - /** @return time position after issuing the command */ @Override - protected synchronized int seekImpl(int msec) { - if(0==moviePtr) { - throw new GLException("FFMPEG native instance null"); - } - int pts0 = getVideoPTS0(moviePtr); - int pts1 = seek0(moviePtr, msec); - System.err.println("Seek: "+pts0+" -> "+msec+" : "+pts1); - return pts1; + protected void preNextTextureImpl(GL gl) { + psm.setUnpackAlignment(gl, 1); // RGBA ? 4 : 1 + gl.glActiveTexture(GL.GL_TEXTURE0+getTextureUnit()); } @Override - protected TextureSequence.TextureFrame getLastTextureImpl() { - return lastTex; + protected void postNextTextureImpl(GL gl) { + psm.restore(gl); } - - private long lastVideoTime = 0; - private int lastVideoPTS = 0; - private static final int dt_d = 9; - + @Override - protected TextureSequence.TextureFrame getNextTextureImpl(GL gl, boolean blocking) { + protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) { if(0==moviePtr) { throw new GLException("FFMPEG native instance null"); - } - if(null != lastTex) { - psm.setUnpackAlignment(gl, 1); // RGBA ? 4 : 1 - try { - final Texture tex = lastTex.getTexture(); - gl.glActiveTexture(GL.GL_TEXTURE0+getTextureUnit()); - tex.enable(gl); - tex.bind(gl); - readNextPacket0(moviePtr, procAddrGLTexSubImage2D, textureTarget, textureFormat, textureType); - } finally { - psm.restore(gl); - } - final int pts = getVideoPTS0(moviePtr); // this frame - if(blocking) { - // poor mans video sync .. TODO: off thread 'readNextPackage0(..)' on shared GLContext and multi textures/unit! - final long now = System.currentTimeMillis(); - final long now_d = now - lastVideoTime; - final long pts_d = pts - lastVideoPTS; - final long dt = (long) ( (float) ( pts_d - now_d ) / getPlaySpeed() ) ; - lastVideoTime = now; - // System.err.println("s: pts-v "+pts+", pts-d "+pts_d+", now_d "+now_d+", dt "+dt); - if(dt>dt_d) { - try { - Thread.sleep(dt-dt_d); - } catch (InterruptedException e) { } - } /* else if(0>pts_d) { - System.err.println("s: pts-v "+pts+", pts-d "+pts_d+", now_d "+now_d+", dt "+dt); - } */ - } - lastVideoPTS = pts; } - return lastTex; - } - - private void consumeAudio(int len) { - + int vPTS = TimeFrameI.INVALID_PTS; + if( null != gl ) { + final Texture tex = nextFrame.getTexture(); + tex.enable(gl); + tex.bind(gl); + } + + /** Try decode up to 10 packets to find one containing video. */ + for(int i=0; TimeFrameI.INVALID_PTS == vPTS && 10 > i; i++) { + vPTS = natives.readNextPacket0(moviePtr, textureTarget, textureFormat, textureType); + } + if( null != nextFrame ) { + nextFrame.setPTS(vPTS); + } + return vPTS; } - - private static native int getAvUtilVersion0(); - private static native int getAvFormatVersion0(); - private static native int getAvCodecVersion0(); - private static native boolean initIDs0(); - private native long createInstance0(boolean verbose); - private native void destroyInstance0(long moviePtr); - - private native void setStream0(long moviePtr, String url, int vid, int aid); - - private native int getVideoPTS0(long moviePtr); - - private native int getAudioPTS0(long moviePtr); - private native Buffer getAudioBuffer0(long moviePtr, int plane); - - private native int readNextPacket0(long moviePtr, long procAddrGLTexSubImage2D, int texTarget, int texFmt, int texType); - - private native int seek0(long moviePtr, int position); - - public static enum PixelFormat { - // NONE= -1, - YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) - YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr - RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB... - BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR... - YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) - YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) - YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) - YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) - GRAY8, ///< Y , 8bpp - MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb - MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb - PAL8, ///< 8 bit with RGB32 palette - YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of YUV420P and setting color_range - YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of YUV422P and setting color_range - YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of YUV444P and setting color_range - XVMC_MPEG2_MC,///< XVideo Motion Acceleration via common packet passing - XVMC_MPEG2_IDCT, - UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 - UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 - BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) - BGR4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits - BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) - RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) - RGB4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits - RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) - NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) - NV21, ///< as above, but U and V bytes are swapped - - ARGB, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB... - RGBA, ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA... - ABGR, ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR... - BGRA, ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA... - - GRAY16BE, ///< Y , 16bpp, big-endian - GRAY16LE, ///< Y , 16bpp, little-endian - YUV440P, ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) - YUVJ440P, ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of YUV440P and setting color_range - YUVA420P, ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) - VDPAU_H264,///< H.264 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - VDPAU_MPEG1,///< MPEG-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - VDPAU_MPEG2,///< MPEG-2 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - VDPAU_WMV3,///< WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - VDPAU_VC1, ///< VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian - RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian - - RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian - RGB565LE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian - RGB555BE, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), big-endian, most significant bit to 0 - RGB555LE, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), little-endian, most significant bit to 0 - - BGR565BE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian - BGR565LE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian - BGR555BE, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), big-endian, most significant bit to 1 - BGR555LE, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), little-endian, most significant bit to 1 - - VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers - VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers - VAAPI_VLD, ///< HW decoding through VA API, Picture.data[3] contains a vaapi_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - - YUV420P16LE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian - YUV420P16BE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian - YUV422P16LE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian - YUV422P16BE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian - YUV444P16LE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian - YUV444P16BE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian - VDPAU_MPEG4, ///< MPEG4 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers - DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer - - RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), little-endian, most significant bits to 0 - RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), big-endian, most significant bits to 0 - BGR444LE, ///< packed BGR 4:4:4, 16bpp, (msb)4A 4B 4G 4R(lsb), little-endian, most significant bits to 1 - BGR444BE, ///< packed BGR 4:4:4, 16bpp, (msb)4A 4B 4G 4R(lsb), big-endian, most significant bits to 1 - Y400A, ///< 8bit gray, 8bit alpha - BGR48BE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian - BGR48LE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian - YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian - YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian - YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian - YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian - YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian - YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian - YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian - YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian - YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian - YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian - YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian - YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian - VDA_VLD, ///< hardware decoding through VDA - GBRP, ///< planar GBR 4:4:4 24bpp - GBRP9BE, ///< planar GBR 4:4:4 27bpp, big endian - GBRP9LE, ///< planar GBR 4:4:4 27bpp, little endian - GBRP10BE, ///< planar GBR 4:4:4 30bpp, big endian - GBRP10LE, ///< planar GBR 4:4:4 30bpp, little endian - GBRP16BE, ///< planar GBR 4:4:4 48bpp, big endian - GBRP16LE, ///< planar GBR 4:4:4 48bpp, little endian - COUNT ///< number of pixel formats in this list - ; - public static PixelFormat valueOf(int i) { - for (PixelFormat fmt : PixelFormat.values()) { - if(fmt.ordinal() == i) { - return fmt; - } - } - return null; + + final void pushSound(ByteBuffer sampleData, int data_size, int audio_pts) { + setFirstAudioPTS2SCR( audio_pts ); + if( 1.0f == playSpeed || audioSinkPlaySpeedSet ) { + audioSink.enqueueData( audio_pts, sampleData, data_size); } } diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java new file mode 100644 index 000000000..b4b887bc9 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java @@ -0,0 +1,281 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.util.av.impl; + +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; + +/* pp */ abstract class FFMPEGNatives { + + private static final Object mutex_avcodec_openclose_jni = new Object(); + + final boolean initSymbols0(long[] symbols, int count) { + return initSymbols0(mutex_avcodec_openclose_jni, symbols, count); + } + abstract boolean initSymbols0(Object mutex_avcodec_openclose, long[] symbols, int count); + abstract int getAvUtilMajorVersionCC0(); + abstract int getAvFormatMajorVersionCC0(); + abstract int getAvCodecMajorVersionCC0(); + abstract int getAvResampleMajorVersionCC0(); + abstract int getSwResampleMajorVersionCC0(); + + abstract long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); + abstract void destroyInstance0(long moviePtr); + + /** + * Issues {@link #updateAttributes(int, int, int, int, int, int, int, float, int, int, String, String)} + * and {@link #updateAttributes2(int, int, int, int, int, int, int, int, int, int)}. + * + * @param moviePtr + * @param url + * @param vid + * @param sizes requested video size as string, i.e. 'hd720'. May be null to favor vWidth and vHeight. + * @param vWidth requested video width (for camera mode) + * @param vHeight requested video width (for camera mode) + * @param vRate requested video framerate (for camera mode) + * @param aid + * @param aPrefSampleRate + * @param aPrefChannelCount + */ + abstract void setStream0(long moviePtr, String url, boolean isCameraInput, + int vid, String sizes, int vWidth, int vHeight, + int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate); + + abstract void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish); + + abstract int getVideoPTS0(long moviePtr); + + abstract int getAudioPTS0(long moviePtr); + + /** + * @return resulting current video PTS, or {@link TextureFrame#INVALID_PTS} + */ + abstract int readNextPacket0(long moviePtr, int texTarget, int texFmt, int texType); + + abstract int play0(long moviePtr); + abstract int pause0(long moviePtr); + abstract int seek0(long moviePtr, int position); + + /** FFMPEG/libAV Audio Sample Format */ + public static enum SampleFormat { + // NONE = -1, + U8, ///< unsigned 8 bits + S16, ///< signed 16 bits + S32, ///< signed 32 bits + FLT, ///< float + DBL, ///< double + + U8P, ///< unsigned 8 bits, planar + S16P, ///< signed 16 bits, planar + S32P, ///< signed 32 bits, planar + FLTP, ///< float, planar + DBLP, ///< double, planar + + COUNT; ///< Number of sample formats. + + /** + * Returns the matching SampleFormat value corresponding to the given SampleFormat's integer ordinal. + * <pre> + * given: + * ordinal = enumValue.ordinal() + * reverse: + * enumValue = EnumClass.values()[ordinal] + * </pre> + * @throws IllegalArgumentException if the given ordinal is out of range, i.e. not within [ 0 .. SampleFormat.values().length-1 ] + */ + public static SampleFormat valueOf(int ordinal) throws IllegalArgumentException { + final SampleFormat[] all = SampleFormat.values(); + if( 0 <= ordinal && ordinal < all.length ) { + return all[ordinal]; + } + throw new IllegalArgumentException("Ordinal "+ordinal+" out of range of SampleFormat.values()[0.."+(all.length-1)+"]"); + } + }; + + /** FFMPEG/libAV Pixel Format */ + public static enum PixelFormat { + // NONE= -1, + /** planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) */ + YUV420P, + /** packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr ( sharing Cb and Cr w/ 2 pixels )*/ + YUYV422, + /** packed RGB 8:8:8, 24bpp, RGBRGB... */ + RGB24, + /** packed RGB 8:8:8, 24bpp, BGRBGR... */ + BGR24, + /** planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) */ + YUV422P, + /** planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) */ + YUV444P, + /** planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) */ + YUV410P, + /** planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) */ + YUV411P, + /** Y, 8bpp */ + GRAY8, + /** Y, 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb */ + MONOWHITE, + /** Y, 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb */ + MONOBLACK, + /** 8 bit with RGB32 palette */ + PAL8, + /** planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of YUV420P and setting color_range */ + YUVJ420P, + /** planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of YUV422P and setting color_range */ + YUVJ422P, + /** planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of YUV444P and setting color_range */ + YUVJ444P, + /** XVideo Motion Acceleration via common packet passing */ + XVMC_MPEG2_MC, + /** */ + XVMC_MPEG2_IDCT, + /** packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 */ + UYVY422, + /** packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 */ + UYYVYY411, + /** packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) */ + BGR8, + /** packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits */ + BGR4, + /** packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) */ + BGR4_BYTE, + /** packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) */ + RGB8, + /** packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits */ + RGB4, + /** packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) */ + RGB4_BYTE, + /** planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) */ + NV12, + /** as above, but U and V bytes are swapped */ + NV21, + + /** packed ARGB 8:8:8:8, 32bpp, ARGBARGB... */ + ARGB, + /** packed RGBA 8:8:8:8, 32bpp, RGBARGBA... */ + RGBA, + /** packed ABGR 8:8:8:8, 32bpp, ABGRABGR... */ + ABGR, + /** packed BGRA 8:8:8:8, 32bpp, BGRABGRA... */ + BGRA, + + /** Y, 16bpp, big-endian */ + GRAY16BE, + /** Y , 16bpp, little-endian */ + GRAY16LE, + /** planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) */ + YUV440P, + /** planar YUV 4:4:0 full scale (JPEG), deprecated in favor of YUV440P and setting color_range */ + YUVJ440P, + /** planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) */ + YUVA420P, + /** H.264 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ + VDPAU_H264, + /** MPEG-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ + VDPAU_MPEG1, + /** MPEG-2 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ + VDPAU_MPEG2, + /** WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ + VDPAU_WMV3, + /** VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers */ + VDPAU_VC1, + /** packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian */ + RGB48BE, + /** packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian */ + RGB48LE, + + RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian + RGB565LE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian + RGB555BE, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), big-endian, most significant bit to 0 + RGB555LE, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), little-endian, most significant bit to 0 + + BGR565BE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian + BGR565LE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian + BGR555BE, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), big-endian, most significant bit to 1 + BGR555LE, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), little-endian, most significant bit to 1 + + VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers + VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers + VAAPI_VLD, ///< HW decoding through VA API, Picture.data[3] contains a vaapi_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + + YUV420P16LE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + YUV420P16BE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + YUV422P16LE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + YUV422P16BE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + YUV444P16LE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + YUV444P16BE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + VDPAU_MPEG4, ///< MPEG4 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer + + RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), little-endian, most significant bits to 0 + RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), big-endian, most significant bits to 0 + BGR444LE, ///< packed BGR 4:4:4, 16bpp, (msb)4A 4B 4G 4R(lsb), little-endian, most significant bits to 1 + BGR444BE, ///< packed BGR 4:4:4, 16bpp, (msb)4A 4B 4G 4R(lsb), big-endian, most significant bits to 1 + Y400A, ///< 8bit gray, 8bit alpha + BGR48BE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian + BGR48LE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian + YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + VDA_VLD, ///< hardware decoding through VDA + GBRP, ///< planar GBR 4:4:4 24bpp + GBRP9BE, ///< planar GBR 4:4:4 27bpp, big endian + GBRP9LE, ///< planar GBR 4:4:4 27bpp, little endian + GBRP10BE, ///< planar GBR 4:4:4 30bpp, big endian + GBRP10LE, ///< planar GBR 4:4:4 30bpp, little endian + GBRP16BE, ///< planar GBR 4:4:4 48bpp, big endian + GBRP16LE, ///< planar GBR 4:4:4 48bpp, little endian + COUNT ///< number of pixel formats in this list + ; + /** + * Returns the matching PixelFormat value corresponding to the given PixelFormat's integer ordinal. + * <pre> + * given: + * ordinal = enumValue.ordinal() + * reverse: + * enumValue = EnumClass.values()[ordinal] + * </pre> + * @throws IllegalArgumentException if the given ordinal is out of range, i.e. not within [ 0 .. PixelFormat.values().length-1 ] + */ + public static PixelFormat valueOf(int ordinal) throws IllegalArgumentException { + final PixelFormat[] all = PixelFormat.values(); + if( 0 <= ordinal && ordinal < all.length ) { + return all[ordinal]; + } + throw new IllegalArgumentException("Ordinal "+ordinal+" out of range of PixelFormat.values()[0.."+(all.length-1)+"]"); + } + } +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java new file mode 100644 index 000000000..22a045825 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java @@ -0,0 +1,41 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.util.av.impl; + +import com.jogamp.common.util.VersionNumber; + +class FFMPEGStaticNatives { + static VersionNumber getAVVersion(int vers) { + return new VersionNumber( ( vers >> 16 ) & 0xFF, + ( vers >> 8 ) & 0xFF, + ( vers >> 0 ) & 0xFF ); + } + static native boolean initIDs0(); + + static native int getAvVersion0(long func); +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java new file mode 100644 index 000000000..6bab23f25 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java @@ -0,0 +1,78 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.util.av.impl; + +class FFMPEGv08Natives extends FFMPEGNatives { + @Override + native boolean initSymbols0(Object mutex_avcodec_openclose, long[] symbols, int count); + + @Override + native int getAvUtilMajorVersionCC0(); + + @Override + native int getAvFormatMajorVersionCC0(); + + @Override + native int getAvCodecMajorVersionCC0(); + + @Override + native int getAvResampleMajorVersionCC0(); + + @Override + native int getSwResampleMajorVersionCC0(); + + @Override + native long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); + + @Override + native void destroyInstance0(long moviePtr); + + @Override + native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, String sizes, int vWidth, int vHeight, int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate); + + @Override + native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish); + + @Override + native int getVideoPTS0(long moviePtr); + + @Override + native int getAudioPTS0(long moviePtr); + + @Override + native int readNextPacket0(long moviePtr, int texTarget, int texFmt, int texType); + + @Override + native int play0(long moviePtr); + + @Override + native int pause0(long moviePtr); + + @Override + native int seek0(long moviePtr, int position); +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java new file mode 100644 index 000000000..a48b5f21f --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java @@ -0,0 +1,78 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.util.av.impl; + +class FFMPEGv09Natives extends FFMPEGNatives { + @Override + native boolean initSymbols0(Object mutex_avcodec_openclose, long[] symbols, int count); + + @Override + native int getAvUtilMajorVersionCC0(); + + @Override + native int getAvFormatMajorVersionCC0(); + + @Override + native int getAvCodecMajorVersionCC0(); + + @Override + native int getAvResampleMajorVersionCC0(); + + @Override + native int getSwResampleMajorVersionCC0(); + + @Override + native long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); + + @Override + native void destroyInstance0(long moviePtr); + + @Override + native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, String sizes, int vWidth, int vHeight, int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate); + + @Override + native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish); + + @Override + native int getVideoPTS0(long moviePtr); + + @Override + native int getAudioPTS0(long moviePtr); + + @Override + native int readNextPacket0(long moviePtr, int texTarget, int texFmt, int texType); + + @Override + native int play0(long moviePtr); + + @Override + native int pause0(long moviePtr); + + @Override + native int seek0(long moviePtr, int position); +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv10Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv10Natives.java new file mode 100644 index 000000000..f35fb29dc --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv10Natives.java @@ -0,0 +1,78 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.opengl.util.av.impl; + +class FFMPEGv10Natives extends FFMPEGNatives { + @Override + native boolean initSymbols0(Object mutex_avcodec_openclose, long[] symbols, int count); + + @Override + native int getAvUtilMajorVersionCC0(); + + @Override + native int getAvFormatMajorVersionCC0(); + + @Override + native int getAvCodecMajorVersionCC0(); + + @Override + native int getAvResampleMajorVersionCC0(); + + @Override + native int getSwResampleMajorVersionCC0(); + + @Override + native long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); + + @Override + native void destroyInstance0(long moviePtr); + + @Override + native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, String sizes, int vWidth, int vHeight, int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate); + + @Override + native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish); + + @Override + native int getVideoPTS0(long moviePtr); + + @Override + native int getAudioPTS0(long moviePtr); + + @Override + native int readNextPacket0(long moviePtr, int texTarget, int texFmt, int texType); + + @Override + native int play0(long moviePtr); + + @Override + native int pause0(long moviePtr); + + @Override + native int seek0(long moviePtr, int position); +} diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java index aef98fcde..05a94def8 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -29,11 +29,9 @@ package jogamp.opengl.util.av.impl; import java.io.IOException; -import java.net.URL; import javax.media.opengl.GL; import javax.media.opengl.GLException; -import javax.media.opengl.GLProfile; import com.jogamp.opengl.util.texture.TextureSequence; @@ -47,18 +45,18 @@ import jogamp.opengl.util.av.EGLMediaPlayerImpl; */ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { static final boolean available; - + static { - // OMX binding is included in jogl_desktop and jogl_mobile + available = false; + /** FIXME! + // OMX binding is included in jogl_desktop and jogl_mobile GLProfile.initSingleton(); - available = initIDs0(); + available = initIDs0(); */ } - + public static final boolean isAvailable() { return available; } - + protected long moviePtr = 0; - - protected TextureSequence.TextureFrame lastTex = null; public OMXGLMediaPlayer() { super(TextureType.KHRImage, true); @@ -72,51 +70,60 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { moviePtr = _createInstance(); if(0==moviePtr) { throw new GLException("Couldn't create OMXInstance"); - } + } } - + @Override - protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) { - final EGLTextureFrame eglTex = (EGLTextureFrame) super.createTexImage(gl, idx, tex); - _setStreamEGLImageTexture2D(moviePtr, idx, tex[idx], eglTex.getImage(), eglTex.getSync()); - lastTex = eglTex; + protected TextureSequence.TextureFrame createTexImage(GL gl, int texName) { + final EGLTextureFrame eglTex = (EGLTextureFrame) super.createTexImage(gl, texName); + _setStreamEGLImageTexture2D(moviePtr, texName, eglTex.getImage(), eglTex.getSync()); return eglTex; } - + @Override - protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) { - lastTex = null; - super.destroyTexImage(gl, imgTex); + protected void destroyTexFrame(GL gl, TextureSequence.TextureFrame imgTex) { + super.destroyTexFrame(gl, imgTex); } - + @Override protected void destroyImpl(GL gl) { - _detachVideoRenderer(moviePtr); if (moviePtr != 0) { + _stop(moviePtr); + _detachVideoRenderer(moviePtr); _destroyInstance(moviePtr); moviePtr = 0; } } - + @Override - protected void initGLStreamImpl(GL gl, int[] texNames) throws IOException { + protected void initStreamImpl(int vid, int aid) throws IOException { if(0==moviePtr) { throw new GLException("OMX native instance null"); } - final URL url = urlConn.getURL(); - if(!url.getProtocol().equals("file")) { - throw new IOException("Only file URLs are allowed: "+url); + if(!streamLoc.getScheme().equals("file")) { + throw new IOException("Only file schemes are allowed: "+streamLoc); + } + final String path=streamLoc.getPath(); + if(DEBUG) { + System.out.println("initGLStream: clean path "+path); + } + + if(DEBUG) { + System.out.println("initGLStream: p1 "+this); } - final String path=url.getPath(); - System.out.println("setURL: clean path "+path); - - System.out.println("setURL: p1 "+this); _setStream(moviePtr, textureCount, path); - System.out.println("setURL: p2 "+this); + if(DEBUG) { + System.out.println("initGLStream: p2 "+this); + } } - @Override - protected int getCurrentPositionImpl() { + protected final void initGLImpl(GL gl) throws IOException, GLException { + // NOP + isInGLOrientation = true; + } + + @Override + protected int getAudioPTSImpl() { return 0!=moviePtr ? _getCurrentPosition(moviePtr) : 0; } @@ -130,7 +137,7 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { } @Override - public synchronized boolean startImpl() { + public synchronized boolean playImpl() { if(0==moviePtr) { return false; } @@ -150,16 +157,6 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { /** @return time position after issuing the command */ @Override - public synchronized boolean stopImpl() { - if(0==moviePtr) { - return false; - } - _stop(moviePtr); - return true; - } - - /** @return time position after issuing the command */ - @Override protected int seekImpl(int msec) { if(0==moviePtr) { throw new GLException("OMX native instance null"); @@ -168,36 +165,33 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { } @Override - protected TextureSequence.TextureFrame getLastTextureImpl() { - return lastTex; - } - - @Override - protected TextureSequence.TextureFrame getNextTextureImpl(GL gl, boolean blocking) { + protected int getNextTextureImpl(GL gl, TextureFrame nextFrame) { if(0==moviePtr) { throw new GLException("OMX native instance null"); } - final int nextTex = _getNextTextureID(moviePtr, blocking); + final int nextTex = _getNextTextureID(moviePtr, true); if(0 < nextTex) { - final TextureSequence.TextureFrame eglImgTex = texFrameMap.get(new Integer(_getNextTextureID(moviePtr, blocking))); + // FIXME set pts ! + /* FIXME + final TextureSequence.TextureFrame eglImgTex = + texFrameMap.get(new Integer(_getNextTextureID(moviePtr, blocking))); if(null!=eglImgTex) { lastTex = eglImgTex; - } + } */ } - return lastTex; + return 0; // FIXME: return pts } - + private String replaceAll(String orig, String search, String repl) { - String dest=null; + StringBuilder dest = new StringBuilder(); // In case replaceAll / java.util.regex.* is not supported (-> CVM) int i=0,j; - dest = new String(); while((j=orig.indexOf(search, i))>=0) { - dest=dest.concat(orig.substring(i, j)); - dest=dest.concat(repl); + dest.append(orig.substring(i, j)); + dest.append(repl); i=j+1; } - return dest.concat(orig.substring(i, orig.length())); + return dest.append(orig.substring(i, orig.length())).toString(); } private void errorCheckEGL(String s) { @@ -208,15 +202,15 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { } private static native boolean initIDs0(); - private native long _createInstance(); + private native long _createInstance(); private native void _destroyInstance(long moviePtr); - + private native void _detachVideoRenderer(long moviePtr); // stop before private native void _attachVideoRenderer(long moviePtr); // detach before private native void _setStream(long moviePtr, int textureNum, String path); private native void _activateStream(long moviePtr); - - private native void _setStreamEGLImageTexture2D(long moviePtr, int i, int tex, long image, long sync); + + private native void _setStreamEGLImageTexture2D(long moviePtr, int tex, long image, long sync); private native int _seek(long moviePtr, int position); private native void _setPlaySpeed(long moviePtr, float rate); private native void _play(long moviePtr); diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java index 602d283d6..1f402f48b 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java @@ -33,40 +33,47 @@ import java.nio.Buffer; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; -import jogamp.opengl.util.GLArrayHandler; import jogamp.opengl.util.GLArrayHandlerFlat; +import jogamp.opengl.util.GLVBOArrayHandler; import com.jogamp.opengl.util.GLArrayDataEditable; import com.jogamp.opengl.util.glsl.ShaderState; /** - * Used for 1:1 GLSL arrays, i.e. where the buffer data - * represents this array only. + * Used for 1:1 GLSL arrays, i.e. where the buffer data + * represents this array only. */ -public class GLSLArrayHandler implements GLArrayHandler { - private GLArrayDataEditable ad; +public class GLSLArrayHandler extends GLVBOArrayHandler { public GLSLArrayHandler(GLArrayDataEditable ad) { - this.ad = ad; + super(ad); } - + + @Override public final void setSubArrayVBOName(int vboName) { throw new UnsupportedOperationException(); } - + + @Override public final void addSubHandler(GLArrayHandlerFlat handler) { throw new UnsupportedOperationException(); } - - public final void syncData(GL gl, boolean enable, Object ext) { + + @Override + public final void enableState(GL gl, boolean enable, Object ext) { final GL2ES2 glsl = gl.getGL2ES2(); - final ShaderState st = (ShaderState) ext; - + if( null != ext ) { + enableShaderState(glsl, enable, (ShaderState)ext); + } else { + enableSimple(glsl, enable); + } + } + + private final void enableShaderState(GL2ES2 glsl, boolean enable, ShaderState st) { if(enable) { - final Buffer buffer = ad.getBuffer(); /* * This would be the non optimized code path: - * + * if(ad.isVBO()) { glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); if(!ad.isVBOWritten()) { @@ -78,6 +85,7 @@ public class GLSLArrayHandler implements GLArrayHandler { } st.vertexAttribPointer(glsl, ad); */ + final Buffer buffer = ad.getBuffer(); if(ad.isVBO()) { // bind and refresh the VBO / vertex-attr only if necessary if(!ad.isVBOWritten()) { @@ -87,6 +95,7 @@ public class GLSLArrayHandler implements GLArrayHandler { } ad.setVBOWritten(true); st.vertexAttribPointer(glsl, ad); + glsl.glBindBuffer(ad.getVBOTarget(), 0); } else if(st.getAttribLocation(glsl, ad) >= 0) { // didn't experience a performance hit on this query .. // (using ShaderState's location query above to validate the location) @@ -95,26 +104,69 @@ public class GLSLArrayHandler implements GLArrayHandler { if(ad.getVBOName() != qi[0]) { glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); st.vertexAttribPointer(glsl, ad); + glsl.glBindBuffer(ad.getVBOTarget(), 0); } } } else if(null!=buffer) { st.vertexAttribPointer(glsl, ad); } - } else if(ad.isVBO()) { - glsl.glBindBuffer(ad.getVBOTarget(), 0); - } - } - - public final void enableState(GL gl, boolean enable, Object ext) { - final GL2ES2 glsl = gl.getGL2ES2(); - final ShaderState st = (ShaderState) ext; - - if(enable) { + st.enableVertexAttribArray(glsl, ad); } else { st.disableVertexAttribArray(glsl, ad); } } + private final void enableSimple(GL2ES2 glsl, boolean enable) { + final int location = ad.getLocation(); + if( 0 > location ) { + return; + } + if(enable) { + /* + * This would be the non optimized code path: + * + if(ad.isVBO()) { + glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); + if(!ad.isVBOWritten()) { + if(null!=buffer) { + glsl.glBufferData(ad.getVBOTarget(), ad.getSizeInBytes(), buffer, ad.getVBOUsage()); + } + ad.setVBOWritten(true); + } + } + st.vertexAttribPointer(glsl, ad); + */ + final Buffer buffer = ad.getBuffer(); + if(ad.isVBO()) { + // bind and refresh the VBO / vertex-attr only if necessary + if(!ad.isVBOWritten()) { + glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); + if(null!=buffer) { + glsl.glBufferData(ad.getVBOTarget(), ad.getSizeInBytes(), buffer, ad.getVBOUsage()); + } + ad.setVBOWritten(true); + glsl.glVertexAttribPointer(ad); + glsl.glBindBuffer(ad.getVBOTarget(), 0); + } else { + // didn't experience a performance hit on this query .. + // (using ShaderState's location query above to validate the location) + final int[] qi = new int[1]; + glsl.glGetVertexAttribiv(location, GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, qi, 0); + if(ad.getVBOName() != qi[0]) { + glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); + glsl.glVertexAttribPointer(ad); + glsl.glBindBuffer(ad.getVBOTarget(), 0); + } + } + } else if(null!=buffer) { + glsl.glVertexAttribPointer(ad); + } + + glsl.glEnableVertexAttribArray(location); + } else { + glsl.glDisableVertexAttribArray(location); + } + } } diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java index c4b761b13..34a381d7d 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java @@ -37,7 +37,7 @@ import com.jogamp.opengl.util.GLArrayDataWrapper; import com.jogamp.opengl.util.glsl.ShaderState; /** - * Used for interleaved GLSL arrays, i.e. where the buffer data itself is handled + * Used for interleaved GLSL arrays, i.e. where the buffer data itself is handled * separately and interleaves many arrays. */ public class GLSLArrayHandlerFlat implements GLArrayHandlerFlat { @@ -47,43 +47,57 @@ public class GLSLArrayHandlerFlat implements GLArrayHandlerFlat { this.ad = ad; } + @Override public GLArrayDataWrapper getData() { return ad; } - - public final void syncData(GL gl, boolean enable, boolean force, Object ext) { - if(enable) { - final GL2ES2 glsl = gl.getGL2ES2(); - final ShaderState st = (ShaderState) ext; + @Override + public final void syncData(GL gl, Object ext) { + final GL2ES2 glsl = gl.getGL2ES2(); + if( null != ext ) { + ((ShaderState)ext).vertexAttribPointer(glsl, ad); + } else { + if( 0 <= ad.getLocation() ) { + glsl.glVertexAttribPointer(ad); + } + } + /** + * Due to probable application VBO switching, this might not make any sense .. + * + if(!written) { st.vertexAttribPointer(glsl, ad); - /** - * Due to probable application VBO switching, this might not make any sense .. - * - if(force) { + } else if(st.getAttribLocation(glsl, ad) >= 0) { + final int[] qi = new int[1]; + glsl.glGetVertexAttribiv(ad.getLocation(), GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, qi, 0); + if(ad.getVBOName() != qi[0]) { + System.err.println("XXX1: "+ad.getName()+", vbo ad "+ad.getVBOName()+", gl "+qi[0]+", "+ad); st.vertexAttribPointer(glsl, ad); - } else if(st.getAttribLocation(glsl, ad) >= 0) { - final int[] qi = new int[1]; - glsl.glGetVertexAttribiv(ad.getLocation(), GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, qi, 0); - if(ad.getVBOName() != qi[0]) { - System.err.println("XXX1: "+ad.getName()+", vbo ad "+ad.getVBOName()+", gl "+qi[0]+", "+ad); - st.vertexAttribPointer(glsl, ad); - } else { - System.err.println("XXX0: "+ad.getName()+", vbo ad "+ad.getVBOName()+", gl "+qi[0]+", "+ad); - } - }*/ - } + } else { + System.err.println("XXX0: "+ad.getName()+", vbo ad "+ad.getVBOName()+", gl "+qi[0]+", "+ad); + } + }*/ } + @Override public final void enableState(GL gl, boolean enable, Object ext) { final GL2ES2 glsl = gl.getGL2ES2(); - final ShaderState st = (ShaderState) ext; - - if(enable) { - st.enableVertexAttribArray(glsl, ad); + if( null != ext ) { + final ShaderState st = (ShaderState)ext; + if(enable) { + st.enableVertexAttribArray(glsl, ad); + } else { + st.disableVertexAttribArray(glsl, ad); + } } else { - st.disableVertexAttribArray(glsl, ad); + final int location = ad.getLocation(); + if( 0 <= location ) { + if(enable) { + glsl.glEnableVertexAttribArray(location); + } else { + glsl.glDisableVertexAttribArray(location); + } + } } - } + } } - diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java index f50429623..e153082e0 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java @@ -28,75 +28,59 @@ package jogamp.opengl.util.glsl; -import java.nio.Buffer; import java.util.ArrayList; import java.util.List; import javax.media.opengl.GL; -import jogamp.opengl.util.GLArrayHandler; import jogamp.opengl.util.GLArrayHandlerFlat; +import jogamp.opengl.util.GLVBOArrayHandler; import com.jogamp.opengl.util.GLArrayDataEditable; /** - * Interleaved fixed function arrays, i.e. where this buffer data - * represents many arrays. + * Interleaved fixed function arrays, i.e. where this buffer data + * represents many arrays. */ -public class GLSLArrayHandlerInterleaved implements GLArrayHandler { - private GLArrayDataEditable ad; - private List<GLArrayHandlerFlat> subArrays = new ArrayList<GLArrayHandlerFlat>(); +public class GLSLArrayHandlerInterleaved extends GLVBOArrayHandler { + private final List<GLArrayHandlerFlat> subArrays = new ArrayList<GLArrayHandlerFlat>(); public GLSLArrayHandlerInterleaved(GLArrayDataEditable ad) { - this.ad = ad; + super(ad); } - + + @Override public final void setSubArrayVBOName(int vboName) { for(int i=0; i<subArrays.size(); i++) { subArrays.get(i).getData().setVBOName(vboName); - } + } } - + + @Override public final void addSubHandler(GLArrayHandlerFlat handler) { subArrays.add(handler); } - private final void syncSubData(GL gl, boolean enable, boolean force, Object ext) { + private final void syncSubData(GL gl, Object ext) { for(int i=0; i<subArrays.size(); i++) { - subArrays.get(i).syncData(gl, enable, force, ext); - } - } - - public final void syncData(GL gl, boolean enable, Object ext) { - if(!ad.isVBO()) { - throw new InternalError("Interleaved handle is not VBO: "+ad); - } - + subArrays.get(i).syncData(gl, ext); + } + } + + @Override + public final void enableState(GL gl, boolean enable, Object ext) { if(enable) { - final Buffer buffer = ad.getBuffer(); - final boolean vboWritten = ad.isVBOWritten(); - - // always bind and refresh the VBO mgr, - // in case more than one gl*Pointer objects are in use - gl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName()); - if(!vboWritten) { - if(null!=buffer) { - gl.glBufferData(ad.getVBOTarget(), buffer.limit() * ad.getComponentSizeInBytes(), buffer, ad.getVBOUsage()); - } - ad.setVBOWritten(true); + if(!ad.isVBO()) { + throw new InternalError("Interleaved handle is not VBO: "+ad); } - // sub data will decide weather to update the vertex attrib pointer - syncSubData(gl, true, !vboWritten, ext); - } else { - // NOP on GLSL: syncSubData(gl, false, ext); - gl.glBindBuffer(ad.getVBOTarget(), 0); + bindBuffer(gl, true); + // sub data will decide whether to update the vertex attrib pointer + syncSubData(gl, ext); + bindBuffer(gl, false); } - } - - public final void enableState(GL gl, boolean enable, Object ext) { for(int i=0; i<subArrays.size(); i++) { subArrays.get(i).enableState(gl, enable, ext); - } + } } } diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLTextureRaster.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLTextureRaster.java new file mode 100644 index 000000000..dba408554 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLTextureRaster.java @@ -0,0 +1,195 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl.util.glsl; + +import java.nio.FloatBuffer; + +import com.jogamp.opengl.util.GLArrayDataServer; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLArrayData; +import javax.media.opengl.GLException; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +public class GLSLTextureRaster { + private final boolean textureVertFlipped; + private final int textureUnit; + + private ShaderProgram sp; + private PMVMatrix pmvMatrix; + private GLUniformData pmvMatrixUniform; + private GLUniformData activeTexUniform; + private GLArrayDataServer interleavedVBO; + + public GLSLTextureRaster(int textureUnit, boolean textureVertFlipped) { + this.textureVertFlipped = textureVertFlipped; + this.textureUnit = textureUnit; + } + + public int getTextureUnit() { return textureUnit; } + + static final String shaderBasename = "texture01_xxx"; + static final String shaderSrcPath = "../../shader"; + static final String shaderBinPath = "../../shader/bin"; + + public void init(GL2ES2 gl) { + // Create & Compile the shader objects + final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), + shaderSrcPath, shaderBinPath, shaderBasename, true); + final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), + shaderSrcPath, shaderBinPath, shaderBasename, true); + rsVp.defaultShaderCustomization(gl, true, true); + rsFp.defaultShaderCustomization(gl, true, true); + + // Create & Link the shader program + sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + if(!sp.link(gl, System.err)) { + throw new GLException("Couldn't link program: "+sp); + } + sp.useProgram(gl, true); + + // setup mgl_PMVMatrix + pmvMatrix = new PMVMatrix(); + pmvMatrix.glMatrixMode(PMVMatrix.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); // P, Mv + if( pmvMatrixUniform.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+pmvMatrixUniform+" in shader: "+sp); + } + gl.glUniform(pmvMatrixUniform); + + activeTexUniform = new GLUniformData("mgl_Texture0", textureUnit); + if( activeTexUniform.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+activeTexUniform+" in shader: "+sp); + } + gl.glUniform(activeTexUniform); + + final float[] s_quadTexCoords; + if( textureVertFlipped ) { + s_quadTexCoords = s_quadTexCoords01; + } else { + s_quadTexCoords = s_quadTexCoords00; + } + + interleavedVBO = GLArrayDataServer.createGLSLInterleaved(3+2, GL.GL_FLOAT, false, 2*4, GL.GL_STATIC_DRAW); + { + final GLArrayData vArrayData = interleavedVBO.addGLSLSubArray("mgl_Vertex", 3, GL.GL_ARRAY_BUFFER); + if( vArrayData.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+vArrayData+" in shader: "+sp); + } + final GLArrayData tArrayData = interleavedVBO.addGLSLSubArray("mgl_MultiTexCoord", 2, GL.GL_ARRAY_BUFFER); + if( tArrayData.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+tArrayData+" in shader: "+sp); + } + final FloatBuffer ib = (FloatBuffer)interleavedVBO.getBuffer(); + for(int i=0; i<4; i++) { + ib.put(s_quadVertices, i*3, 3); + ib.put(s_quadTexCoords, i*2, 2); + } + } + interleavedVBO.seal(gl, true); + interleavedVBO.enableBuffer(gl, false); + + sp.useProgram(gl, false); + } + + public void reshape(GL2ES2 gl, int x, int y, int width, int height) { + if(null != sp) { + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 10.0f); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + + sp.useProgram(gl, true); + gl.glUniform(pmvMatrixUniform); + sp.useProgram(gl, false); + } + } + + public void dispose(GL2ES2 gl) { + if(null != pmvMatrixUniform) { + pmvMatrixUniform = null; + } + if(null != pmvMatrix) { + pmvMatrix.destroy(); + pmvMatrix=null; + } + if(null != interleavedVBO) { + interleavedVBO.destroy(gl); + interleavedVBO=null; + } + if(null != sp) { + sp.destroy(gl); + sp=null; + } + } + + public void display(GL2ES2 gl) { + if(null != sp) { + sp.useProgram(gl, true); + interleavedVBO.enableBuffer(gl, true); + + gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4); + + interleavedVBO.enableBuffer(gl, false); + sp.useProgram(gl, false); + } + } + + private static final float[] s_quadVertices = { + -1f, -1f, 0f, // LB + 1f, -1f, 0f, // RB + -1f, 1f, 0f, // LT + 1f, 1f, 0f // RT + }; + private static final float[] s_quadTexCoords00 = { + 0f, 0f, // LB + 1f, 0f, // RB + 0f, 1f, // LT + 1f, 1f // RT + }; + private static final float[] s_quadTexCoords01 = { + 0f, 1f, // LB + 1f, 1f, // RB + 0f, 0f, // LT + 1f, 0f // RT + }; +} + diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java index 897967f8b..458a9c94f 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java @@ -29,87 +29,115 @@ package jogamp.opengl.util.glsl.fixedfunc; -import javax.media.opengl.*; -import javax.media.opengl.fixedfunc.*; -import com.jogamp.common.nio.Buffers; -import com.jogamp.opengl.util.*; +import java.nio.Buffer; +import java.nio.IntBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLArrayData; +import javax.media.opengl.GLException; +import javax.media.opengl.fixedfunc.GLLightingFunc; +import javax.media.opengl.fixedfunc.GLMatrixFunc; +import javax.media.opengl.fixedfunc.GLPointerFunc; -import java.nio.*; +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.ValueConv; +import com.jogamp.opengl.util.GLArrayDataWrapper; +import com.jogamp.opengl.util.GLBuffers; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.fixedfunc.ShaderSelectionMode; public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFunc { public static final int MAX_TEXTURE_UNITS = 8; - protected FixedFuncPipeline fixedFunction=null; - protected PMVMatrix pmvMatrix=null; - protected GL2ES2 gl=null; + protected FixedFuncPipeline fixedFunction; + protected PMVMatrix pmvMatrix; + protected boolean ownsPMVMatrix; + protected GL2ES2 gl; - public FixedFuncHook (GL2ES2 gl) { - this(gl, null); + /** + * @param gl + * @param mode TODO + * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} + */ + public FixedFuncHook (GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix) { + this.gl = gl; + if(null != pmvMatrix) { + this.ownsPMVMatrix = false; + this.pmvMatrix = pmvMatrix; + } else { + this.ownsPMVMatrix = true; + this.pmvMatrix = new PMVMatrix(); + } + fixedFunction = new FixedFuncPipeline(this.gl, mode, this.pmvMatrix); } - public FixedFuncHook (GL2ES2 gl, PMVMatrix matrix) { + /** + * @param gl + * @param mode TODO + * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} + */ + public FixedFuncHook(GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix, + Class<?> shaderRootClass, String shaderSrcRoot, String shaderBinRoot, + String vertexColorFile, String vertexColorLightFile, + String fragmentColorFile, String fragmentColorTextureFile) { this.gl = gl; - pmvMatrix = (null!=matrix)?matrix:new PMVMatrix(); + if(null != pmvMatrix) { + this.ownsPMVMatrix = false; + this.pmvMatrix = pmvMatrix; + } else { + this.ownsPMVMatrix = true; + this.pmvMatrix = new PMVMatrix(); + } - fixedFunction = new FixedFuncPipeline(gl, pmvMatrix); + fixedFunction = new FixedFuncPipeline(this.gl, mode, this.pmvMatrix, shaderRootClass, shaderSrcRoot, + shaderBinRoot, vertexColorFile, vertexColorLightFile, fragmentColorFile, fragmentColorTextureFile); } - public FixedFuncHook(GL2ES2 gl, PMVMatrix matrix, - Class<?> shaderRootClass, String shaderSrcRoot, String shaderBinRoot, - String vertexColorFile, - String vertexColorLightFile, - String fragmentColorFile, - String fragmentColorTextureFile) { - this.gl = gl; - pmvMatrix = matrix; + public boolean verbose() { return fixedFunction.verbose(); } - fixedFunction = new FixedFuncPipeline(gl, pmvMatrix, - shaderRootClass, shaderSrcRoot, shaderBinRoot, - vertexColorFile, vertexColorLightFile, fragmentColorFile, fragmentColorTextureFile); - } + public void setVerbose(boolean v) { fixedFunction.setVerbose(v); } public void destroy() { fixedFunction.destroy(gl); fixedFunction = null; + if(ownsPMVMatrix) { + pmvMatrix.destroy(); + } + pmvMatrix=null; + gl=null; } public PMVMatrix getMatrix() { return pmvMatrix; } // - // FixedFuncHookIf - hooks + // FixedFuncHookIf - hooks // public void glDrawArrays(int mode, int first, int count) { - fixedFunction.validate(gl); - gl.glDrawArrays(mode, first, count); + fixedFunction.glDrawArrays(gl, mode, first, count); } public void glDrawElements(int mode, int count, int type, java.nio.Buffer indices) { - fixedFunction.validate(gl); - gl.glDrawElements(mode, count, type, indices); + fixedFunction.glDrawElements(gl, mode, count, type, indices); } public void glDrawElements(int mode, int count, int type, long indices_buffer_offset) { - fixedFunction.validate(gl); - gl.glDrawElements(mode, count, type, indices_buffer_offset); + fixedFunction.glDrawElements(gl, mode, count, type, indices_buffer_offset); } public void glActiveTexture(int texture) { - fixedFunction.glActiveTexture(gl, texture); + fixedFunction.glActiveTexture(texture); gl.glActiveTexture(texture); } public void glEnable(int cap) { - if(fixedFunction.glEnable(gl, cap, true)) { + if(fixedFunction.glEnable(cap, true)) { gl.glEnable(cap); } } public void glDisable(int cap) { - if(fixedFunction.glEnable(gl, cap, false)) { + if(fixedFunction.glEnable(cap, false)) { gl.glDisable(cap); } } - public void glCullFace(int faceName) { - fixedFunction.glCullFace(gl, faceName); - gl.glCullFace(faceName); - } - + @Override public void glGetFloatv(int pname, java.nio.FloatBuffer params) { if(PMVMatrix.isMatrixGetName(pname)) { pmvMatrix.glGetFloatv(pname, params); @@ -117,6 +145,7 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun } gl.glGetFloatv(pname, params); } + @Override public void glGetFloatv(int pname, float[] params, int params_offset) { if(PMVMatrix.isMatrixGetName(pname)) { pmvMatrix.glGetFloatv(pname, params, params_offset); @@ -124,6 +153,7 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun } gl.glGetFloatv(pname, params, params_offset); } + @Override public void glGetIntegerv(int pname, IntBuffer params) { if(PMVMatrix.isMatrixGetName(pname)) { pmvMatrix.glGetIntegerv(pname, params); @@ -131,6 +161,7 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun } gl.glGetIntegerv(pname, params); } + @Override public void glGetIntegerv(int pname, int[] params, int params_offset) { if(PMVMatrix.isMatrixGetName(pname)) { pmvMatrix.glGetIntegerv(pname, params, params_offset); @@ -139,95 +170,193 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun gl.glGetIntegerv(pname, params, params_offset); } - // + public void glTexEnvi(int target, int pname, int value) { + fixedFunction.glTexEnvi(target, pname, value); + } + public void glGetTexEnviv(int target, int pname, IntBuffer params) { + fixedFunction.glGetTexEnviv(target, pname, params); + } + public void glGetTexEnviv(int target, int pname, int[] params, int params_offset) { + fixedFunction.glGetTexEnviv(target, pname, params, params_offset); + } + public void glBindTexture(int target, int texture) { + fixedFunction.glBindTexture(target, texture); + gl.glBindTexture(target, texture); + } + public void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, + int format, int type, Buffer pixels) { + // align internalformat w/ format, an ES2 requirement + switch(internalformat) { + case 3: internalformat= ( GL.GL_RGBA == format ) ? GL.GL_RGBA : GL.GL_RGB; break; + case 4: internalformat= ( GL.GL_RGB == format ) ? GL.GL_RGB : GL.GL_RGBA; break; + } + fixedFunction.glTexImage2D(target, /* level, */ internalformat, /*width, height, border, */ format /*, type, pixels*/); + gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + } + public void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, + int format, int type, long pixels_buffer_offset) { + // align internalformat w/ format, an ES2 requirement + switch(internalformat) { + case 3: internalformat= ( GL.GL_RGBA == format ) ? GL.GL_RGBA : GL.GL_RGB; break; + case 4: internalformat= ( GL.GL_RGB == format ) ? GL.GL_RGB : GL.GL_RGBA; break; + } + fixedFunction.glTexImage2D(target, /* level, */ internalformat, /*width, height, border, */ format /*, type, pixels*/); + gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels_buffer_offset); + } + + public void glPointSize(float size) { + fixedFunction.glPointSize(size); + } + public void glPointParameterf(int pname, float param) { + fixedFunction.glPointParameterf(pname, param); + } + public void glPointParameterfv(int pname, float[] params, int params_offset) { + fixedFunction.glPointParameterfv(pname, params, params_offset); + } + public void glPointParameterfv(int pname, java.nio.FloatBuffer params) { + fixedFunction.glPointParameterfv(pname, params); + } + + // // MatrixIf // public int glGetMatrixMode() { return pmvMatrix.glGetMatrixMode(); } + @Override public void glMatrixMode(int mode) { pmvMatrix.glMatrixMode(mode); } + @Override public void glLoadMatrixf(java.nio.FloatBuffer m) { pmvMatrix.glLoadMatrixf(m); } + @Override public void glLoadMatrixf(float[] m, int m_offset) { glLoadMatrixf(GLBuffers.newDirectFloatBuffer(m, m_offset)); } + @Override public void glPopMatrix() { pmvMatrix.glPopMatrix(); } + @Override public void glPushMatrix() { pmvMatrix.glPushMatrix(); } + @Override public void glLoadIdentity() { pmvMatrix.glLoadIdentity(); } + @Override public void glMultMatrixf(java.nio.FloatBuffer m) { pmvMatrix.glMultMatrixf(m); } + @Override public void glMultMatrixf(float[] m, int m_offset) { glMultMatrixf(GLBuffers.newDirectFloatBuffer(m, m_offset)); } + @Override public void glTranslatef(float x, float y, float z) { pmvMatrix.glTranslatef(x, y, z); } + @Override public void glRotatef(float angdeg, float x, float y, float z) { pmvMatrix.glRotatef(angdeg, x, y, z); } + @Override public void glScalef(float x, float y, float z) { pmvMatrix.glScalef(x, y, z); } + public void glOrtho(double left, double right, double bottom, double top, double near_val, double far_val) { + glOrthof((float) left, (float) right, (float) bottom, (float) top, (float) near_val, (float) far_val); + } + @Override public void glOrthof(float left, float right, float bottom, float top, float zNear, float zFar) { pmvMatrix.glOrthof(left, right, bottom, top, zNear, zFar); } + public void glFrustum(double left, double right, double bottom, double top, double zNear, double zFar) { + glFrustumf((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar); + } + @Override public void glFrustumf(float left, float right, float bottom, float top, float zNear, float zFar) { pmvMatrix.glFrustumf(left, right, bottom, top, zNear, zFar); } - // + // // LightingIf // + @Override public void glColor4f(float red, float green, float blue, float alpha) { - fixedFunction.glColor4fv(gl, GLBuffers.newDirectFloatBuffer(new float[] { red, green, blue, alpha })); + fixedFunction.glColor4f(gl, red, green, blue, alpha); } + public void glColor4ub(byte red, byte green, byte blue, byte alpha) { + glColor4f(ValueConv.byte_to_float(red, false), + ValueConv.byte_to_float(green, false), + ValueConv.byte_to_float(blue, false), + ValueConv.byte_to_float(alpha, false) ); + } + @Override public void glLightfv(int light, int pname, java.nio.FloatBuffer params) { fixedFunction.glLightfv(gl, light, pname, params); } + @Override public void glLightfv(int light, int pname, float[] params, int params_offset) { glLightfv(light, pname, GLBuffers.newDirectFloatBuffer(params, params_offset)); } + @Override public void glMaterialfv(int face, int pname, java.nio.FloatBuffer params) { fixedFunction.glMaterialfv(gl, face, pname, params); } + @Override public void glMaterialfv(int face, int pname, float[] params, int params_offset) { glMaterialfv(face, pname, GLBuffers.newDirectFloatBuffer(params, params_offset)); } + @Override public void glMaterialf(int face, int pname, float param) { glMaterialfv(face, pname, GLBuffers.newDirectFloatBuffer(new float[] { param })); } + + // + // Misc Simple States + // + @Override public void glShadeModel(int mode) { fixedFunction.glShadeModel(gl, mode); } + public void glAlphaFunc(int func, float ref) { + fixedFunction.glAlphaFunc(func, ref); + } + + /** ES2 supports CullFace implicit + public void glCullFace(int faceName) { + fixedFunction.glCullFace(faceName); + gl.glCullFace(faceName); + } */ // // PointerIf // + public void glClientActiveTexture(int textureUnit) { + fixedFunction.glClientActiveTexture(textureUnit); + } + @Override public void glEnableClientState(int glArrayIndex) { fixedFunction.glEnableClientState(gl, glArrayIndex); } + @Override public void glDisableClientState(int glArrayIndex) { fixedFunction.glDisableClientState(gl, glArrayIndex); } + @Override public void glVertexPointer(GLArrayData array) { if(array.isVBO()) { - if(!gl.glIsVBOArrayEnabled()) { + if(!gl.isVBOArrayBound()) { throw new GLException("VBO array is not enabled: "+array); } } else { - if(gl.glIsVBOArrayEnabled()) { + if(gl.isVBOArrayBound()) { throw new GLException("VBO array is not disabled: "+array); } Buffers.rangeCheck(array.getBuffer(), 1); @@ -237,25 +366,29 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun fixedFunction.glVertexPointer(gl, array); } + @Override public void glVertexPointer(int size, int type, int stride, java.nio.Buffer pointer) { - glVertexPointer(GLArrayDataWrapper.createFixed(GL_VERTEX_ARRAY, size, type, false, stride, pointer, 0, 0, 0, GL.GL_ARRAY_BUFFER)); + glVertexPointer(GLArrayDataWrapper.createFixed(GL_VERTEX_ARRAY, size, type, GLBuffers.isGLTypeFixedPoint(type), stride, + pointer, 0, 0, 0, GL.GL_ARRAY_BUFFER)); } + @Override public void glVertexPointer(int size, int type, int stride, long pointer_buffer_offset) { - int vboName = gl.glGetBoundBuffer(GL.GL_ARRAY_BUFFER); + int vboName = gl.getBoundBuffer(GL.GL_ARRAY_BUFFER); if(vboName==0) { throw new GLException("no GL_ARRAY_BUFFER VBO bound"); } - glVertexPointer(GLArrayDataWrapper.createFixed(GL_VERTEX_ARRAY, size, type, false, stride, + glVertexPointer(GLArrayDataWrapper.createFixed(GL_VERTEX_ARRAY, size, type, GLBuffers.isGLTypeFixedPoint(type), stride, null, vboName, pointer_buffer_offset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)); } + @Override public void glColorPointer(GLArrayData array) { if(array.isVBO()) { - if(!gl.glIsVBOArrayEnabled()) { + if(!gl.isVBOArrayBound()) { throw new GLException("VBO array is not enabled: "+array); } } else { - if(gl.glIsVBOArrayEnabled()) { + if(gl.isVBOArrayBound()) { throw new GLException("VBO array is not disabled: "+array); } Buffers.rangeCheck(array.getBuffer(), 1); @@ -264,29 +397,32 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun } fixedFunction.glColorPointer(gl, array); } + @Override public void glColorPointer(int size, int type, int stride, java.nio.Buffer pointer) { - glColorPointer(GLArrayDataWrapper.createFixed(GL_COLOR_ARRAY, size, type, false, stride, + glColorPointer(GLArrayDataWrapper.createFixed(GL_COLOR_ARRAY, size, type, GLBuffers.isGLTypeFixedPoint(type), stride, pointer, 0, 0, 0, GL.GL_ARRAY_BUFFER)); } + @Override public void glColorPointer(int size, int type, int stride, long pointer_buffer_offset) { - int vboName = gl.glGetBoundBuffer(GL.GL_ARRAY_BUFFER); + int vboName = gl.getBoundBuffer(GL.GL_ARRAY_BUFFER); if(vboName==0) { throw new GLException("no GL_ARRAY_BUFFER VBO bound"); } - glColorPointer(GLArrayDataWrapper.createFixed(GL_COLOR_ARRAY, size, type, false, stride, + glColorPointer(GLArrayDataWrapper.createFixed(GL_COLOR_ARRAY, size, type, GLBuffers.isGLTypeFixedPoint(type), stride, null, vboName, pointer_buffer_offset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)); } + @Override public void glNormalPointer(GLArrayData array) { if(array.getComponentCount()!=3) { throw new GLException("Only 3 components per normal allowed"); } if(array.isVBO()) { - if(!gl.glIsVBOArrayEnabled()) { + if(!gl.isVBOArrayBound()) { throw new GLException("VBO array is not enabled: "+array); } } else { - if(gl.glIsVBOArrayEnabled()) { + if(gl.isVBOArrayBound()) { throw new GLException("VBO array is not disabled: "+array); } Buffers.rangeCheck(array.getBuffer(), 1); @@ -295,26 +431,29 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun } fixedFunction.glNormalPointer(gl, array); } + @Override public void glNormalPointer(int type, int stride, java.nio.Buffer pointer) { - glNormalPointer(GLArrayDataWrapper.createFixed(GL_NORMAL_ARRAY, 3, type, false, stride, + glNormalPointer(GLArrayDataWrapper.createFixed(GL_NORMAL_ARRAY, 3, type, GLBuffers.isGLTypeFixedPoint(type), stride, pointer, 0, 0, 0, GL.GL_ARRAY_BUFFER)); } + @Override public void glNormalPointer(int type, int stride, long pointer_buffer_offset) { - int vboName = gl.glGetBoundBuffer(GL.GL_ARRAY_BUFFER); + int vboName = gl.getBoundBuffer(GL.GL_ARRAY_BUFFER); if(vboName==0) { throw new GLException("no GL_ARRAY_BUFFER VBO bound"); } - glNormalPointer(GLArrayDataWrapper.createFixed(GL_NORMAL_ARRAY, 3, type, false, stride, + glNormalPointer(GLArrayDataWrapper.createFixed(GL_NORMAL_ARRAY, 3, type, GLBuffers.isGLTypeFixedPoint(type), stride, null, vboName, pointer_buffer_offset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER)); } + @Override public void glTexCoordPointer(GLArrayData array) { if(array.isVBO()) { - if(!gl.glIsVBOArrayEnabled()) { + if(!gl.isVBOArrayBound()) { throw new GLException("VBO array is not enabled: "+array); } } else { - if(gl.glIsVBOArrayEnabled()) { + if(gl.isVBOArrayBound()) { throw new GLException("VBO array is not disabled: "+array); } Buffers.rangeCheck(array.getBuffer(), 1); @@ -323,25 +462,29 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun } fixedFunction.glTexCoordPointer(gl, array); } + @Override public void glTexCoordPointer(int size, int type, int stride, java.nio.Buffer pointer) { glTexCoordPointer( - GLArrayDataWrapper.createFixed(GL_TEXTURE_COORD_ARRAY, size, type, false, stride, pointer, 0, 0, 0, GL.GL_ARRAY_BUFFER)); + GLArrayDataWrapper.createFixed(GL_TEXTURE_COORD_ARRAY, size, type, GLBuffers.isGLTypeFixedPoint(type), stride, + pointer, 0, 0, 0, GL.GL_ARRAY_BUFFER)); } + @Override public void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset) { - int vboName = gl.glGetBoundBuffer(GL.GL_ARRAY_BUFFER); + int vboName = gl.getBoundBuffer(GL.GL_ARRAY_BUFFER); if(vboName==0) { throw new GLException("no GL_ARRAY_BUFFER VBO bound"); } glTexCoordPointer( - GLArrayDataWrapper.createFixed(GL_TEXTURE_COORD_ARRAY, size, type, false, stride, + GLArrayDataWrapper.createFixed(GL_TEXTURE_COORD_ARRAY, size, type, GLBuffers.isGLTypeFixedPoint(type), stride, null, vboName, pointer_buffer_offset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) ); } + @Override public final String toString() { StringBuilder buf = new StringBuilder(); buf.append(getClass().getName()+" ("); if(null!=pmvMatrix) { - buf.append(", matrixDirty: "+pmvMatrix.isDirty()); + buf.append(", matrixDirty: "+ (0 != pmvMatrix.getModifiedBits(false))); } buf.append("\n\t, FixedFunction: "+fixedFunction); buf.append(gl); 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 bfe2e13c2..42269588d 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncPipeline.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncPipeline.java @@ -29,33 +29,87 @@ package jogamp.opengl.util.glsl.fixedfunc; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLArrayData; +import javax.media.opengl.GLES2; +import javax.media.opengl.GLException; +import javax.media.opengl.GLRunnable2; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLLightingFunc; +import javax.media.opengl.fixedfunc.GLPointerFunc; +import javax.media.opengl.fixedfunc.GLPointerFuncUtil; + +import jogamp.opengl.Debug; + import com.jogamp.common.nio.Buffers; -import javax.media.opengl.*; -import javax.media.opengl.fixedfunc.*; -import com.jogamp.opengl.util.*; -import com.jogamp.opengl.util.glsl.*; -import java.nio.*; +import com.jogamp.common.util.IntIntHashMap; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; +import com.jogamp.opengl.util.glsl.ShaderState; +import com.jogamp.opengl.util.glsl.fixedfunc.ShaderSelectionMode; +/** + * + * <p> + * Note: Certain GL FFP state values (e.g.: alphaTestFunc and cullFace) + * are mapped to a lower number range so they can be stored in low precision storage, + * i.e. in a 'lowp int' (GL ES2). + * </p> + */ public class FixedFuncPipeline { + 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; - public FixedFuncPipeline(GL2ES2 gl, PMVMatrix pmvMatrix) { - init(gl, pmvMatrix, FixedFuncPipeline.class, shaderSrcRootDef, shaderBinRootDef, - vertexColorFileDef, vertexColorLightFileDef, fragmentColorFileDef, fragmentColorTextureFileDef); + public FixedFuncPipeline(GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix) { + shaderRootClass = FixedFuncPipeline.class; + shaderSrcRoot = shaderSrcRootDef; + shaderBinRoot = shaderBinRootDef; + vertexColorFile = vertexColorFileDef; + vertexColorLightFile = vertexColorLightFileDef; + fragmentColorFile = fragmentColorFileDef; + fragmentColorTextureFile = fragmentColorTextureFileDef; + init(gl, mode, pmvMatrix); } - public FixedFuncPipeline(GL2ES2 gl, PMVMatrix pmvMatrix, Class shaderRootClass, String shaderSrcRoot, String shaderBinRoot, - String vertexColorFile, - String vertexColorLightFile, - String fragmentColorFile, - String fragmentColorTextureFile) { - init(gl, pmvMatrix, shaderRootClass, shaderSrcRoot, shaderBinRoot, - vertexColorFile, vertexColorLightFile, fragmentColorFile, fragmentColorTextureFile); + public FixedFuncPipeline(GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix, + Class<?> shaderRootClass, String shaderSrcRoot, + String shaderBinRoot, + String vertexColorFile, String vertexColorLightFile, + String fragmentColorFile, String fragmentColorTextureFile) { + this.shaderRootClass = shaderRootClass; + this.shaderSrcRoot = shaderSrcRoot; + this.shaderBinRoot = shaderBinRoot; + this.vertexColorFile = vertexColorFile; + this.vertexColorLightFile = vertexColorLightFile; + this.fragmentColorFile = fragmentColorFile; + this.fragmentColorTextureFile = fragmentColorTextureFile; + init(gl, mode, pmvMatrix); } + public ShaderSelectionMode getShaderSelectionMode() { return requestedShaderSelectionMode; } + public void setShaderSelectionMode(ShaderSelectionMode mode) { requestedShaderSelectionMode=mode; } + public ShaderSelectionMode getCurrentShaderSelectionMode() { return currentShaderSelectionMode; } + public boolean verbose() { return verbose; } - public void setVerbose(boolean v) { verbose=v; } + public void setVerbose(boolean v) { verbose = DEBUG || v; } public boolean isValid() { return shaderState.linked(); @@ -69,46 +123,83 @@ public class FixedFuncPipeline { return activeTextureUnit; } - public String getArrayIndexName(int glArrayIndex) { - String name = GLPointerFuncUtil.getPredefinedArrayIndexName(glArrayIndex); - switch(glArrayIndex) { - case GLPointerFunc.GL_VERTEX_ARRAY: - case GLPointerFunc.GL_NORMAL_ARRAY: - case GLPointerFunc.GL_COLOR_ARRAY: - break; - case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: - name = name + activeTextureUnit; - } - return name; - } - public void destroy(GL2ES2 gl) { - shaderProgramColor.release(gl, true); - shaderProgramColorLight.release(gl, true); - shaderProgramColorTexture.release(gl, true); - shaderProgramColorTextureLight.release(gl, true); + if(null != shaderProgramColor) { + shaderProgramColor.release(gl, true); + } + if(null != shaderProgramColorLight) { + shaderProgramColorLight.release(gl, true); + } + if(null != shaderProgramColorTexture2) { + shaderProgramColorTexture2.release(gl, true); + } + if(null != shaderProgramColorTexture4) { + shaderProgramColorTexture4.release(gl, true); + } + if(null != shaderProgramColorTexture4) { + shaderProgramColorTexture4.release(gl, true); + } + if(null != shaderProgramColorTexture8Light) { + shaderProgramColorTexture8Light.release(gl, true); + } shaderState.destroy(gl); } - public void glEnableClientState(GL2ES2 gl, int glArrayIndex) { - shaderState.useProgram(gl, true); + // + // Simple Globals + // + public void glColor4f(GL2ES2 gl, float red, float green, float blue, float alpha) { + colorStatic.put(0, red); + colorStatic.put(1, green); + colorStatic.put(2, blue); + colorStatic.put(3, alpha); - shaderState.enableVertexAttribArray(gl, getArrayIndexName(glArrayIndex)); - // textureCoordsEnabled |= (1 << activeTextureUnit); - if ( textureCoordsEnabled.get(activeTextureUnit) != 1 ) { - textureCoordsEnabled.put(activeTextureUnit, 1); - textureCoordsEnabledDirty = true; + shaderState.useProgram(gl, true); + final GLUniformData ud = shaderState.getUniform(mgl_ColorStatic); + if(null!=ud) { + // same data object .. + shaderState.uniform(gl, ud); + } else { + throw new GLException("Failed to update: mgl_ColorStatic"); } } + // + // Arrays / States + // + + public void glEnableClientState(GL2ES2 gl, int glArrayIndex) { + glToggleClientState(gl, glArrayIndex, true); + } + public void glDisableClientState(GL2ES2 gl, int glArrayIndex) { - shaderState.useProgram(gl, true); + glToggleClientState(gl, glArrayIndex, false); + } - shaderState.disableVertexAttribArray(gl, getArrayIndexName(glArrayIndex)); - // textureCoordsEnabled &= ~(1 << activeTextureUnit); - if ( textureCoordsEnabled.get(activeTextureUnit) != 0 ) { - textureCoordsEnabled.put(activeTextureUnit, 0); - textureCoordsEnabledDirty = true; + private void glToggleClientState(GL2ES2 gl, int glArrayIndex, boolean enable) { + final String arrayName = GLPointerFuncUtil.getPredefinedArrayIndexName(glArrayIndex, clientActiveTextureUnit); + if(null == arrayName) { + throw new GLException("arrayIndex "+toHexString(glArrayIndex)+" unknown"); + } + shaderState.useProgram(gl, true); + if(enable) { + shaderState.enableVertexAttribArray(gl, arrayName ); + } else { + shaderState.disableVertexAttribArray(gl, arrayName ); + } + switch( glArrayIndex ) { + case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: + final int enableV = enable ? 1 : 0; + // enable-bitwise: textureCoordsEnabled |= (1 << clientActiveTextureUnit); + // disable-bitwise: textureCoordsEnabled &= ~(1 << clientActiveTextureUnit); + if ( textureCoordEnabled.get(clientActiveTextureUnit) != enableV) { + textureCoordEnabled.put(clientActiveTextureUnit, enableV); + textureCoordEnabledDirty = true; + } + break; + case GLPointerFunc.GL_COLOR_ARRAY: + colorVAEnabledDirty = true; + break; } } @@ -122,26 +213,235 @@ public class FixedFuncPipeline { shaderState.vertexAttribPointer(gl, data); } - public void glColor4fv(GL2ES2 gl, FloatBuffer data ) { + public void glNormalPointer(GL2ES2 gl, GLArrayData data) { shaderState.useProgram(gl, true); - GLUniformData ud = shaderState.getUniform(mgl_ColorStatic); - if(null!=ud) { - ud.setData(data); - shaderState.uniform(gl, ud); + shaderState.vertexAttribPointer(gl, data); + } + + // + // MULTI-TEXTURE + // + + /** Enables/Disables the named texture unit (if changed), returns previous state */ + private boolean glEnableTexture(boolean enable, int unit) { + final boolean isEnabled = 0 != ( textureEnabledBits & ( 1 << activeTextureUnit ) ); + if( isEnabled != enable ) { + if(enable) { + textureEnabledBits |= ( 1 << unit ); + textureEnabled.put(unit, 1); + } else { + textureEnabledBits &= ~( 1 << unit ); + textureEnabled.put(unit, 0); + } + textureEnabledDirty=true; } + return isEnabled; } - public void glNormalPointer(GL2ES2 gl, GLArrayData data) { - shaderState.useProgram(gl, true); - shaderState.vertexAttribPointer(gl, data); + public void glClientActiveTexture(int textureUnit) { + textureUnit -= GL.GL_TEXTURE0; + if(0 <= textureUnit && textureUnit<MAX_TEXTURE_UNITS) { + clientActiveTextureUnit = textureUnit; + } else { + throw new GLException("glClientActiveTexture textureUnit not within GL_TEXTURE0 + [0.."+MAX_TEXTURE_UNITS+"]: "+textureUnit); + } + } + + public void glActiveTexture(int textureUnit) { + textureUnit -= GL.GL_TEXTURE0; + if(0 <= textureUnit && textureUnit<MAX_TEXTURE_UNITS) { + activeTextureUnit = textureUnit; + } else { + throw new GLException("glActivateTexture textureUnit not within GL_TEXTURE0 + [0.."+MAX_TEXTURE_UNITS+"]: "+textureUnit); + } } public void glTexCoordPointer(GL2ES2 gl, GLArrayData data) { + if( GLPointerFunc.GL_TEXTURE_COORD_ARRAY != data.getIndex() ) { + throw new GLException("Invalid GLArrayData Index "+toHexString(data.getIndex())+", "+data); + } shaderState.useProgram(gl, true); - data.setName( getArrayIndexName(data.getIndex()) ); + data.setName( GLPointerFuncUtil.getPredefinedArrayIndexName(data.getIndex(), clientActiveTextureUnit) ) ; shaderState.vertexAttribPointer(gl, data); } + public void glBindTexture(int target, int texture) { + if(GL.GL_TEXTURE_2D == target) { + if( texture != boundTextureObject[activeTextureUnit] ) { + boundTextureObject[activeTextureUnit] = texture; + textureFormatDirty = true; + } + } else { + System.err.println("FixedFuncPipeline: Unimplemented glBindTexture for target "+toHexString(target)+". Texture name "+toHexString(texture)); + } + } + + public void glTexImage2D(int target, /* int level, */ int internalformat, /*, int width, int height, int border, */ + int format /*, int type, Buffer pixels */) { + final int ifmt; + if(GL.GL_TEXTURE_2D == target) { + switch(internalformat) { + case 3: + case GL.GL_RGB: + case GL.GL_RGB565: + case GL.GL_RGB8: + case GL.GL_RGB10: + ifmt = 3; + break; + case 4: + case GL.GL_RGBA: + case GL.GL_RGB5_A1: + case GL.GL_RGBA4: + case GL.GL_RGBA8: + case GL.GL_RGB10_A2: + ifmt = 4; + break; + default: + System.err.println("FixedFuncPipeline: glTexImage2D TEXTURE_2D: Unimplemented internalformat "+toHexString(internalformat)); + ifmt = 4; + break; + } + if( ifmt != texID2Format.put(boundTextureObject[activeTextureUnit], ifmt) ) { + textureFormatDirty = true; + // System.err.println("glTexImage2D TEXTURE_2D: internalformat ifmt "+toHexString(internalformat)+" fmt "+toHexString(format)+" -> "+toHexString(ifmt)); + } + } else { + System.err.println("FixedFuncPipeline: Unimplemented glTexImage2D: target "+toHexString(target)+", internalformat "+toHexString(internalformat)); + } + } + /* + public void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, + int format, int type, long pixels_buffer_offset) { + textureFormat.put(activeTextureUnit, internalformat); + textureFormatDirty = true; + }*/ + + public void glTexEnvi(int target, int pname, int value) { + if(GL2ES1.GL_TEXTURE_ENV == target && GL2ES1.GL_TEXTURE_ENV_MODE == pname) { + final int mode; + switch( value ) { + case GL2ES1.GL_ADD: + mode = 1; + break; + case GL2ES1.GL_MODULATE: + mode = 2; + break; + case GL2ES1.GL_DECAL: + mode = 3; + break; + case GL2ES1.GL_BLEND: + mode = 4; + break; + case GL2ES1.GL_REPLACE: + mode = 5; + break; + case GL2ES1.GL_COMBINE: + mode = 2; // FIXME + System.err.println("FixedFuncPipeline: glTexEnv GL_TEXTURE_ENV_MODE: unimplemented mode: "+toHexString(value)); + break; + default: + throw new GLException("glTexEnv GL_TEXTURE_ENV_MODE: invalid mode: "+toHexString(value)); + } + setTextureEnvMode(mode); + } else if(verbose) { + System.err.println("FixedFuncPipeline: Unimplemented TexEnv: target "+toHexString(target)+", pname "+toHexString(pname)+", mode: "+toHexString(value)); + } + } + private void setTextureEnvMode(int value) { + if( value != textureEnvMode.get(activeTextureUnit) ) { + textureEnvMode.put(activeTextureUnit, value); + textureEnvModeDirty = true; + } + } + public void glGetTexEnviv(int target, int pname, IntBuffer params) { // FIXME + System.err.println("FixedFuncPipeline: Unimplemented glGetTexEnviv: target "+toHexString(target)+", pname "+toHexString(pname)); + } + public void glGetTexEnviv(int target, int pname, int[] params, int params_offset) { // FIXME + System.err.println("FixedFuncPipeline: Unimplemented glGetTexEnviv: target "+toHexString(target)+", pname "+toHexString(pname)); + } + + // + // Point Sprites + // + public void glPointSize(float size) { + pointParams.put(0, size); + pointParamsDirty = true; + } + public void glPointParameterf(int pname, float param) { + switch(pname) { + case GL2ES1.GL_POINT_SIZE_MIN: + pointParams.put(2, param); + break; + case GL2ES1.GL_POINT_SIZE_MAX: + pointParams.put(3, param); + break; + case GL2ES2.GL_POINT_FADE_THRESHOLD_SIZE: + pointParams.put(4+3, param); + break; + } + pointParamsDirty = true; + } + public void glPointParameterfv(int pname, float[] params, int params_offset) { + switch(pname) { + case GL2ES1.GL_POINT_DISTANCE_ATTENUATION: + pointParams.put(4+0, params[params_offset + 0]); + pointParams.put(4+1, params[params_offset + 1]); + pointParams.put(4+2, params[params_offset + 2]); + break; + } + pointParamsDirty = true; + } + public void glPointParameterfv(int pname, java.nio.FloatBuffer params) { + final int o = params.position(); + switch(pname) { + case GL2ES1.GL_POINT_DISTANCE_ATTENUATION: + pointParams.put(4+0, params.get(o + 0)); + pointParams.put(4+1, params.get(o + 1)); + pointParams.put(4+2, params.get(o + 2)); + break; + } + pointParamsDirty = true; + } + + // private int[] pointTexObj = new int[] { 0 }; + + private void glDrawPoints(GL2ES2 gl, GLRunnable2<Object,Object> glDrawAction, Object args) { + if(gl.isGL2GL3()) { + gl.glEnable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); + } + if(gl.isGL2ES1()) { + gl.glEnable(GL2ES1.GL_POINT_SPRITE); + } + loadShaderPoints(gl); + shaderState.attachShaderProgram(gl, shaderProgramPoints, true); + validate(gl, false); // sync uniforms + + glDrawAction.run(gl, args); + + if(gl.isGL2ES1()) { + gl.glDisable(GL2ES1.GL_POINT_SPRITE); + } + if(gl.isGL2GL3()) { + gl.glDisable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); + } + shaderState.attachShaderProgram(gl, selectShaderProgram(gl, currentShaderSelectionMode), true); + } + private static final GLRunnable2<Object, Object> glDrawArraysAction = new GLRunnable2<Object,Object>() { + @Override + public Object run(GL gl, Object args) { + int[] _args = (int[])args; + gl.glDrawArrays(GL.GL_POINTS, _args[0], _args[1]); + return null; + } + }; + private final void glDrawPointArrays(GL2ES2 gl, int first, int count) { + glDrawPoints(gl, glDrawArraysAction, new int[] { first, count }); + } + + // + // Lighting + // + public void glLightfv(GL2ES2 gl, int light, int pname, java.nio.FloatBuffer params) { shaderState.useProgram(gl, true); light -=GLLightingFunc.GL_LIGHT0; @@ -179,17 +479,14 @@ public class FixedFuncPipeline { ud = shaderState.getUniform(mgl_LightSource+"["+light+"].quadraticAttenuation"); break; default: - if(verbose) { - System.err.println("glLightfv pname not within [GL_AMBIENT GL_DIFFUSE GL_SPECULAR GL_POSITION GL_SPOT_DIRECTION]: "+pname); - } - return; + throw new GLException("glLightfv invalid pname: "+toHexString(pname)); } if(null!=ud) { ud.setData(params); shaderState.uniform(gl, ud); } - } else if(verbose) { - System.err.println("glLightfv light not within [0.."+MAX_LIGHTS+"]: "+light); + } else { + throw new GLException("glLightfv light not within [0.."+MAX_LIGHTS+"]: "+light); } } @@ -201,10 +498,8 @@ public class FixedFuncPipeline { case GL.GL_FRONT_AND_BACK: break; case GL.GL_BACK: - if(verbose) { - System.err.println("glMaterialfv face GL_BACK currently not supported"); - } - break; + System.err.println("FixedFuncPipeline: Unimplemented glMaterialfv GL_BACK face"); + return; default: } @@ -214,7 +509,13 @@ public class FixedFuncPipeline { ud = shaderState.getUniform(mgl_FrontMaterial+".ambient"); break; case GLLightingFunc.GL_AMBIENT_AND_DIFFUSE: - glMaterialfv(gl, face, GLLightingFunc.GL_AMBIENT, params); + { + ud = shaderState.getUniform(mgl_FrontMaterial+".ambient"); + if(null!=ud) { + ud.setData(params); + shaderState.uniform(gl, ud); + } + } // fall through intended .. case GLLightingFunc.GL_DIFFUSE: ud = shaderState.getUniform(mgl_FrontMaterial+".diffuse"); @@ -229,17 +530,20 @@ public class FixedFuncPipeline { ud = shaderState.getUniform(mgl_FrontMaterial+".shininess"); break; default: - if(verbose) { - System.err.println("glMaterialfv pname not within [GL_AMBIENT GL_DIFFUSE GL_SPECULAR GL_EMISSION GL_SHININESS]: "+pname); - } - return; + throw new GLException("glMaterialfv invalid pname: "+toHexString(pname)); } if(null!=ud) { ud.setData(params); shaderState.uniform(gl, ud); + } else if(verbose) { + } } + // + // Misc States + // + public void glShadeModel(GL2ES2 gl, int mode) { shaderState.useProgram(gl, true); GLUniformData ud = shaderState.getUniform(mgl_ShadeModel); @@ -249,46 +553,135 @@ public class FixedFuncPipeline { } } - public void glActiveTexture(GL2ES2 gl, int textureUnit) { - textureUnit -= GL.GL_TEXTURE0; - if(0 <= textureUnit && textureUnit<MAX_TEXTURE_UNITS) { - shaderState.useProgram(gl, true); - GLUniformData ud; - ud = shaderState.getUniform(mgl_ActiveTexture); - if(null!=ud) { - ud.setData(textureUnit); - shaderState.uniform(gl, ud); + /** ES2 supports CullFace implicit + public void glCullFace(int faceName) { + int _cullFace; + switch(faceName) { + case GL.GL_FRONT: + _cullFace = 1; + break; + case GL.GL_BACK: + _cullFace = 2; + break; + case GL.GL_FRONT_AND_BACK: + _cullFace = 3; + break; + default: + throw new GLException("glCullFace invalid faceName: "+toHexString(faceName)); + } + if(0 < _cullFace) { + if(0>cullFace) { + _cullFace *= -1; } - ud = shaderState.getUniform(mgl_ActiveTextureIdx); - if(null!=ud) { - ud.setData(textureUnit); - shaderState.uniform(gl, ud); + if(cullFace != _cullFace) { + cullFace = _cullFace; + cullFaceDirty=true; + } + } + } */ + + public void glAlphaFunc(int func, float ref) { + int _func; + switch(func) { + case GL.GL_NEVER: + _func = 1; + break; + case GL.GL_LESS: + _func = 2; + break; + case GL.GL_EQUAL: + _func = 3; + break; + case GL.GL_LEQUAL: + _func = 4; + break; + case GL.GL_GREATER: + _func = 5; + break; + case GL.GL_NOTEQUAL: + _func = 6; + break; + case GL.GL_GEQUAL: + _func = 7; + break; + case GL.GL_ALWAYS: + _func = 8; + break; + default: + throw new GLException("glAlphaFunc invalid func: "+toHexString(func)); + } + if(0 < _func) { + if(0>alphaTestFunc) { + _func *= -1; + } + if( alphaTestFunc != _func || alphaTestRef != ref ) { + alphaTestFunc = _func; + alphaTestRef = ref; + alphaTestDirty=true; } - activeTextureUnit = textureUnit; - } else { - throw new GLException("glActivateTexture textureUnit not within GL_TEXTURE0 + [0.."+MAX_TEXTURE_UNITS+"]: "+textureUnit); } } /** - * @return false if digested in regard to GL2ES2 spec, + * @return false if digested in regard to GL2ES2 spec, * eg this call must not be passed to an underlying ES2 implementation. * true if this call shall be passed to an underlying GL2ES2/ES2 implementation as well. */ - public boolean glEnable(GL2ES2 gl, int cap, boolean enable) { + public boolean glEnable(int cap, boolean enable) { switch(cap) { - case GL.GL_TEXTURE_2D: - textureEnabled=enable; + case GL.GL_BLEND: + case GL.GL_DEPTH_TEST: + case GL.GL_DITHER: + case GL.GL_POLYGON_OFFSET_FILL: + case GL.GL_SAMPLE_ALPHA_TO_COVERAGE: + case GL.GL_SAMPLE_COVERAGE: + case GL.GL_SCISSOR_TEST: + case GL.GL_STENCIL_TEST: + return true; + + case GL.GL_CULL_FACE: + /** ES2 supports CullFace implicit + final int _cullFace; + if(0>cullFace && enable || 0<cullFace && !enable) { + _cullFace = cullFace * -1; + } else { + _cullFace = cullFace; + } + if(_cullFace != cullFace) { + cullFaceDirty=true; + cullFace=_cullFace; + } */ return true; + + case GL.GL_TEXTURE_2D: + glEnableTexture(enable, activeTextureUnit); + return false; + case GLLightingFunc.GL_LIGHTING: lightingEnabled=enable; return false; - case GL.GL_CULL_FACE: - cullFace=Math.abs(cullFace); - if(!enable) { - cullFace*=-1; + + case GL2ES1.GL_ALPHA_TEST: + final int _alphaTestFunc; + if(0>alphaTestFunc && enable || 0<alphaTestFunc && !enable) { + _alphaTestFunc = alphaTestFunc * -1; + } else { + _alphaTestFunc = alphaTestFunc; } - return true; + if(_alphaTestFunc != alphaTestFunc) { + alphaTestDirty=true; + alphaTestFunc=_alphaTestFunc; + } + return false; + + case GL2ES1.GL_POINT_SMOOTH: + pointParams.put(1, enable ? 1.0f : 0.0f); + pointParamsDirty = true; + return false; + + case GL2ES1.GL_POINT_SPRITE: + // gl_PointCoord always enabled + return false; } int light = cap - GLLightingFunc.GL_LIGHT0; @@ -299,157 +692,422 @@ public class FixedFuncPipeline { return false; } } - return true; // pass it on .. + System.err.println("FixedFunctionPipeline: "+(enable ? "glEnable" : "glDisable")+" "+toHexString(cap)+" not handled in emulation and not supported in ES2"); + return false; // ignore! } - public void glCullFace(GL2ES2 gl, int faceName) { - switch(faceName) { - case GL.GL_FRONT: - faceName = 1; break; - case GL.GL_BACK: - faceName = 2; break; - case GL.GL_FRONT_AND_BACK: - faceName = 3; break; + // + // Draw + // + + public void glDrawArrays(GL2ES2 gl, int mode, int first, int count) { + switch(mode) { + case GL2.GL_QUAD_STRIP: + mode=GL.GL_TRIANGLE_STRIP; + break; + case GL2.GL_POLYGON: + mode=GL.GL_TRIANGLE_FAN; + break; + case GL2ES1.GL_POINTS: + glDrawPointArrays(gl, first, count); + return; } - if(0>cullFace) { - faceName *= -1; + validate(gl, true); + if ( GL2.GL_QUADS == mode && !gl.isGL2() ) { + for (int j = first; j < count - 3; j += 4) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, j, 4); + } + } else { + gl.glDrawArrays(mode, first, count); } - cullFace = faceName; } + public void glDrawElements(GL2ES2 gl, int mode, int count, int type, java.nio.Buffer indices) { + validate(gl, true); + if ( GL2.GL_QUADS == mode && !gl.isGL2() ) { + final int idx0 = indices.position(); + + if( GL.GL_UNSIGNED_BYTE == type ) { + final ByteBuffer b = (ByteBuffer) indices; + for (int j = 0; j < count; j++) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, (int)(0x000000ff & b.get(idx0+j)), 4); + } + } else if( GL.GL_UNSIGNED_SHORT == type ){ + final ShortBuffer b = (ShortBuffer) indices; + for (int j = 0; j < count; j++) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, (int)(0x0000ffff & b.get(idx0+j)), 4); + } + } else { + final IntBuffer b = (IntBuffer) indices; + for (int j = 0; j < count; j++) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, (int)(0xffffffff & b.get(idx0+j)), 4); + } + } + } else { + // FIXME: Impl. VBO usage .. or unroll (see above)! + if( !gl.getContext().isCPUDataSourcingAvail() ) { + throw new GLException("CPU data sourcing n/a w/ "+gl.getContext()); + } + if( GL2ES1.GL_POINTS != mode ) { + ((GLES2)gl).glDrawElements(mode, count, type, indices); + } else { + // FIXME GL_POINTS ! + ((GLES2)gl).glDrawElements(mode, count, type, indices); + } + } + } + public void glDrawElements(GL2ES2 gl, int mode, int count, int type, long indices_buffer_offset) { + validate(gl, true); + if ( GL2.GL_QUADS == mode && !gl.isGL2() ) { + throw new GLException("Cannot handle indexed QUADS on !GL2 w/ VBO due to lack of CPU index access"); + } else if( GL2ES1.GL_POINTS != mode ) { + // FIXME GL_POINTS ! + gl.glDrawElements(mode, count, type, indices_buffer_offset); + } else { + gl.glDrawElements(mode, count, type, indices_buffer_offset); + } + } + + private final int textureEnabledCount() { + int n=0; + for(int i=MAX_TEXTURE_UNITS-1; i>=0; i--) { + if( 0 != textureEnabled.get(i) ) { + n++; + } + } + return n; + } + + public void validate(GL2ES2 gl, boolean selectShader) { + if( selectShader ) { + if( ShaderSelectionMode.AUTO == requestedShaderSelectionMode) { + final ShaderSelectionMode newMode; + + // pre-validate shader switch + if( 0 != textureEnabledBits ) { + if(lightingEnabled) { + newMode = ShaderSelectionMode.COLOR_TEXTURE8_LIGHT_PER_VERTEX; + } else { + final int n = textureEnabledCount(); + if( 4 < n ) { + newMode = ShaderSelectionMode.COLOR_TEXTURE8; + } else if ( 2 < n ) { + newMode = ShaderSelectionMode.COLOR_TEXTURE4; + } else { + newMode = ShaderSelectionMode.COLOR_TEXTURE2; + } + } + } else { + if(lightingEnabled) { + newMode = ShaderSelectionMode.COLOR_LIGHT_PER_VERTEX; + } else { + newMode = ShaderSelectionMode.COLOR; + } + } + shaderState.attachShaderProgram(gl, selectShaderProgram(gl, newMode), true); // enables shader-program implicit + } else { + shaderState.useProgram(gl, true); + } + } - public void validate(GL2ES2 gl) { - shaderState.useProgram(gl, true); GLUniformData ud; - if(pmvMatrix.update()) { + if( pmvMatrix.update() ) { ud = shaderState.getUniform(mgl_PMVMatrix); if(null!=ud) { + final FloatBuffer m; + if(ShaderSelectionMode.COLOR_TEXTURE8_LIGHT_PER_VERTEX == currentShaderSelectionMode || + ShaderSelectionMode.COLOR_LIGHT_PER_VERTEX== currentShaderSelectionMode ) { + m = pmvMatrix.glGetPMvMvitMatrixf(); + } else { + m = pmvMatrix.glGetPMvMatrixf(); + } + if(m != ud.getBuffer()) { + ud.setData(m); + } // same data object .. shaderState.uniform(gl, ud); } else { throw new GLException("Failed to update: mgl_PMVMatrix"); } } - ud = shaderState.getUniform(mgl_ColorEnabled); - if(null!=ud) { - int ca = (shaderState.isVertexAttribArrayEnabled(GLPointerFuncUtil.mgl_Color)==true)?1:0; - if(ca!=ud.intValue()) { - ud.setData(ca); - shaderState.uniform(gl, ud); + if(colorVAEnabledDirty) { + ud = shaderState.getUniform(mgl_ColorEnabled); + if(null!=ud) { + int ca = true == shaderState.isVertexAttribArrayEnabled(GLPointerFuncUtil.mgl_Color) ? 1 : 0 ; + if(ca!=ud.intValue()) { + ud.setData(ca); + shaderState.uniform(gl, ud); + } + } else { + throw new GLException("Failed to update: mgl_ColorEnabled"); } + colorVAEnabledDirty = false; } - ud = shaderState.getUniform(mgl_CullFace); - if(null!=ud) { - if(cullFace!=ud.intValue()) { + /** ES2 supports CullFace implicit + if(cullFaceDirty) { + ud = shaderState.getUniform(mgl_CullFace); + if(null!=ud) { ud.setData(cullFace); shaderState.uniform(gl, ud); } + cullFaceDirty = false; + } */ + + if(alphaTestDirty) { + ud = shaderState.getUniform(mgl_AlphaTestFunc); + if(null!=ud) { + ud.setData(alphaTestFunc); + shaderState.uniform(gl, ud); + } + ud = shaderState.getUniform(mgl_AlphaTestRef); + if(null!=ud) { + ud.setData(alphaTestRef); + shaderState.uniform(gl, ud); + } + alphaTestDirty = false; + } + if(pointParamsDirty) { + ud = shaderState.getUniform(mgl_PointParams); + if(null!=ud) { + // same data object + shaderState.uniform(gl, ud); + } + pointParamsDirty = false; } if(lightsEnabledDirty) { ud = shaderState.getUniform(mgl_LightsEnabled); if(null!=ud) { - // same data object + // same data object shaderState.uniform(gl, ud); } lightsEnabledDirty=false; } - if(textureCoordsEnabledDirty) { + if(textureCoordEnabledDirty) { ud = shaderState.getUniform(mgl_TexCoordEnabled); if(null!=ud) { - // same data object + // same data object shaderState.uniform(gl, ud); } - textureCoordsEnabledDirty=false; + textureCoordEnabledDirty=false; } - if(textureEnabled) { - if(lightingEnabled) { - shaderState.attachShaderProgram(gl, shaderProgramColorTextureLight, true); - } else { - shaderState.attachShaderProgram(gl, shaderProgramColorTexture, true); + if(textureEnvModeDirty) { + ud = shaderState.getUniform(mgl_TexEnvMode); + if(null!=ud) { + // same data object + shaderState.uniform(gl, ud); } - } else { - if(lightingEnabled) { - shaderState.attachShaderProgram(gl, shaderProgramColorLight, true); - } else { - shaderState.attachShaderProgram(gl, shaderProgramColor, true); + textureEnvModeDirty = false; + } + + if(textureFormatDirty) { + for(int i = 0; i<MAX_TEXTURE_UNITS; i++) { + textureFormat.put(i, texID2Format.get(boundTextureObject[i])); } + ud = shaderState.getUniform(mgl_TexFormat); + if(null!=ud) { + // same data object + shaderState.uniform(gl, ud); + } + textureFormatDirty = false; } - if(DEBUG) { - System.err.println("validate: "+this); + if(textureEnabledDirty) { + ud = shaderState.getUniform(mgl_TextureEnabled); + if(null!=ud) { + // same data object + shaderState.uniform(gl, ud); + } + textureEnabledDirty=false; + } + + if(verbose) { + System.err.println("validate: "+toString(null, DEBUG).toString()); } } - public String toString() { - return "FixedFuncPipeline[pmv: "+pmvMatrix+ - ", textureEnabled: "+textureEnabled+ - ", textureCoordsEnabled: "+textureCoordsEnabled+ - ", lightingEnabled: "+lightingEnabled+ - ", lightsEnabled: "+lightsEnabled+ - "\n\t, shaderProgramColor: "+shaderProgramColor+ - "\n\t, shaderProgramColorTexture: "+shaderProgramColorTexture+ - "\n\t, shaderProgramColorLight: "+shaderProgramColorLight+ - "\n\t, shaderProgramColorTextureLight: "+shaderProgramColorTextureLight+ - "\n\t, ShaderState: "+shaderState+ - "]"; - } - - protected void init(GL2ES2 gl, PMVMatrix pmvMatrix, Class shaderRootClass, String shaderSrcRoot, String shaderBinRoot, - String vertexColorFile, - String vertexColorLightFile, - String fragmentColorFile, - String fragmentColorTextureFile) - { - if(null==pmvMatrix) { - throw new GLException("PMVMatrix is null"); + public StringBuilder toString(StringBuilder sb, boolean alsoUnlocated) { + if(null == sb) { + sb = new StringBuilder(); } - this.pmvMatrix=pmvMatrix; - this.shaderState=new ShaderState(); - this.shaderState.setVerbose(verbose); - ShaderCode vertexColor, vertexColorLight, fragmentColor, fragmentColorTexture; + sb.append("FixedFuncPipeline["); + sb.append(", textureEnabled: "+toHexString(textureEnabledBits)+", "); Buffers.toString(sb, null, textureEnabled); + sb.append("\n\t, textureCoordEnabled: "); Buffers.toString(sb, null, textureCoordEnabled); + sb.append("\n\t lightingEnabled: "+lightingEnabled); + sb.append(", lightsEnabled: "); Buffers.toString(sb, null, lightsEnabled); + sb.append("\n\t, shaderProgramColor: "+shaderProgramColor); + sb.append("\n\t, shaderProgramColorTexture2: "+shaderProgramColorTexture2); + sb.append("\n\t, shaderProgramColorTexture4: "+shaderProgramColorTexture4); + sb.append("\n\t, shaderProgramColorTexture8: "+shaderProgramColorTexture8); + sb.append("\n\t, shaderProgramColorLight: "+shaderProgramColorLight); + sb.append("\n\t, shaderProgramColorTexture8Light: "+shaderProgramColorTexture8Light); + sb.append("\n\t, ShaderState: "); + shaderState.toString(sb, alsoUnlocated); + sb.append("]"); + return sb; + } + @Override + public String toString() { + return toString(null, DEBUG).toString(); + } - vertexColor = ShaderCode.create( gl, gl.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, - shaderBinRoot, vertexColorFile, false); + private static final String constMaxTextures0 = "#define MAX_TEXTURE_UNITS 0\n"; + private static final String constMaxTextures2 = "#define MAX_TEXTURE_UNITS 2\n"; + private static final String constMaxTextures4 = "#define MAX_TEXTURE_UNITS 4\n"; + private static final String constMaxTextures8 = "#define MAX_TEXTURE_UNITS 8\n"; - vertexColorLight = ShaderCode.create( gl, gl.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, - shaderBinRoot, vertexColorLightFile, false); + private final void customizeShader(GL2ES2 gl, ShaderCode vp, ShaderCode fp, String maxTextureDefine) { + int rsVpPos = vp.defaultShaderCustomization(gl, true, true); + int rsFpPos = fp.defaultShaderCustomization(gl, true, true); + vp.insertShaderSource(0, rsVpPos, maxTextureDefine); + fp.insertShaderSource(0, rsFpPos, maxTextureDefine); + } - fragmentColor = ShaderCode.create( gl, gl.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, - shaderBinRoot, fragmentColorFile, false); + private final void loadShaderPoints(GL2ES2 gl) { + if( null != shaderProgramPoints ) { + return; + } - fragmentColorTexture = ShaderCode.create( gl, gl.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, - shaderBinRoot, fragmentColorTextureFile, false); + final ShaderCode vp = ShaderCode.create( gl, GL2ES2.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, shaderPointFileDef, true); + final ShaderCode fp = ShaderCode.create( gl, GL2ES2.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, shaderPointFileDef, true); + customizeShader(gl, vp, fp, constMaxTextures2); + shaderProgramPoints = new ShaderProgram(); + shaderProgramPoints.add(vp); + shaderProgramPoints.add(fp); + if(!shaderProgramPoints.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColor program: "+shaderProgramPoints); + } + } - shaderProgramColor = new ShaderProgram(); - shaderProgramColor.add(vertexColor); - shaderProgramColor.add(fragmentColor); - if(!shaderProgramColor.link(gl, System.err)) { - throw new GLException("Couldn't link VertexColor program: "+shaderProgramColor); + private final void loadShader(GL2ES2 gl, ShaderSelectionMode mode) { + final boolean loadColor = ShaderSelectionMode.COLOR == mode; + final boolean loadColorTexture2 = ShaderSelectionMode.COLOR_TEXTURE2 == mode; + final boolean loadColorTexture4 = ShaderSelectionMode.COLOR_TEXTURE4 == mode; + final boolean loadColorTexture8 = ShaderSelectionMode.COLOR_TEXTURE8 == mode; + final boolean loadColorTexture = loadColorTexture2 || loadColorTexture4 || loadColorTexture8 ; + final boolean loadColorLightPerVertex = ShaderSelectionMode.COLOR_LIGHT_PER_VERTEX == mode; + final boolean loadColorTexture8LightPerVertex = ShaderSelectionMode.COLOR_TEXTURE8_LIGHT_PER_VERTEX == mode; + + if( null != shaderProgramColor && loadColor || + null != shaderProgramColorTexture2 && loadColorTexture2 || + null != shaderProgramColorTexture4 && loadColorTexture4 || + null != shaderProgramColorTexture8 && loadColorTexture8 || + null != shaderProgramColorLight && loadColorLightPerVertex || + null != shaderProgramColorTexture8Light && loadColorTexture8LightPerVertex ) { + return; } - shaderProgramColorTexture = new ShaderProgram(); - shaderProgramColorTexture.add(vertexColor); - shaderProgramColorTexture.add(fragmentColorTexture); - if(!shaderProgramColorTexture.link(gl, System.err)) { - throw new GLException("Couldn't link VertexColorTexture program: "+shaderProgramColorTexture); + if( loadColor ) { + final ShaderCode vp = ShaderCode.create( gl, GL2ES2.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, vertexColorFile, true); + final ShaderCode fp = ShaderCode.create( gl, GL2ES2.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, fragmentColorFile, true); + customizeShader(gl, vp, fp, constMaxTextures0); + shaderProgramColor = new ShaderProgram(); + shaderProgramColor.add(vp); + shaderProgramColor.add(fp); + if(!shaderProgramColor.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColor program: "+shaderProgramColor); + } + } else if( loadColorTexture ) { + final ShaderCode vp = ShaderCode.create( gl, GL2ES2.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, shaderBinRoot, vertexColorFile, true); + final ShaderCode fp = ShaderCode.create( gl, GL2ES2.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, fragmentColorTextureFile, true); + + if( loadColorTexture2 ) { + customizeShader(gl, vp, fp, constMaxTextures2); + shaderProgramColorTexture2 = new ShaderProgram(); + shaderProgramColorTexture2.add(vp); + shaderProgramColorTexture2.add(fp); + if(!shaderProgramColorTexture2.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColorTexture2 program: "+shaderProgramColorTexture2); + } + } else if( loadColorTexture4 ) { + customizeShader(gl, vp, fp, constMaxTextures4); + shaderProgramColorTexture4 = new ShaderProgram(); + shaderProgramColorTexture4.add(vp); + shaderProgramColorTexture4.add(fp); + if(!shaderProgramColorTexture4.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColorTexture4 program: "+shaderProgramColorTexture4); + } + } else if( loadColorTexture8 ) { + customizeShader(gl, vp, fp, constMaxTextures8); + shaderProgramColorTexture8 = new ShaderProgram(); + shaderProgramColorTexture8.add(vp); + shaderProgramColorTexture8.add(fp); + if(!shaderProgramColorTexture8.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColorTexture8 program: "+shaderProgramColorTexture8); + } + } + } else if( loadColorLightPerVertex ) { + final ShaderCode vp = ShaderCode.create( gl, GL2ES2.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, vertexColorLightFile, true); + final ShaderCode fp = ShaderCode.create( gl, GL2ES2.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, fragmentColorFile, true); + customizeShader(gl, vp, fp, constMaxTextures0); + shaderProgramColorLight = new ShaderProgram(); + shaderProgramColorLight.add(vp); + shaderProgramColorLight.add(fp); + if(!shaderProgramColorLight.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColorLight program: "+shaderProgramColorLight); + } + } else if( loadColorTexture8LightPerVertex ) { + final ShaderCode vp = ShaderCode.create( gl, GL2ES2.GL_VERTEX_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, vertexColorLightFile, true); + final ShaderCode fp = ShaderCode.create( gl, GL2ES2.GL_FRAGMENT_SHADER, shaderRootClass, shaderSrcRoot, + shaderBinRoot, fragmentColorTextureFile, true); + customizeShader(gl, vp, fp, constMaxTextures8); + shaderProgramColorTexture8Light = new ShaderProgram(); + shaderProgramColorTexture8Light.add(vp); + shaderProgramColorTexture8Light.add(fp); + if(!shaderProgramColorTexture8Light.link(gl, System.err)) { + throw new GLException("Couldn't link VertexColorLight program: "+shaderProgramColorTexture8Light); + } } + } - shaderProgramColorLight = new ShaderProgram(); - shaderProgramColorLight.add(vertexColorLight); - shaderProgramColorLight.add(fragmentColor); - if(!shaderProgramColorLight.link(gl, System.err)) { - throw new GLException("Couldn't link VertexColorLight program: "+shaderProgramColorLight); + private ShaderProgram selectShaderProgram(GL2ES2 gl, ShaderSelectionMode newMode) { + if(ShaderSelectionMode.AUTO == newMode) { + newMode = ShaderSelectionMode.COLOR; } + loadShader(gl, newMode); + final ShaderProgram sp; + switch(newMode) { + case COLOR_LIGHT_PER_VERTEX: + sp = shaderProgramColorLight; + break; + case COLOR_TEXTURE2: + sp = shaderProgramColorTexture2; + break; + case COLOR_TEXTURE4: + sp = shaderProgramColorTexture4; + break; + case COLOR_TEXTURE8: + sp = shaderProgramColorTexture8; + break; + case COLOR_TEXTURE8_LIGHT_PER_VERTEX: + sp = shaderProgramColorTexture8Light; + break; + case COLOR: + default: + sp = shaderProgramColor; + } + currentShaderSelectionMode = newMode; + return sp; + } - shaderProgramColorTextureLight = new ShaderProgram(); - shaderProgramColorTextureLight.add(vertexColorLight); - shaderProgramColorTextureLight.add(fragmentColorTexture); - if(!shaderProgramColorTextureLight.link(gl, System.err)) { - throw new GLException("Couldn't link VertexColorLight program: "+shaderProgramColorTextureLight); + private void init(GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix) { + if(null==pmvMatrix) { + throw new GLException("PMVMatrix is null"); } + this.pmvMatrix=pmvMatrix; + this.requestedShaderSelectionMode = mode; + this.shaderState=new ShaderState(); + this.shaderState.setVerbose(verbose); - shaderState.attachShaderProgram(gl, shaderProgramColor, true); + shaderState.attachShaderProgram(gl, selectShaderProgram(gl, requestedShaderSelectionMode), true); // mandatory .. if(!shaderState.uniform(gl, new GLUniformData(mgl_PMVMatrix, 4, 4, pmvMatrix.glGetPMvMvitMatrixf()))) { @@ -457,16 +1115,26 @@ public class FixedFuncPipeline { } shaderState.uniform(gl, new GLUniformData(mgl_ColorEnabled, 0)); - shaderState.uniform(gl, new GLUniformData(mgl_ColorStatic, 4, zero4f)); - shaderState.uniform(gl, new GLUniformData(mgl_TexCoordEnabled, 1, textureCoordsEnabled)); - shaderState.uniform(gl, new GLUniformData(mgl_ActiveTexture, activeTextureUnit)); - shaderState.uniform(gl, new GLUniformData(mgl_ActiveTextureIdx, activeTextureUnit)); + shaderState.uniform(gl, new GLUniformData(mgl_ColorStatic, 4, colorStatic)); + + texID2Format.setKeyNotFoundValue(0); + shaderState.uniform(gl, new GLUniformData(mgl_TexCoordEnabled, 1, textureCoordEnabled)); + shaderState.uniform(gl, new GLUniformData(mgl_TexEnvMode, 1, textureEnvMode)); + shaderState.uniform(gl, new GLUniformData(mgl_TexFormat, 1, textureFormat)); + shaderState.uniform(gl, new GLUniformData(mgl_TextureEnabled, 1, textureEnabled)); + for(int i=0; i<MAX_TEXTURE_UNITS; i++) { + shaderState.uniform(gl, new GLUniformData(mgl_Texture+i, i)); + } shaderState.uniform(gl, new GLUniformData(mgl_ShadeModel, 0)); - shaderState.uniform(gl, new GLUniformData(mgl_CullFace, cullFace)); + /** ES2 supports CullFace implicit + shaderState.uniform(gl, new GLUniformData(mgl_CullFace, cullFace)); */ + shaderState.uniform(gl, new GLUniformData(mgl_AlphaTestFunc, alphaTestFunc)); + shaderState.uniform(gl, new GLUniformData(mgl_AlphaTestRef, alphaTestRef)); + shaderState.uniform(gl, new GLUniformData(mgl_PointParams, 4, pointParams)); for(int i=0; i<MAX_LIGHTS; i++) { shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].ambient", 4, defAmbient)); - shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].diffuse", 4, defDiffuse)); - shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].specular", 4, defSpecular)); + shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].diffuse", 4, 0==i ? one4f : defDiffuseN)); + shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].specular", 4, 0==i ? one4f : defSpecularN)); shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].position", 4, defPosition)); shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].spotDirection", 3, defSpotDir)); shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].spotExponent", defSpotExponent)); @@ -475,6 +1143,7 @@ public class FixedFuncPipeline { shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].linearAttenuation", defLinearAtten)); shaderState.uniform(gl, new GLUniformData(mgl_LightSource+"["+i+"].quadraticAttenuation", defQuadraticAtten)); } + shaderState.uniform(gl, new GLUniformData(mgl_LightModel+".ambient", 4, defLightModelAmbient)); shaderState.uniform(gl, new GLUniformData(mgl_LightsEnabled, 1, lightsEnabled)); shaderState.uniform(gl, new GLUniformData(mgl_FrontMaterial+".ambient", 4, defMatAmbient)); shaderState.uniform(gl, new GLUniformData(mgl_FrontMaterial+".diffuse", 4, defMatDiffuse)); @@ -483,70 +1152,121 @@ public class FixedFuncPipeline { shaderState.uniform(gl, new GLUniformData(mgl_FrontMaterial+".shininess", defMatShininess)); shaderState.useProgram(gl, false); + if(verbose) { + System.err.println("init: "+toString(null, DEBUG).toString()); + } } - protected static final boolean DEBUG=false; - protected boolean verbose=false; + private String toHexString(int i) { + return "0x"+Integer.toHexString(i); + } + + protected boolean verbose = DEBUG; + + private final FloatBuffer colorStatic = Buffers.copyFloatBuffer(one4f); + + private int activeTextureUnit=0; + private int clientActiveTextureUnit=0; + private final IntIntHashMap texID2Format = new IntIntHashMap(); + private final int[] boundTextureObject = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // per unit + private int textureEnabledBits = 0; + private final IntBuffer textureEnabled = Buffers.newDirectIntBuffer(new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }); // per unit + private boolean textureEnabledDirty = false; + private final IntBuffer textureCoordEnabled = Buffers.newDirectIntBuffer(new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }); // per unit + private boolean textureCoordEnabledDirty = false; + // textureEnvMode: 1 GL_ADD, 2 GL_MODULATE (default), 3 GL_DECAL, 4 GL_BLEND, 5 GL_REPLACE, 6 GL_COMBINE + private final IntBuffer textureEnvMode = Buffers.newDirectIntBuffer(new int[] { 2, 2, 2, 2, 2, 2, 2, 2 }); + private boolean textureEnvModeDirty = false; + private final IntBuffer textureFormat = Buffers.newDirectIntBuffer(new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }); // per unit + private boolean textureFormatDirty = false; + + /** ES2 supports CullFace implicit + private int cullFace=-2; // <=0 disabled, 1 GL_FRONT, 2 GL_BACK (default) and 3 GL_FRONT_AND_BACK + private boolean cullFaceDirty = false; + private static final String mgl_CullFace = "mgl_CullFace"; // 1i (lowp int) */ - protected boolean textureEnabled=false; - protected IntBuffer textureCoordsEnabled = Buffers.newDirectIntBuffer(new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }); - protected boolean textureCoordsEnabledDirty = false; - protected int activeTextureUnit=0; + private boolean colorVAEnabledDirty = false; + private boolean lightingEnabled=false; + private final IntBuffer lightsEnabled = Buffers.newDirectIntBuffer(new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }); + private boolean lightsEnabledDirty = false; - protected int cullFace=-2; // <=0 disabled, 1: front, 2: back (default, but disabled), 3: front & back + private boolean alphaTestDirty=false; + private int alphaTestFunc=-8; // <=0 disabled; 1 GL_NEVER, 2 GL_LESS, 3 GL_EQUAL, 4 GL_LEQUAL, 5 GL_GREATER, 6 GL_NOTEQUAL, 7 GL_GEQUAL, and 8 GL_ALWAYS (default) + private float alphaTestRef=0f; - protected boolean lightingEnabled=false; - protected IntBuffer lightsEnabled = Buffers.newDirectIntBuffer(new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }); - protected boolean lightsEnabledDirty = false; + private boolean pointParamsDirty = false; + /** ( pointSize, pointSmooth, attn. pointMinSize, attn. pointMaxSize ) , ( attenuation coefficients 1f 0f 0f, attenuation fade theshold 1f ) */ + private final FloatBuffer pointParams = Buffers.newDirectFloatBuffer(new float[] { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f }); - protected PMVMatrix pmvMatrix; - protected ShaderState shaderState; - protected ShaderProgram shaderProgramColor; - protected ShaderProgram shaderProgramColorTexture; - protected ShaderProgram shaderProgramColorLight; - protected ShaderProgram shaderProgramColorTextureLight; + private PMVMatrix pmvMatrix; + private ShaderState shaderState; + private ShaderProgram shaderProgramColor; + private ShaderProgram shaderProgramColorTexture2, shaderProgramColorTexture4, shaderProgramColorTexture8; + private ShaderProgram shaderProgramColorLight; + private ShaderProgram shaderProgramColorTexture8Light; + private ShaderProgram shaderProgramPoints; + + private ShaderSelectionMode requestedShaderSelectionMode = ShaderSelectionMode.AUTO; + private ShaderSelectionMode currentShaderSelectionMode = requestedShaderSelectionMode; // uniforms .. - protected static final String mgl_PMVMatrix = "mgl_PMVMatrix"; // m4fv[4] - P, Mv, Mvi and Mvit - protected static final String mgl_ColorEnabled = "mgl_ColorEnabled"; // 1i - protected static final String mgl_ColorStatic = "mgl_ColorStatic"; // 4fv - - protected static final String mgl_LightSource = "mgl_LightSource"; // struct mgl_LightSourceParameters[MAX_LIGHTS] - protected static final String mgl_FrontMaterial = "mgl_FrontMaterial"; // struct mgl_MaterialParameters - protected static final String mgl_LightsEnabled = "mgl_LightsEnabled"; // int mgl_LightsEnabled[MAX_LIGHTS]; - - protected static final String mgl_ShadeModel = "mgl_ShadeModel"; // 1i - - protected static final String mgl_TexCoordEnabled = "mgl_TexCoordEnabled"; // int mgl_TexCoordEnabled[MAX_TEXTURE_UNITS]; - protected static final String mgl_ActiveTexture = "mgl_ActiveTexture"; // 1i - protected static final String mgl_ActiveTextureIdx = "mgl_ActiveTextureIdx";// 1i - - protected static final String mgl_CullFace = "mgl_CullFace"; // 1i - - protected static final FloatBuffer zero4f = Buffers.newDirectFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }); - - public static final FloatBuffer defAmbient = Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, 0f, 1f }); - public static final FloatBuffer defDiffuse = zero4f; - public static final FloatBuffer defSpecular= zero4f; - public static final FloatBuffer defPosition= Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, 1f, 0f }); - public static final FloatBuffer defSpotDir = Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, -1f }); - public static final float defSpotExponent = 0f; - public static final float defSpotCutoff = 180f; - public static final float defConstantAtten = 1f; - public static final float defLinearAtten = 0f; - public static final float defQuadraticAtten= 0f; - - public static final FloatBuffer defMatAmbient = Buffers.newDirectFloatBuffer(new float[] { 0.2f, 0.2f, 0.2f, 1.0f }); - public static final FloatBuffer defMatDiffuse = Buffers.newDirectFloatBuffer(new float[] { 0.8f, 0.8f, 0.8f, 1.0f }); - public static final FloatBuffer defMatSpecular= Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, 0f, 1f}); - public static final FloatBuffer defMatEmission= Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, 0f, 1f}); + private static final String mgl_PMVMatrix = "mgl_PMVMatrix"; // m4fv[4] - P, Mv, Mvi and Mvit + private static final String mgl_ColorEnabled = "mgl_ColorEnabled"; // 1i + private static final String mgl_ColorStatic = "mgl_ColorStatic"; // 4fv + + private static final String mgl_LightModel = "mgl_LightModel"; // struct mgl_LightModelParameters + private static final String mgl_LightSource = "mgl_LightSource"; // struct mgl_LightSourceParameters[MAX_LIGHTS] + private static final String mgl_FrontMaterial = "mgl_FrontMaterial"; // struct mgl_MaterialParameters + private static final String mgl_LightsEnabled = "mgl_LightsEnabled"; // int mgl_LightsEnabled[MAX_LIGHTS]; + + private static final String mgl_AlphaTestFunc = "mgl_AlphaTestFunc"; // 1i (lowp int) + private static final String mgl_AlphaTestRef = "mgl_AlphaTestRef"; // 1f + private static final String mgl_ShadeModel = "mgl_ShadeModel"; // 1i + private static final String mgl_PointParams = "mgl_PointParams"; // vec4[2]: { (sz, smooth, attnMinSz, attnMaxSz), (attnCoeff(3), attnFadeTs) } + + private static final String mgl_TextureEnabled = "mgl_TextureEnabled"; // int mgl_TextureEnabled[MAX_TEXTURE_UNITS]; + private static final String mgl_Texture = "mgl_Texture"; // sampler2D mgl_Texture<0..7> + private static final String mgl_TexCoordEnabled = "mgl_TexCoordEnabled"; // int mgl_TexCoordEnabled[MAX_TEXTURE_UNITS]; + private static final String mgl_TexEnvMode = "mgl_TexEnvMode"; // int mgl_TexEnvMode[MAX_TEXTURE_UNITS]; + private static final String mgl_TexFormat = "mgl_TexFormat"; // int mgl_TexFormat[MAX_TEXTURE_UNITS]; + + // private static final FloatBuffer zero4f = Buffers.newDirectFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }); + private static final FloatBuffer neut4f = Buffers.newDirectFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f, 1.0f }); + private static final FloatBuffer one4f = Buffers.newDirectFloatBuffer(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }); + + public static final FloatBuffer defAmbient = neut4f; + public static final FloatBuffer defDiffuseN = neut4f; + public static final FloatBuffer defSpecularN = neut4f; + public static final FloatBuffer defPosition = Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, 1f, 0f }); + public static final FloatBuffer defSpotDir = Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, -1f }); + public static final float defSpotExponent = 0f; + public static final float defSpotCutoff = 180f; + public static final float defConstantAtten = 1f; + public static final float defLinearAtten = 0f; + public static final float defQuadraticAtten = 0f; + + public static final FloatBuffer defLightModelAmbient = Buffers.newDirectFloatBuffer(new float[] { 0.2f, 0.2f, 0.2f, 1.0f }); + + public static final FloatBuffer defMatAmbient = Buffers.newDirectFloatBuffer(new float[] { 0.2f, 0.2f, 0.2f, 1.0f }); + public static final FloatBuffer defMatDiffuse = Buffers.newDirectFloatBuffer(new float[] { 0.8f, 0.8f, 0.8f, 1.0f }); + public static final FloatBuffer defMatSpecular = neut4f; + public static final FloatBuffer defMatEmission = neut4f; public static final float defMatShininess = 0f; - protected static final String vertexColorFileDef = "FixedFuncColor"; - protected static final String vertexColorLightFileDef = "FixedFuncColorLight"; - protected static final String fragmentColorFileDef = "FixedFuncColor"; - protected static final String fragmentColorTextureFileDef = "FixedFuncColorTexture"; - protected static final String shaderSrcRootDef = "shaders" ; - protected static final String shaderBinRootDef = "shaders/bin" ; + private static final String vertexColorFileDef = "FixedFuncColor"; + private static final String vertexColorLightFileDef = "FixedFuncColorLight"; + private static final String fragmentColorFileDef = "FixedFuncColor"; + private static final String fragmentColorTextureFileDef = "FixedFuncColorTexture"; + private static final String shaderPointFileDef = "FixedFuncPoints"; + private static final String shaderSrcRootDef = "shaders" ; + private static final String shaderBinRootDef = "shaders/bin" ; + + private final Class<?> shaderRootClass; + private final String shaderSrcRoot; + private final String shaderBinRoot; + private final String vertexColorFile; + private final String vertexColorLightFile; + private final String fragmentColorFile; + private final String fragmentColorTextureFile; } diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.fp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.fp index 408ff7251..22dd1e61a 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.fp +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.fp @@ -1,16 +1,32 @@ + +#if __VERSION__ >= 130 + #define varying in + out vec4 mgl_FragColor; +#else + #define mgl_FragColor gl_FragColor +#endif + #include es_precision.glsl #include mgl_uniform.glsl #include mgl_varying.glsl +#include mgl_alphatest.fp + void main (void) { - if( mgl_CullFace > 0 && - ( ( mgl_CullFace == 1 && gl_FrontFacing ) || - ( mgl_CullFace == 2 && !gl_FrontFacing ) || - ( mgl_CullFace == 3 ) ) ) { - discard; + vec4 color = frontColor; + + /** ES2 supports CullFace implicit .. + if( mgl_CullFace > 0 && + ( ( MGL_FRONT == mgl_CullFace && gl_FrontFacing ) || + ( MGL_BACK == mgl_CullFace && !gl_FrontFacing ) || + ( MGL_FRONT_AND_BACK == mgl_CullFace ) ) ) { + DISCARD(color); + } */ + if( mgl_AlphaTestFunc > 0 ) { + alphaTest(color); } - gl_FragColor = frontColor; + mgl_FragColor = color; } diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.vp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.vp index 346e40196..f39fcfbd0 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.vp +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColor.vp @@ -1,3 +1,9 @@ + +#if __VERSION__ >= 130 + #define attribute in + #define varying out +#endif + #include es_precision.glsl #include mgl_const.glsl diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorLight.vp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorLight.vp index 7ce1eedcf..942a540af 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorLight.vp +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorLight.vp @@ -1,3 +1,9 @@ + +#if __VERSION__ >= 130 + #define attribute in + #define varying out +#endif + #include es_precision.glsl #include mgl_lightdef.glsl @@ -50,16 +56,18 @@ void main(void) } } } - ambient *= mgl_FrontMaterial.ambient; - diffuse *= mgl_FrontMaterial.diffuse; - specular *= mgl_FrontMaterial.specular; - if(mgl_ColorEnabled>0) { frontColor=mgl_Color; } else { frontColor=mgl_ColorStatic; } if( lightEnabled ) { + // light-ambient + global-ambient + // ( mgl_LightSource[0..n].ambient * mgl_FrontMaterial.ambient ) + ( mgl_LightModel.ambient * mgl_FrontMaterial.ambient ) + ambient = ( ambient + mgl_LightModel.ambient ) * mgl_FrontMaterial.ambient; + diffuse *= mgl_FrontMaterial.diffuse; + specular *= mgl_FrontMaterial.specular; + frontColor *= ambient + diffuse + specular; } diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorTexture.fp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorTexture.fp index 86e6ace73..130711e19 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorTexture.fp +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncColorTexture.fp @@ -1,4 +1,13 @@ +#if __VERSION__ >= 130 + #define varying in + out vec4 mgl_FragColor; + #define texture2D texture +#else + #define mgl_FragColor gl_FragColor +#endif + + #include es_precision.glsl #include mgl_lightdef.glsl @@ -6,42 +15,103 @@ #include mgl_uniform.glsl #include mgl_varying.glsl -vec4 getTexColor(in sampler2D tex, in int idx) { - vec4 coord; - if(idx==0) { - coord= mgl_TexCoords[0]; - } else if(idx==1) { - coord= mgl_TexCoords[1]; - } else if(idx==2) { - coord= mgl_TexCoords[2]; - } else if(idx==3) { - coord= mgl_TexCoords[3]; - } else if(idx==4) { - coord= mgl_TexCoords[4]; - } else if(idx==5) { - coord= mgl_TexCoords[5]; - } else if(idx==6) { - coord= mgl_TexCoords[6]; - } else { - coord= mgl_TexCoords[7]; +#include mgl_alphatest.fp + +const float gamma = 1.5; // FIXME +const vec3 igammav = vec3(1.0 / gamma); // FIXME +const vec4 texEnvColor = vec4(0.0); // FIXME + +const vec4 zerov4 = vec4(0.0); +const vec4 onev4 = vec4(1.0); + +void calcTexColor(inout vec4 color, vec4 texColor, in int texFormat, in int texEnvMode) { + if(MGL_MODULATE == texEnvMode) { // default + if( 4 == texFormat ) { + color *= texColor; + } else { + color.rgb *= texColor.rgb; + } + } else if(MGL_REPLACE == texEnvMode) { + if( 4 == texFormat ) { + color = texColor; + } else { + color.rgb = texColor.rgb; + } + } else if(MGL_ADD == texEnvMode) { + if( 4 == texFormat ) { + color += texColor; + } else { + color.rgb += texColor.rgb; + } + } else if(MGL_BLEND == texEnvMode) { + color.rgb = mix(color.rgb, texEnvColor.rgb, texColor.rgb); + if( 4 == texFormat ) { + color.a *= texColor.a; + } + } else if(MGL_DECAL == texEnvMode) { + if( 4 == texFormat ) { + color.rgb = mix(color.rgb, texColor.rgb, texColor.a); + } else { + color.rgb = texColor.rgb; + } } - return texture2D(tex, coord.st); + color = clamp(color, zerov4, onev4); } void main (void) -{ - if( mgl_CullFace > 0 && - ( ( mgl_CullFace == 1 && gl_FrontFacing ) || - ( mgl_CullFace == 2 && !gl_FrontFacing ) || - ( mgl_CullFace == 3 ) ) ) { - discard; - } - - vec4 texColor = getTexColor(mgl_ActiveTexture,mgl_ActiveTextureIdx); - - if(length(texColor.rgb)>0.0) { - gl_FragColor = vec4(frontColor.rgb*texColor.rgb, frontColor.a) ; +{ + vec4 color = frontColor; + + /** ES2 supports CullFace implicit .. + if( mgl_CullFace > 0 && + ( ( MGL_FRONT == mgl_CullFace && gl_FrontFacing ) || + ( MGL_BACK == mgl_CullFace && !gl_FrontFacing ) || + ( MGL_FRONT_AND_BACK == mgl_CullFace ) ) ) { + DISCARD(color); + } else { */ + #if MAX_TEXTURE_UNITS >= 2 + if( 0 != mgl_TextureEnabled[0] ) { + calcTexColor(color, texture2D(mgl_Texture0, mgl_TexCoords[0].st), mgl_TexFormat[0], mgl_TexEnvMode[0]); + } + if( 0 != mgl_TextureEnabled[1] ) { + calcTexColor(color, texture2D(mgl_Texture1, mgl_TexCoords[1].st), mgl_TexFormat[1], mgl_TexEnvMode[1]); + } + #endif + #if MAX_TEXTURE_UNITS >= 4 + if( 0 != mgl_TextureEnabled[2] ) { + calcTexColor(color, texture2D(mgl_Texture2, mgl_TexCoords[2].st), mgl_TexFormat[2], mgl_TexEnvMode[2]); + } + if( 0 != mgl_TextureEnabled[3] ) { + calcTexColor(color, texture2D(mgl_Texture3, mgl_TexCoords[3].st), mgl_TexFormat[3], mgl_TexEnvMode[3]); + } + #endif + #if MAX_TEXTURE_UNITS >= 8 + if( 0 != mgl_TextureEnabled[4] ) { + calcTexColor(color, texture2D(mgl_Texture4, mgl_TexCoords[4].st), mgl_TexFormat[4], mgl_TexEnvMode[4]); + } + if( 0 != mgl_TextureEnabled[5] ) { + calcTexColor(color, texture2D(mgl_Texture5, mgl_TexCoords[5].st), mgl_TexFormat[5], mgl_TexEnvMode[5]); + } + if( 0 != mgl_TextureEnabled[6] ) { + calcTexColor(color, texture2D(mgl_Texture6, mgl_TexCoords[6].st), mgl_TexFormat[6], mgl_TexEnvMode[6]); + } + if( 0 != mgl_TextureEnabled[7] ) { + calcTexColor(color, texture2D(mgl_Texture7, mgl_TexCoords[7].st), mgl_TexFormat[7], mgl_TexEnvMode[7]); + } + #endif + if( mgl_AlphaTestFunc > 0 ) { + alphaTest(color); + } + // } /* CullFace */ + + mgl_FragColor = color; + /** + // simple alpha check + if (color.a != 0.0) { + mgl_FragColor = vec4(pow(color.rgb, igammav), color.a); } else { - gl_FragColor = frontColor; - } + // discard; // freezes NV tegra2 compiler + mgl_FragColor = color; + } */ } + diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncPoints.fp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncPoints.fp new file mode 100644 index 000000000..2d58f2320 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncPoints.fp @@ -0,0 +1,47 @@ + +#if __VERSION__ >= 130 + #define varying in + out vec4 mgl_FragColor; +#else + #define mgl_FragColor gl_FragColor +#endif + + +#include es_precision.glsl +#include mgl_lightdef.glsl + +#include mgl_const.glsl +#include mgl_uniform.glsl +#include mgl_varying.glsl + +// #define TEST 1 + +void main (void) +{ + mgl_FragColor = frontColor; + + if( pointSmooth > 0.5 ) { + // smooth (AA) + const float border = 0.90; // take/give 10% for AA + + // origin to 0/0, [-1/-1 .. 1/1] + vec2 pointPos = 2.0 * gl_PointCoord - 1.0 ; + float r = length( pointPos ); // one-circle sqrt(x * x + y * y), range: in-circle [0..1], out >1 + float r1 = 1.0 - ( step(border, r) * 10.0 * ( r - border ) ) ; // [0..1] + #ifndef TEST + if( r1 < 0.0 ) { + discard; + } + #endif + + #ifndef TEST + mgl_FragColor.a *= r1; + #else + mgl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + mgl_FragColor.r = r1 < 0.0 ? 1.0 : 0.0; + mgl_FragColor.g = r > 1.0 ? 1.0 : 0.0; + mgl_FragColor.b = r > border ? 1.0 : 0.0; + #endif + } +} + diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncPoints.vp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncPoints.vp new file mode 100644 index 000000000..4a5d93a3d --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/FixedFuncPoints.vp @@ -0,0 +1,40 @@ + +#if __VERSION__ >= 130 + #define attribute in + #define varying out +#endif + +#include es_precision.glsl + +#include mgl_const.glsl +#include mgl_uniform.glsl +#include mgl_attribute.glsl +#include mgl_varying.glsl + +#include mgl_settexcoord.vp + +void main(void) +{ + if( mgl_ColorEnabled > 0 ) { + frontColor = mgl_Color; + } else { + frontColor = mgl_ColorStatic; + } + + vec4 eyeCoord = mgl_PMVMatrix[1] * mgl_Vertex; + gl_Position = mgl_PMVMatrix[0] * eyeCoord; + + float dist = distance(eyeCoord, vec4(0.0, 0.0, 0.0, 1.0)); + float atten = sqrt( 1.0 / ( pointDistanceConstantAtten + + ( pointDistanceLinearAtten + + pointDistanceQuadraticAtten * dist + ) * dist + ) + ); + float size = clamp(pointSize * atten, pointSizeMin, pointSizeMax); + gl_PointSize = max(size, pointFadeThresholdSize); + + float fade = min(size, pointFadeThresholdSize) / pointFadeThresholdSize; + frontColor.a *= fade * fade; +} + diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_alphatest.fp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_alphatest.fp new file mode 100644 index 000000000..2b64cdeb8 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_alphatest.fp @@ -0,0 +1,33 @@ + +void alphaTest(inout vec4 color) { + if( MGL_GREATER == mgl_AlphaTestFunc ) { + if ( color.a <= mgl_AlphaTestRef ) { + DISCARD(color); + } + } else if( MGL_LESS == mgl_AlphaTestFunc ) { + if ( color.a >= mgl_AlphaTestRef ) { + DISCARD(color); + } + } else if( MGL_LEQUAL == mgl_AlphaTestFunc ) { + if ( color.a > mgl_AlphaTestRef ) { + DISCARD(color); + } + } else if( MGL_GEQUAL == mgl_AlphaTestFunc ) { + if ( color.a < mgl_AlphaTestRef ) { + DISCARD(color); + } + } else if( MGL_EQUAL == mgl_AlphaTestFunc ) { + if ( abs( color.a - mgl_AlphaTestRef ) > EPSILON ) { + DISCARD(color); + } + } else if( MGL_NOTEQUAL == mgl_AlphaTestFunc ) { + if ( abs( color.a - mgl_AlphaTestRef ) <= EPSILON ) { + DISCARD(color); + } + } else if( MGL_NEVER == mgl_AlphaTestFunc ) { + DISCARD(color); + } /* else if( MGL_ALWAYS == mgl_AlphaTestFunc ) { + // NOP + } */ +} + diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_attribute.glsl b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_attribute.glsl index 09a11ec95..f670f7b77 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_attribute.glsl +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_attribute.glsl @@ -4,16 +4,22 @@ #include es_precision.glsl -attribute HIGHP vec4 mgl_Vertex; -attribute HIGHP vec4 mgl_Normal; -attribute HIGHP vec4 mgl_Color; -attribute HIGHP vec4 mgl_MultiTexCoord0; -attribute HIGHP vec4 mgl_MultiTexCoord1; -attribute HIGHP vec4 mgl_MultiTexCoord2; -attribute HIGHP vec4 mgl_MultiTexCoord3; -attribute HIGHP vec4 mgl_MultiTexCoord4; -attribute HIGHP vec4 mgl_MultiTexCoord5; -attribute HIGHP vec4 mgl_MultiTexCoord6; -attribute HIGHP vec4 mgl_MultiTexCoord7; +attribute vec4 mgl_Vertex; +attribute vec4 mgl_Normal; +attribute vec4 mgl_Color; +#if MAX_TEXTURE_UNITS >= 2 +attribute vec4 mgl_MultiTexCoord0; +attribute vec4 mgl_MultiTexCoord1; +#endif +#if MAX_TEXTURE_UNITS >= 4 +attribute vec4 mgl_MultiTexCoord2; +attribute vec4 mgl_MultiTexCoord3; +#endif +#if MAX_TEXTURE_UNITS >= 8 +attribute vec4 mgl_MultiTexCoord4; +attribute vec4 mgl_MultiTexCoord5; +attribute vec4 mgl_MultiTexCoord6; +attribute vec4 mgl_MultiTexCoord7; +#endif #endif // mgl_attribute_glsl diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_const.glsl b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_const.glsl index 1a464a1cb..4f97292e3 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_const.glsl +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_const.glsl @@ -4,7 +4,36 @@ #include es_precision.glsl -const LOWP int MAX_TEXTURE_UNITS = 8; // <=gl_MaxTextureImageUnits +// will be defined at runtime: MAX_TEXTURE_UNITS [0|2|4|8] const LOWP int MAX_LIGHTS = 8; +const float EPSILON = 0.0000001; // FIXME: determine proper hw-precision + +// discard freezes NV tegra2 compiler (STILL TRUE?) +// #define DISCARD(c) (c.a = 0.0) +#define DISCARD(c) discard + +// Texture Environment / Multi Texturing +#define MGL_ADD 1 +#define MGL_MODULATE 2 +#define MGL_DECAL 3 +#define MGL_BLEND 4 +#define MGL_REPLACE 5 +#define MGL_COMBINE 6 + +// Alpha Test +#define MGL_NEVER 1 +#define MGL_LESS 2 +#define MGL_EQUAL 3 +#define MGL_LEQUAL 4 +#define MGL_GREATER 5 +#define MGL_NOTEQUAL 6 +#define MGL_GEQUAL 7 +#define MGL_ALWAYS 8 + +// Cull Face +#define MGL_FRONT 1 +#define MGL_BACK 2 +#define MGL_FRONT_AND_BACK 3 + #endif // mgl_const_glsl diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_lightdef.glsl b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_lightdef.glsl index 98e214139..deaf95408 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_lightdef.glsl +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_lightdef.glsl @@ -1,6 +1,9 @@ #ifndef mgl_lightdef_glsl #define mgl_lightdef_glsl +struct mgl_LightModelParameters { + vec4 ambient; +}; struct mgl_LightSourceParameters { vec4 ambient; vec4 diffuse; diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_settexcoord.vp b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_settexcoord.vp index 1efe328d0..cbf0db642 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_settexcoord.vp +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_settexcoord.vp @@ -22,14 +22,20 @@ void setTexCoord(in vec4 defpos) { mgl_TexCoords[7] = ( 0 != (mgl_TexCoordEnabled & 128) ) ? mgl_MultiTexCoord7 : defpos; */ + #if MAX_TEXTURE_UNITS >= 2 mgl_TexCoords[0] = ( 0 != mgl_TexCoordEnabled[0] ) ? mgl_MultiTexCoord0 : defpos; mgl_TexCoords[1] = ( 0 != mgl_TexCoordEnabled[1] ) ? mgl_MultiTexCoord1 : defpos; + #endif + #if MAX_TEXTURE_UNITS >= 4 mgl_TexCoords[2] = ( 0 != mgl_TexCoordEnabled[2] ) ? mgl_MultiTexCoord2 : defpos; mgl_TexCoords[3] = ( 0 != mgl_TexCoordEnabled[3] ) ? mgl_MultiTexCoord3 : defpos; + #endif + #if MAX_TEXTURE_UNITS >= 8 mgl_TexCoords[4] = ( 0 != mgl_TexCoordEnabled[4] ) ? mgl_MultiTexCoord4 : defpos; mgl_TexCoords[5] = ( 0 != mgl_TexCoordEnabled[5] ) ? mgl_MultiTexCoord5 : defpos; mgl_TexCoords[6] = ( 0 != mgl_TexCoordEnabled[6] ) ? mgl_MultiTexCoord6 : defpos; mgl_TexCoords[7] = ( 0 != mgl_TexCoordEnabled[7] ) ? mgl_MultiTexCoord7 : defpos; + #endif } #endif // mgl_settexcoord_vp diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform.glsl b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform.glsl index 4c4000dfa..5029e4bd8 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform.glsl +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform.glsl @@ -6,12 +6,45 @@ #include mgl_const.glsl -uniform HIGHP mat4 mgl_PMVMatrix[4]; // P, Mv, Mvi and Mvit (transpose(inverse(ModelView)) == normalMatrix) +uniform mat4 mgl_PMVMatrix[4]; // P, Mv, Mvi and Mvit (transpose(inverse(ModelView)) == normalMatrix) uniform LOWP int mgl_ColorEnabled; -uniform HIGHP vec4 mgl_ColorStatic; +uniform vec4 mgl_ColorStatic; +uniform LOWP int mgl_AlphaTestFunc; +uniform float mgl_AlphaTestRef; + +// [0].rgba: size, smooth, attnMinSz, attnMaxSz +// [1].rgba: attnCoeff(3), attnFadeTs +uniform MEDIUMP vec4 mgl_PointParams[2]; + +#define pointSize (mgl_PointParams[0].r) +#define pointSmooth (mgl_PointParams[0].g) +#define pointSizeMin (mgl_PointParams[0].b) +#define pointSizeMax (mgl_PointParams[0].a) +#define pointDistanceConstantAtten (mgl_PointParams[1].r) +#define pointDistanceLinearAtten (mgl_PointParams[1].g) +#define pointDistanceQuadraticAtten (mgl_PointParams[1].b) +#define pointFadeThresholdSize (mgl_PointParams[1].a) + +// uniform LOWP int mgl_CullFace; // ES2 supports CullFace implicit .. +#if MAX_TEXTURE_UNITS > 0 +uniform LOWP int mgl_TextureEnabled[MAX_TEXTURE_UNITS]; uniform LOWP int mgl_TexCoordEnabled[MAX_TEXTURE_UNITS]; -uniform sampler2D mgl_ActiveTexture; -uniform LOWP int mgl_ActiveTextureIdx; -uniform LOWP int mgl_CullFace; +uniform LOWP int mgl_TexEnvMode[MAX_TEXTURE_UNITS]; +uniform LOWP int mgl_TexFormat[MAX_TEXTURE_UNITS]; +#if MAX_TEXTURE_UNITS >= 2 +uniform sampler2D mgl_Texture0; +uniform sampler2D mgl_Texture1; +#endif +#if MAX_TEXTURE_UNITS >= 4 +uniform sampler2D mgl_Texture2; +uniform sampler2D mgl_Texture3; +#endif +#if MAX_TEXTURE_UNITS >= 8 +uniform sampler2D mgl_Texture4; +uniform sampler2D mgl_Texture5; +uniform sampler2D mgl_Texture6; +uniform sampler2D mgl_Texture7; +#endif +#endif #endif // mgl_uniform_glsl diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform_light.glsl b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform_light.glsl index 0dedb5d5d..5b34fd9cf 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform_light.glsl +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_uniform_light.glsl @@ -9,6 +9,7 @@ uniform LOWP int mgl_LightsEnabled[MAX_LIGHTS]; +uniform mgl_LightModelParameters mgl_LightModel; uniform mgl_LightSourceParameters mgl_LightSource[MAX_LIGHTS]; uniform mgl_MaterialParameters mgl_FrontMaterial; diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_varying.glsl b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_varying.glsl index fc9f735d1..599ac4a53 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_varying.glsl +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/shaders/mgl_varying.glsl @@ -7,6 +7,8 @@ #include mgl_const.glsl varying vec4 frontColor; +#if MAX_TEXTURE_UNITS > 0 varying vec4 mgl_TexCoords[MAX_TEXTURE_UNITS]; +#endif #endif // mgl_varying_glsl diff --git a/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java b/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java new file mode 100644 index 000000000..45087ea2c --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/jpeg/JPEGDecoder.java @@ -0,0 +1,1517 @@ +/** + * Original JavaScript code from <https://github.com/notmasteryet/jpgjs/blob/master/jpg.js>, + * ported to Java for JogAmp Community. + * + * Enhancements: + * * InputStream instead of memory buffer + * * User provided memory handler + * * Fixed JPEG Component ID/Index mapping + * * Color space conversion (YCCK, CMYK -> RGB) + * * More error tolerant + * + * ***************** + * + * Copyright 2011 notmasteryet + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ***************** + * + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl.util.jpeg; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; + +import jogamp.opengl.Debug; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.common.util.Bitstream; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.opengl.util.texture.TextureData; +import com.jogamp.opengl.util.texture.TextureData.ColorSpace; + +/** + * + * <ul> + * <li> The JPEG specification can be found in the ITU CCITT Recommendation T.81 + * (www.w3.org/Graphics/JPEG/itu-t81.pdf) </li> + * <li> The JFIF specification can be found in the JPEG File Interchange Format + * (www.w3.org/Graphics/JPEG/jfif3.pdf)</li> + * <li> The Adobe Application-Specific JPEG markers in the Supporting the DCT Filters + * in PostScript Level 2, Technical Note #5116 + * (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf)</li> + * <li> http://halicery.com/jpeg/huffman.html </li> + * <li> https://en.wikipedia.org/wiki/Jpg#Syntax_and_structure </li> + * <li> http://www.cs.sfu.ca/CourseCentral/365/mark/material/notes/Chap4/Chap4.2/Chap4.2.html </li> + * <li> https://github.com/notmasteryet/jpgjs/blob/master/jpg.js </li> + * </ul> + */ +public class JPEGDecoder { + private static final boolean DEBUG = Debug.debug("JPEGImage"); + private static final boolean DEBUG_IN = false; + + /** Allows user to hook a {@link ColorSink} to another toolkit to produce {@link TextureData}. */ + public static interface ColorSink { + /** + * @param width + * @param height + * @param sourceCS the color-space of the decoded JPEG + * @param sourceComponents number of components used for the given source color-space + * @return Either {@link TextureData.ColorSpace#RGB} or {@link TextureData.ColorSpace#YCbCr}. {@link TextureData.ColorSpace#YCCK} and {@link TextureData.ColorSpace#CMYK} will throw an exception! + * @throws RuntimeException + */ + public TextureData.ColorSpace allocate(int width, int height, TextureData.ColorSpace sourceCS, int sourceComponents) throws RuntimeException; + public void store2(int x, int y, byte c1, byte c2); + public void storeRGB(int x, int y, byte r, byte g, byte b); + public void storeYCbCr(int x, int y, byte Y, byte Cb, byte Cr); + } + + public static class JFIF { + final VersionNumber version; + final int densityUnits; + final int xDensity; + final int yDensity; + final int thumbWidth; + final int thumbHeight; + final byte[] thumbData; + + private JFIF(final byte data[]) { + version = new VersionNumber(data[5], data[6], 0); + densityUnits = data[7]; + xDensity = (data[8] << 8) | data[9]; + yDensity = (data[10] << 8) | data[11]; + thumbWidth = data[12]; + thumbHeight = data[13]; + if( 0 < thumbWidth && 0 < thumbHeight ) { + final int len = 14 + 3 * thumbWidth * thumbHeight; + thumbData = new byte[len]; + System.arraycopy(data, 14, thumbData, 0, len); + } else { + thumbData = null; + } + } + + public static final JFIF get(final byte[] data) throws RuntimeException { + if ( data[0] == (byte)0x4A && data[1] == (byte)0x46 && data[2] == (byte)0x49 && + data[3] == (byte)0x46 && data[4] == (byte)0x0) { // 'JFIF\x00' + final JFIF r = new JFIF(data); + return r; + } else { + return null; + } + } + + @Override + public final String toString() { + return "JFIF[ver "+version+", density[units "+densityUnits+", "+xDensity+"x"+yDensity+"], thumb "+thumbWidth+"x"+thumbHeight+"]"; + } + } + + public static class Adobe { + final short version; + final short flags0; + final short flags1; + final short colorCode; + final ColorSpace colorSpace; + + private Adobe(final byte[] data) { + version = data[6]; + flags0 = (short) ( (data[7] << 8) | data[8] ) ; + flags1 = (short) ( (data[9] << 8) | data[10] ) ; + colorCode = data[11]; + switch( colorCode ) { + case 2: colorSpace = ColorSpace.YCCK; break; + case 1: colorSpace = ColorSpace.YCbCr; break; + default: colorSpace = ColorSpace.CMYK; break; + } + } + public static final Adobe get(final byte[] data) throws RuntimeException { + if (data[0] == (byte)0x41 && data[1] == (byte)0x64 && data[2] == (byte)0x6F && + data[3] == (byte)0x62 && data[4] == (byte)0x65 && data[5] == (byte)0) { // 'Adobe\x00' + final Adobe r = new Adobe(data); + return r; + } else { + return null; + } + } + @Override + public final String toString() { + return "Adobe[ver "+version+", flags["+toHexString(flags0)+", "+toHexString(flags1)+"], colorSpace/Code "+colorSpace+"/"+toHexString(colorCode)+"]"; + } + } + /** TODO */ + public static class EXIF { + private EXIF(final byte data[]) { + } + + public static final EXIF get(final byte[] data) throws RuntimeException { + if ( data[0] == (byte)0x45 && data[1] == (byte)0x78 && data[2] == (byte)0x69 && + data[3] == (byte)0x66 && data[4] == (byte)0x0) { // 'Exif\x00' + final EXIF r = new EXIF(data); + return r; + } else { + return null; + } + } + @Override + public final String toString() { + return "EXIF[]"; + } + } + + @SuppressWarnings("serial") + public static class CodecException extends RuntimeException { + CodecException(String message) { + super(message); + } + } + @SuppressWarnings("serial") + public static class MarkerException extends CodecException { + final int marker; + MarkerException(int marker, String message) { + super(message+" - Marker "+toHexString(marker)); + this.marker = marker; + } + public int getMarker() { return marker; } + } + + /** Start of Image */ + private static final int M_SOI = 0xFFD8; + /** End of Image */ + private static final int M_EOI = 0xFFD9; + /** Start of Frame - Baseline DCT */ + private static final int M_SOF0 = 0xFFC0; + /** Start of Frame - Extended sequential DCT */ + // private static final int M_SOF1 = 0xFFC1; + /** Start of Frame - Progressive DCT */ + private static final int M_SOF2 = 0xFFC2; + /** DHT (Define Huffman Tables) */ + private static final int M_DHT = 0xFFC4; + // private static final int M_DAC = 0xFFCC; + /** SOS (Start of Scan) */ + private static final int M_SOS = 0xFFDA; + /** DQT (Define Quantization Tables) */ + private static final int M_QTT = 0xFFDB; + /** DRI (Define Restart Interval) */ + private static final int M_DRI = 0xFFDD; + /** APP0 (Application Specific) - JFIF Header */ + private static final int M_APP00 = 0xFFE0; + /** APP1 (Application Specific) - Exif Header */ + private static final int M_APP01 = 0xFFE1; + /** APP2 (Application Specific) */ + private static final int M_APP02 = 0xFFE2; + /** APP3 (Application Specific) */ + private static final int M_APP03 = 0xFFE3; + /** APP4 (Application Specific) */ + private static final int M_APP04 = 0xFFE4; + /** APP5 (Application Specific) */ + private static final int M_APP05 = 0xFFE5; + /** APP6 (Application Specific) */ + private static final int M_APP06 = 0xFFE6; + /** APP7 (Application Specific) */ + private static final int M_APP07 = 0xFFE7; + /** APP8 (Application Specific) */ + private static final int M_APP08 = 0xFFE8; + /** APP9 (Application Specific) */ + private static final int M_APP09 = 0xFFE9; + /** APP10 (Application Specific) */ + private static final int M_APP10 = 0xFFEA; + /** APP11 (Application Specific) */ + private static final int M_APP11 = 0xFFEB; + /** APP12 (Application Specific) */ + private static final int M_APP12 = 0xFFEC; + /** APP13 (Application Specific) */ + private static final int M_APP13 = 0xFFED; + /** APP14 (Application Specific) - ADOBE Header */ + private static final int M_APP14 = 0xFFEE; + /** APP15 (Application Specific) */ + private static final int M_APP15 = 0xFFEF; + + /** Annotation / Comment */ + private static final int M_ANO = 0xFFFE; + + static final int[] dctZigZag = new int[] { + 0, + 1, 8, + 16, 9, 2, + 3, 10, 17, 24, + 32, 25, 18, 11, 4, + 5, 12, 19, 26, 33, 40, + 48, 41, 34, 27, 20, 13, 6, + 7, 14, 21, 28, 35, 42, 49, 56, + 57, 50, 43, 36, 29, 22, 15, + 23, 30, 37, 44, 51, 58, + 59, 52, 45, 38, 31, + 39, 46, 53, 60, + 61, 54, 47, + 55, 62, + 63 + }; + + static final int dctCos1 = 4017; // cos(pi/16) + static final int dctSin1 = 799; // sin(pi/16) + static final int dctCos3 = 3406; // cos(3*pi/16) + static final int dctSin3 = 2276; // sin(3*pi/16) + static final int dctCos6 = 1567; // cos(6*pi/16) + static final int dctSin6 = 3784; // sin(6*pi/16) + static final int dctSqrt2 = 5793; // sqrt(2) + static final int dctSqrt1d2 = 2896; // sqrt(2) / 2 + + static class Frame { + final boolean progressive; + final int precision; + final int scanLines; + final int samplesPerLine; + 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, int[][] qtt) { + this.progressive = progressive; + this.precision = precision; + this.scanLines = scanLines; + this.samplesPerLine = samplesPerLine; + compIDs = new ArrayHashSet<Integer>(componentsCount); + comps = new ComponentIn[componentsCount]; + this.compCount = componentsCount; + this.qtt = qtt; + } + + private final void checkBounds(int idx) { + if( 0 > idx || idx >= compCount ) { + 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; } + + public final void putOrdered(int compID, ComponentIn component) { + if( maxCompID < compID ) { + maxCompID = compID; + } + final int idx = compIDs.size(); + checkBounds(idx); + compIDs.add(compID); + comps[idx] = component; + } + public final ComponentIn getCompByIndex(int i) { + checkBounds(i); + return comps[i]; + } + public final ComponentIn getCompByID(int componentID) { + return getCompByIndex( compIDs.indexOf(componentID) ); + } + public final int getCompID(int idx) { + return compIDs.get(idx); + } + public final boolean hasCompID(int componentID) { + return compIDs.contains(componentID); + } + @Override + public final String toString() { + return "Frame[progressive "+progressive+", precision "+precision+", scanLines "+scanLines+", samplesPerLine "+samplesPerLine+ + ", components[count "+compCount+", maxID "+maxCompID+", componentIDs "+compIDs+", comps "+Arrays.asList(comps)+"]]"; + } + } + + /** The JPEG encoded components */ + class ComponentIn { + final int h, v; + /** index to frame.qtt[] */ + final int qttIdx; + int blocksPerColumn; + int blocksPerColumnForMcu; + int blocksPerLine; + int blocksPerLineForMcu; + /** [blocksPerColumnForMcu][blocksPerLineForMcu][64]; */ + int[][][] blocks; + int pred; + BinObj huffmanTableAC; + BinObj huffmanTableDC; + + ComponentIn(int h, int v, int qttIdx) { + this.h = h; + this.v = v; + this.qttIdx = qttIdx; + } + + public final void allocateBlocks(int blocksPerColumn, int blocksPerColumnForMcu, int blocksPerLine, int blocksPerLineForMcu) { + this.blocksPerColumn = blocksPerColumn; + this.blocksPerColumnForMcu = blocksPerColumnForMcu; + this.blocksPerLine = blocksPerLine; + this.blocksPerLineForMcu = blocksPerLineForMcu; + this.blocks = new int[blocksPerColumnForMcu][blocksPerLineForMcu][64]; + } + public final int[] getBlock(int row, int col) { + if( row >= blocksPerColumnForMcu || col >= blocksPerLineForMcu ) { + throw new CodecException("Out of bounds given ["+row+"]["+col+"] - "+this); + } + return blocks[row][col]; + } + + @Override + public final String toString() { + return "CompIn[h "+h+", v "+v+", qttIdx "+qttIdx+", blocks["+blocksPerColumn+", mcu "+blocksPerColumnForMcu+"]["+blocksPerLine+", mcu "+blocksPerLineForMcu+"][64]]"; + } + } + + /** The decoded components */ + class ComponentOut { + private final ArrayList<byte[]> lines; + final float scaleX; + final float scaleY; + + ComponentOut(ArrayList<byte[]> lines, float scaleX, float scaleY) { + this.lines = lines; + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + /** Safely returning a line, if index exceeds number of lines, last line is returned. */ + public final byte[] getLine(int i) { + final int sz = lines.size(); + return lines.get( i < sz ? i : sz - 1); + } + + @Override + public final String toString() { + return "CompOut[lines "+lines.size()+", scale "+scaleX+"x"+scaleY+"]"; + } + } + + @Override + public String toString() { + final String jfifS = null != jfif ? jfif.toString() : "JFIF nil"; + final String exifS = null != exif ? exif.toString() : "Exif nil"; + final String adobeS = null != adobe ? adobe.toString() : "Adobe nil"; + final String compOuts = null != components ? Arrays.asList(components).toString() : "nil"; + return "JPEG[size "+width+"x"+height+", compOut "+compOuts+", "+jfifS+", "+exifS+", "+adobeS+"]"; + } + + private final Bitstream<InputStream> bstream = new Bitstream<InputStream>(new Bitstream.ByteInputStream(null), false /* outputMode */); + + private int width = 0; + private int height = 0; + private JFIF jfif = null; + private EXIF exif = null; + private Adobe adobe = null; + private ComponentOut[] components = null; + + public final JFIF getJFIFHeader() { return jfif; } + public final EXIF getEXIFHeader() { return exif; } + public final Adobe getAdobeHeader() { return adobe; } + public final int getWidth() { return width; } + public final int getHeight() { return height; } + + private final void setStream(InputStream is) { + try { + bstream.setStream(is, false /* outputMode */); + } catch (Exception e) { + throw new RuntimeException(e); // should not happen, no flush() + } + } + + private final int readUInt8() throws IOException { + return bstream.readUInt8(true /* msbFirst */); + } + + private final int readUInt16() throws IOException { + return bstream.readUInt16(true /* msbFirst */, true /* bigEndian */); + } + + private final int readNumber() throws IOException { + final int len=readUInt16(); + if(len!=4){ + throw new CodecException("ERROR: Define number format error [Len!=4, but "+len+"]"); + } + return readUInt16(); + } + + private final byte[] readDataBlock() throws IOException { + int count=0, i=0; + final int len=readUInt16(); count+=2; + byte[] data = new byte[len-2]; + while(count<len){ + data[i++] = (byte)readUInt8(); count++; + } + if(DEBUG_IN) { System.err.println("JPEG.readDataBlock: net-len "+(len-2)+", "+this); dumpData(data, 0, len-2); } + return data; + } + static final void dumpData(byte[] data, int offset, int len) { + for(int i=0; i<len; ) { + System.err.print(i%8+": "); + for(int j=0; j<8 && i<len; j++, i++) { + System.err.print(toHexString(0x000000FF & data[offset+i])+", "); + } + System.err.println(""); + } + } + + public synchronized void clear(InputStream inputStream) { + setStream(inputStream); + width = 0; + height = 0; + jfif = null; + exif = null; + adobe = null; + components = null; + } + public synchronized JPEGDecoder parse(final InputStream inputStream) throws IOException { + clear(inputStream); + + 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)); + } + + fileMarker = readUInt16(); + while (fileMarker != M_EOI) { + if(DEBUG) { System.err.println("JPG.parse got marker "+toHexString(fileMarker)); } + switch(fileMarker) { + case M_APP00: + case M_APP01: + case M_APP02: + case M_APP03: + case M_APP04: + case M_APP05: + case M_APP06: + case M_APP07: + case M_APP08: + case M_APP09: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + case M_ANO: { + final byte[] appData = readDataBlock(); + + if ( fileMarker == M_APP00 ) { + jfif = JFIF.get( appData ); + } + if ( fileMarker == M_APP01 ) { + exif = EXIF.get(appData); + } + if (fileMarker == M_APP14) { + adobe = Adobe.get(appData); + } + fileMarker = 0; // consumed and get-next + } + break; + + case M_QTT: { + int count = 0; + final int quantizationTablesLength = readUInt16(); count+=2; + 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++) { + final int z = dctZigZag[j]; + tableData[z] = readUInt8(); count++; + } + } else if ( precisionID == 1) { //16 bit + for (int j = 0; j < 64; j++) { + final int z = dctZigZag[j]; + tableData[z] = readUInt16(); count+=2; + } + } else { + throw new CodecException("DQT: invalid table precision "+precisionID+", quantizationTableSpec "+quantizationTableSpec+", idx "+tableIdx); + } + 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]: "+count+"/"+quantizationTablesLength); + } + fileMarker = 0; // consumed and get-next + } + break; + + 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; + { + final boolean progressive = (fileMarker == M_SOF2); + final int precision = readUInt8(); count++; + final int scanLines = readUInt16(); count+=2; + final int samplesPerLine = readUInt16(); count+=2; + componentsCount = readUInt8(); count++; + frame = new Frame(progressive, precision, scanLines, samplesPerLine, componentsCount, quantizationTables); + width = frame.samplesPerLine; + height = frame.scanLines; + } + for (int i = 0; i < componentsCount; i++) { + final int componentId = readUInt8(); count++; + final int temp = readUInt8(); count++; + final int h = temp >> 4; + final int v = temp & 0x0F; + 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); // JAU: max 1-frame + if(DEBUG) { System.err.println("JPG.parse.SOF[02]: Got frame "+frame); } + fileMarker = 0; // consumed and get-next + } + break; + + case M_DHT: { + int count = 0; + final int huffmanLength = readUInt16(); count+=2; + int i=count, codeLengthTotal = 0; + while( i < huffmanLength ) { + final int huffmanTableSpec = readUInt8(); count++; + final int[] codeLengths = new int[16]; + int codeLengthSum = 0; + for (int j = 0; j < 16; j++) { + codeLengthSum += (codeLengths[j] = readUInt8()); count++; + } + final byte[] huffmanValues = new byte[codeLengthSum]; + for (int j = 0; j < codeLengthSum; j++) { + huffmanValues[j] = (byte)readUInt8(); count++; + } + codeLengthTotal += codeLengthSum; + i += 17 + codeLengthSum; + final BinObj[] table = ( huffmanTableSpec >> 4 ) == 0 ? huffmanTablesDC : huffmanTablesAC; + table[huffmanTableSpec & 0x0F] = buildHuffmanTable(codeLengths, huffmanValues); + } + if(count!=huffmanLength || i!=count){ + throw new CodecException("ERROR: Huffman table format error [count!=Length]"); + } + if(DEBUG) { System.err.println("JPG.parse.DHT: Got Huffman CodeLengthTotal "+codeLengthTotal); } + fileMarker = 0; // consumed and get-next + } + break; + + case M_DRI: + resetInterval = readNumber(); + if(DEBUG) { System.err.println("JPG.parse.DRI: Got Reset Interval "+resetInterval); } + fileMarker = 0; // consumed and get-next + break; + + case M_SOS: { + int count = 0; + final int sosLen = readUInt16(); count+=2; + final int selectorsCount = readUInt8(); count++; + ArrayList<ComponentIn> components = new ArrayList<ComponentIn>(); + if(DEBUG) { System.err.println("JPG.parse.SOS: selectorCount [0.."+(selectorsCount-1)+"]: "+frame); } + for (int i = 0; i < selectorsCount; i++) { + final int compID = readUInt8(); count++; + final ComponentIn component = frame.getCompByID(compID); + final int tableSpec = readUInt8(); count++; + component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4]; + component.huffmanTableAC = huffmanTablesAC[tableSpec & 15]; + components.add(component); + } + final int spectralStart = readUInt8(); count++; + final int spectralEnd = readUInt8(); count++; + final int successiveApproximation = readUInt8(); count++; + if(count!=sosLen){ + throw new CodecException("ERROR: scan header format error [count!=Length]"); + } + fileMarker = decoder.decodeScan(frame, components, resetInterval, + spectralStart, spectralEnd, + successiveApproximation >> 4, successiveApproximation & 15); + if(DEBUG) { System.err.println("JPG.parse.SOS.decode result "+toHexString(fileMarker)); } + } + break; + default: + /** + if (data[offset - 3] == 0xFF && + data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) { + // could be incorrect encoding -- last 0xFF byte of the previous + // block was eaten by the encoder + offset -= 3; + break; + } */ + throw new CodecException("unknown JPEG marker " + toHexString(fileMarker) + ", " + bstream); + } + if( 0 == fileMarker ) { + fileMarker = readUInt16(); + } + } + 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 "+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 ); + } + if(DEBUG) { System.err.println("JPG.parse.X: End of processing input "+this); } + return this; + } + + private void prepareComponents(Frame frame) { + int maxH = 0, maxV = 0; + // for (componentId in frame.components) { + final int compCount = frame.getCompCount(); + for (int i=0; i<compCount; i++) { + final ComponentIn component = frame.getCompByIndex(i); + if (maxH < component.h) maxH = component.h; + if (maxV < component.v) maxV = component.v; + } + int mcusPerLine = (int) Math.ceil(frame.samplesPerLine / 8f / maxH); + int mcusPerColumn = (int) Math.ceil(frame.scanLines / 8f / maxV); + // for (componentId in frame.components) { + for (int i=0; i<compCount; i++) { + final ComponentIn component = frame.getCompByIndex(i); + final int blocksPerLine = (int) Math.ceil(Math.ceil(frame.samplesPerLine / 8f) * component.h / maxH); + final int blocksPerColumn = (int) Math.ceil(Math.ceil(frame.scanLines / 8f) * component.v / maxV); + final int blocksPerLineForMcu = mcusPerLine * component.h; + final int blocksPerColumnForMcu = mcusPerColumn * component.v; + component.allocateBlocks(blocksPerColumn, blocksPerColumnForMcu, blocksPerLine, blocksPerLineForMcu); + } + frame.maxH = maxH; + frame.maxV = maxV; + frame.mcusPerLine = mcusPerLine; + frame.mcusPerColumn = mcusPerColumn; + } + + private static class BinObjIdxed { + final BinObj children; + byte index; + BinObjIdxed() { + this.children = new BinObj(); + this.index = 0; + } + } + private static class BinObj { + final boolean isValue; + final BinObj[] tree; + final byte b; + + BinObj(byte b) { + this.isValue= true; + this.b = b; + this.tree = null; + } + BinObj() { + this.isValue= false; + this.b = (byte)0; + this.tree = new BinObj[2]; + } + final byte getValue() { return b; } + final BinObj get(int i) { return tree[i]; } + final void set(byte i, byte v) { tree[i] = new BinObj(v); } + final void set(byte i, BinObj v) { tree[i] = v; } + } + + private BinObj buildHuffmanTable(int[] codeLengths, byte[] values) { + int k = 0; + int length = 16; + final ArrayList<BinObjIdxed> code = new ArrayList<BinObjIdxed>(); + while (length > 0 && 0==codeLengths[length - 1]) { + length--; + } + code.add(new BinObjIdxed()); + BinObjIdxed p = code.get(0), q; + for (int i = 0; i < length; i++) { + for (int j = 0; j < codeLengths[i]; j++) { + p = code.remove(code.size()-1); + p.children.set(p.index, values[k]); + while (p.index > 0) { + p = code.remove(code.size()-1); + } + p.index++; + code.add(p); + while (code.size() <= i) { + q = new BinObjIdxed(); + code.add(q); + p.children.set(p.index, q.children); + p = q; + } + k++; + } + if (i + 1 < length) { + // p here points to last code + q = new BinObjIdxed(); + code.add(q); + p.children.set(p.index, q.children); + p = q; + } + } + return code.get(0).children; + } + + private final Output output = new Output(); + private static class Output { + private int blocksPerLine; + private int blocksPerColumn; + private int samplesPerLine; + + private ArrayList<byte[]> buildComponentData(Frame frame, ComponentIn component) { + ArrayList<byte[]> lines = new ArrayList<byte[]>(); + blocksPerLine = component.blocksPerLine; + blocksPerColumn = component.blocksPerColumn; + samplesPerLine = blocksPerLine << 3; + final int[] R = new int[64]; + final byte[] r = new byte[64]; + + for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) { + 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++) { + // 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; + for (int j = 0; j < 8; j++) { + final byte[] line = lines.get(scanLine + j); + for (int i = 0; i < 8; i++) + line[sample + i] = r[offset++]; + } + } + } + return lines; + } + + // A port of poppler's IDCT method which in turn is taken from: + // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, + // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", + // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, + // 988-991. + private void quantizeAndInverse(int[] zz, byte[] dataOut, int[] dataIn, int[] qt) { + int v0, v1, v2, v3, v4, v5, v6, v7, t; + int[] p = dataIn; + int i; + + // dequant + for (i = 0; i < 64; i++) { + p[i] = zz[i] * qt[i]; + } + + // inverse DCT on rows + for (i = 0; i < 8; ++i) { + int row = 8 * i; + + // check for all-zero AC coefficients + if (p[1 + row] == 0 && p[2 + row] == 0 && p[3 + row] == 0 && + p[4 + row] == 0 && p[5 + row] == 0 && p[6 + row] == 0 && + p[7 + row] == 0) { + t = (dctSqrt2 * p[0 + row] + 512) >> 10; + p[0 + row] = t; + p[1 + row] = t; + p[2 + row] = t; + p[3 + row] = t; + p[4 + row] = t; + p[5 + row] = t; + p[6 + row] = t; + p[7 + row] = t; + continue; + } + + // stage 4 + v0 = (dctSqrt2 * p[0 + row] + 128) >> 8; + v1 = (dctSqrt2 * p[4 + row] + 128) >> 8; + v2 = p[2 + row]; + v3 = p[6 + row]; + v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8; + v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8; + v5 = p[3 + row] << 4; + v6 = p[5 + row] << 4; + + // stage 3 + t = (v0 - v1+ 1) >> 1; + v0 = (v0 + v1 + 1) >> 1; + v1 = t; + t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8; + v3 = t; + t = (v4 - v6 + 1) >> 1; + v4 = (v4 + v6 + 1) >> 1; + v6 = t; + t = (v7 + v5 + 1) >> 1; + v5 = (v7 - v5 + 1) >> 1; + v7 = t; + + // stage 2 + t = (v0 - v3 + 1) >> 1; + v0 = (v0 + v3 + 1) >> 1; + v3 = t; + t = (v1 - v2 + 1) >> 1; + v1 = (v1 + v2 + 1) >> 1; + v2 = t; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p[0 + row] = v0 + v7; + p[7 + row] = v0 - v7; + p[1 + row] = v1 + v6; + p[6 + row] = v1 - v6; + p[2 + row] = v2 + v5; + p[5 + row] = v2 - v5; + p[3 + row] = v3 + v4; + p[4 + row] = v3 - v4; + } + + // inverse DCT on columns + for (i = 0; i < 8; ++i) { + int col = i; + + // check for all-zero AC coefficients + if (p[1*8 + col] == 0 && p[2*8 + col] == 0 && p[3*8 + col] == 0 && + p[4*8 + col] == 0 && p[5*8 + col] == 0 && p[6*8 + col] == 0 && + p[7*8 + col] == 0) { + t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14; + p[0*8 + col] = t; + p[1*8 + col] = t; + p[2*8 + col] = t; + p[3*8 + col] = t; + p[4*8 + col] = t; + p[5*8 + col] = t; + p[6*8 + col] = t; + p[7*8 + col] = t; + continue; + } + + // stage 4 + v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12; + v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12; + v2 = p[2*8 + col]; + v3 = p[6*8 + col]; + v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12; + v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12; + v5 = p[3*8 + col]; + v6 = p[5*8 + col]; + + // stage 3 + t = (v0 - v1 + 1) >> 1; + v0 = (v0 + v1 + 1) >> 1; + v1 = t; + t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12; + v3 = t; + t = (v4 - v6 + 1) >> 1; + v4 = (v4 + v6 + 1) >> 1; + v6 = t; + t = (v7 + v5 + 1) >> 1; + v5 = (v7 - v5 + 1) >> 1; + v7 = t; + + // stage 2 + t = (v0 - v3 + 1) >> 1; + v0 = (v0 + v3 + 1) >> 1; + v3 = t; + t = (v1 - v2 + 1) >> 1; + v1 = (v1 + v2 + 1) >> 1; + v2 = t; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p[0*8 + col] = v0 + v7; + p[7*8 + col] = v0 - v7; + p[1*8 + col] = v1 + v6; + p[6*8 + col] = v1 - v6; + p[2*8 + col] = v2 + v5; + p[5*8 + col] = v2 - v5; + p[3*8 + col] = v3 + v4; + p[4*8 + col] = v3 - v4; + } + + // convert to 8-bit integers + for (i = 0; i < 64; ++i) { + int sample = 128 + ((p[i] + 8) >> 4); + dataOut[i] = (byte) ( sample < 0 ? 0 : sample > 0xFF ? 0xFF : sample ); + } + } + } + + private static interface DecoderFunction { + void decode(ComponentIn component, int[] zz) throws IOException; + } + + private class Decoder { + // private int precision; + // private int samplesPerLine; + // private int scanLines; + private int mcusPerLine; + private boolean progressive; + // private int maxH, maxV; + private int spectralStart, spectralEnd; + private int successive; + private int eobrun; + private int successiveACState, successiveACNextValue; + + private int decodeScan(Frame frame, ArrayList<ComponentIn> components, int resetInterval, + int spectralStart, int spectralEnd, int successivePrev, int successive) throws IOException { + // this.precision = frame.precision; + // this.samplesPerLine = frame.samplesPerLine; + // this.scanLines = frame.scanLines; + this.mcusPerLine = frame.mcusPerLine; + this.progressive = frame.progressive; + // this.maxH = frame.maxH; + // this.maxV = frame.maxV; + bstream.skip( bstream.getBitCount() ); // align to next byte + this.spectralStart = spectralStart; + this.spectralEnd = spectralEnd; + this.successive = successive; + + final int componentsLength = components.size(); + + final DecoderFunction decodeFn; + if (progressive) { + if (spectralStart == 0) { + decodeFn = successivePrev == 0 ? decodeDCFirst : decodeDCSuccessive; + } else { + decodeFn = successivePrev == 0 ? decodeACFirst : decodeACSuccessive; + } + } else { + decodeFn = decodeBaseline; + } + + int mcu = 0; + int mcuExpected; + if (componentsLength == 1) { + final ComponentIn c = components.get(0); + mcuExpected = c.blocksPerLine * c.blocksPerColumn; + } else { + mcuExpected = mcusPerLine * frame.mcusPerColumn; + } + if (0 == resetInterval) { + resetInterval = mcuExpected; + } + if(DEBUG) { + System.err.println("JPEG.decodeScan.1 resetInterval "+resetInterval+", mcuExpected "+mcuExpected+", sA "+spectralStart+", sP "+successivePrev+", sE "+spectralEnd+", suc "+successive+", decodeFn "+decodeFn.getClass().getSimpleName()); + } + int marker = 0; + while ( /* untilMarker || */ mcu < mcuExpected) { + // reset interval stuff + for (int i = 0; i < componentsLength; i++) { + components.get(i).pred = 0; + } + eobrun = 0; + + try { + if (componentsLength == 1) { + final ComponentIn component = components.get(0); + for (int n = 0; n < resetInterval; n++) { + decodeBlock(component, decodeFn, mcu); + mcu++; + } + } else { + for (int n = 0; n < resetInterval; n++) { + for (int i = 0; i < componentsLength; i++) { + final ComponentIn component = components.get(i); + final int h = component.h; + final int v = component.v; + for (int j = 0; j < v; j++) { + for (int k = 0; k < h; k++) { + decodeMcu(component, decodeFn, mcu, j, k); + } + } + } + mcu++; + } + } + } catch (MarkerException markerException) { + if(DEBUG) { System.err.println("JPEG.decodeScan: Marker exception: "+markerException.getMessage()); markerException.printStackTrace(); } + return markerException.getMarker(); + } catch (CodecException codecException) { + if(DEBUG) { System.err.println("JPEG.decodeScan: Codec exception: "+codecException.getMessage()); codecException.printStackTrace(); } + bstream.skip( bstream.getBitCount() ); // align to next byte + return M_EOI; // force end ! + } + + // find marker + bstream.skip( bstream.getBitCount() ); // align to next byte + bstream.mark(2); + marker = readUInt16(); + if( marker < 0xFF00 ) { + bstream.reset(); + throw new CodecException("marker not found @ mcu "+mcu+"/"+mcuExpected+", u16: "+toHexString(marker)); + } + final boolean isRSTx = 0xFFD0 <= marker && marker <= 0xFFD7; // !RSTx + if(DEBUG) { + System.err.println("JPEG.decodeScan: MCUs "+mcu+"/"+mcuExpected+", u16 "+toHexString(marker)+", RSTx "+isRSTx+", "+frame); + } + if ( !isRSTx ) { + break; // handle !RSTx marker in caller + } + } + return marker; + } + + private final int readBit() throws MarkerException, IOException { + final int bit = bstream.readBit(true /* msbFirst */); + if( Bitstream.EOS == bit || 7 != bstream.getBitCount() ) { + return bit; + } + // new byte read, i.e. bitCount == 7 + final int bitsData = bstream.getBitBuffer(); // peek for marker + if ( 0xFF == bitsData ) { // marker prefix + final int nextByte = bstream.getStream().read(); // snoop marker signature, will be dropped! + if( -1 == nextByte ) { + throw new CodecException("marked prefix 0xFF, then EOF"); + } + if (0 != nextByte) { + final int marker = (bitsData << 8) | nextByte; + throw new MarkerException(marker, "Marker at readBit pos " + bstream); + } + // unstuff 0 + } + return bit; + } + + private int decodeHuffman(BinObj tree) throws IOException { + BinObj node = tree; + int bit; + while ( ( bit = readBit() ) != -1 ) { + node = node.get(bit); + if ( node.isValue ) { + return 0x000000FF & node.getValue(); + } + } + throw new CodecException("EOF reached at "+bstream); + } + private int receive(int length) throws IOException { + int n = 0; + while (length > 0) { + final int bit = readBit(); + if (bit == -1) { + return -1; + } + n = (n << 1) | bit; + length--; + } + return n; + } + private int receiveAndExtend(int length) throws IOException { + final int n = receive(length); + if (n >= 1 << (length - 1)) { + return n; + } + return n + (-1 << length) + 1; + } + + final DecoderFunction decodeBaseline = new BaselineDecoder(); + final DecoderFunction decodeDCFirst = new DCFirstDecoder(); + final DecoderFunction decodeDCSuccessive = new DCSuccessiveDecoder(); + final DecoderFunction decodeACFirst = new ACFirstDecoder(); + final DecoderFunction decodeACSuccessive = new ACSuccessiveDecoder(); + + class BaselineDecoder implements DecoderFunction { + @Override + public void decode(ComponentIn component, int[] zz) throws IOException { + final int t = decodeHuffman(component.huffmanTableDC); + final int diff = ( t == 0 ) ? 0 : receiveAndExtend(t); + zz[0] = ( component.pred += diff ); + int k = 1; + while (k < 64) { + final int rs = decodeHuffman(component.huffmanTableAC); + final int s = rs & 15, r = rs >> 4; + if (s == 0) { + if (r < 15) { + break; + } + k += 16; + continue; + } + k += r; + final int z = dctZigZag[k]; + zz[z] = receiveAndExtend(s); + k++; + } + } + } + class DCFirstDecoder implements DecoderFunction { + @Override + public void decode(ComponentIn component, int[] zz) throws IOException { + final int t = decodeHuffman(component.huffmanTableDC); + final int diff = ( t == 0 ) ? 0 : (receiveAndExtend(t) << successive); + zz[0] = ( component.pred += diff ); + } + } + class DCSuccessiveDecoder implements DecoderFunction { + @Override + public void decode(ComponentIn component, int[] zz) throws IOException { + zz[0] |= readBit() << successive; + } + } + + class ACFirstDecoder implements DecoderFunction { + @Override + public void decode(ComponentIn component, int[] zz) throws IOException { + if (eobrun > 0) { + eobrun--; + return; + } + int k = spectralStart, e = spectralEnd; + while (k <= e) { + final int rs = decodeHuffman(component.huffmanTableAC); + final int s = rs & 15, r = rs >> 4; + if (s == 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r) - 1; + break; + } + k += 16; + continue; + } + k += r; + final int z = dctZigZag[k]; + zz[z] = receiveAndExtend(s) * (1 << successive); + k++; + } + } + } + class ACSuccessiveDecoder implements DecoderFunction { + @Override + public void decode(ComponentIn component, int[] zz) throws IOException { + int k = spectralStart, e = spectralEnd, r = 0; + while (k <= e) { + final int z = dctZigZag[k]; + switch (successiveACState) { + case 0: // initial state + final int rs = decodeHuffman(component.huffmanTableAC); + final int s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r); + successiveACState = 4; + } else { + r = 16; + successiveACState = 1; + } + } else { + // if (s !== 1) { + if (s != 1) { + throw new CodecException("invalid ACn encoding"); + } + successiveACNextValue = receiveAndExtend(s); + successiveACState = r != 0 ? 2 : 3; + } + continue; + case 1: // skipping r zero items + case 2: + if ( zz[z] != 0 ) { + zz[z] += (readBit() << successive); + } else { + r--; + if (r == 0) { + successiveACState = successiveACState == 2 ? 3 : 0; + } + } + break; + case 3: // set value for a zero item + if ( zz[z] != 0 ) { + zz[z] += (readBit() << successive); + } else { + zz[z] = successiveACNextValue << successive; + successiveACState = 0; + } + break; + case 4: // eob + if ( zz[z] != 0 ) { + zz[z] += (readBit() << successive); + } + break; + } + k++; + } + if (successiveACState == 4) { + eobrun--; + if (eobrun == 0) { + successiveACState = 0; + } + } + } + } + void decodeMcu(ComponentIn component, DecoderFunction decoder, int mcu, int row, int col) throws IOException { + final int mcuRow = (mcu / mcusPerLine) | 0; + final int mcuCol = mcu % mcusPerLine; + final int blockRow = mcuRow * component.v + row; + final int blockCol = mcuCol * component.h + col; + decoder.decode(component, component.getBlock(blockRow, blockCol)); + } + void decodeBlock(ComponentIn component, DecoderFunction decoder, int mcu) throws IOException { + final int blockRow = (mcu / component.blocksPerLine) | 0; + final int blockCol = mcu % component.blocksPerLine; + decoder.decode(component, component.getBlock(blockRow, blockCol)); + } + } + + private final Decoder decoder = new Decoder(); + + /** wrong color space .. + private final void storeYCbCr2BGR(final PixelStorage pixelStorage, int x, int y, int Y, final int Cb, final int Cr) + { + if(Y<0) Y=0; + int B = Y + ( ( 116130 * Cb ) >> 16 ) ; + if(B<0) B=0; + else if(B>255) B=255; + + int G = Y - ( ( 22554 * Cb + 46802 * Cr ) >> 16 ) ; + if(G<0) G=0; + else if(G>255) G=255; + + int R = Y + ( ( 91881 * Cr ) >> 16 ); + if(R<0) R=0; + else if(R>255) R=255; + + pixelStorage.storeRGB(x, y, (byte)R, (byte)G, (byte)B); + } */ + + public synchronized void getPixel(JPEGDecoder.ColorSink pixelStorage, int width, int height) { + final int scaleX = this.width / width, scaleY = this.height / height; + + final int componentCount = this.components.length; + final ColorSpace sourceCS = ( null != adobe ) ? adobe.colorSpace : ColorSpace.YCbCr; + final ColorSpace storageCS = pixelStorage.allocate(width, height, sourceCS, componentCount); + if( ColorSpace.RGB != storageCS && ColorSpace.YCbCr != storageCS ) { + throw new IllegalArgumentException("Unsupported storage color space: "+storageCS); + } + + switch (componentCount) { + case 1: { + // Grayscale + final ComponentOut component1 = this.components[0]; + for (int y = 0; y < height; y++) { + final byte[] component1Line = component1.getLine((int)(y * component1.scaleY * scaleY)); + for (int x = 0; x < width; x++) { + final byte Y = component1Line[(int)(x * component1.scaleX * scaleX)]; + if( ColorSpace.YCbCr == storageCS ) { + pixelStorage.storeYCbCr(x, y, Y, (byte)0, (byte)0); + } else { + pixelStorage.storeRGB(x, y, Y, Y, Y); + } + } + } + } + break; + case 2: { + // PDF might compress two component data in custom colorspace + final ComponentOut component1 = this.components[0]; + final ComponentOut component2 = this.components[1]; + for (int y = 0; y < height; y++) { + final int ys = y * scaleY; + final byte[] component1Line = component1.getLine((int)(ys * component1.scaleY)); + final byte[] component2Line = component1.getLine((int)(ys * component2.scaleY)); + for (int x = 0; x < width; x++) { + final int xs = x * scaleX; + final byte Y1 = component1Line[(int)(xs * component1.scaleX)]; + final byte Y2 = component2Line[(int)(xs * component2.scaleX)]; + pixelStorage.store2(x, y, Y1, Y2); + } + } + } + break; + case 3: { + if (ColorSpace.YCbCr != sourceCS) { + throw new CodecException("Unsupported source color space w 3 components: "+sourceCS); + } + final ComponentOut component1 = this.components[0]; + final ComponentOut component2 = this.components[1]; + final ComponentOut component3 = this.components[2]; + for (int y = 0; y < height; y++) { + final int ys = y * scaleY; + final byte[] component1Line = component1.getLine((int)(ys * component1.scaleY)); + final byte[] component2Line = component2.getLine((int)(ys * component2.scaleY)); + final byte[] component3Line = component3.getLine((int)(ys * component3.scaleY)); + if( ColorSpace.YCbCr == storageCS ) { + for (int x = 0; x < width; x++) { + final int xs = x * scaleX; + final byte Y = component1Line[(int)(xs * component1.scaleX)]; + final byte Cb = component2Line[(int)(xs * component2.scaleX)]; + final byte Cr = component3Line[(int)(xs * component3.scaleX)]; + pixelStorage.storeYCbCr(x, y, Y, Cb, Cr); + } + } else { + for (int x = 0; x < width; x++) { + final int xs = x * scaleX; + final int Y = 0x000000FF & component1Line[(int)(xs * component1.scaleX)]; + final int Cb = 0x000000FF & component2Line[(int)(xs * component2.scaleX)]; + final int Cr = 0x000000FF & component3Line[(int)(xs * component3.scaleX)]; + // storeYCbCr2BGR(pixelStorage, x, y, Y, Cb, Cr); + final byte R = clampTo8bit(Y + 1.402f * (Cr - 128f)); + final byte G = clampTo8bit(Y - 0.3441363f * (Cb - 128f) - 0.71413636f * (Cr - 128f)); + final byte B = clampTo8bit(Y + 1.772f * (Cb - 128f)); + pixelStorage.storeRGB(x, y, R, G, B); + } + } + } + } + break; + case 4: { + if (ColorSpace.YCCK != sourceCS && ColorSpace.CMYK != sourceCS) { + throw new CodecException("Unsupported source color space w 4 components: "+sourceCS); + } + final ComponentOut component1 = this.components[0]; + final ComponentOut component2 = this.components[1]; + final ComponentOut component3 = this.components[2]; + final ComponentOut component4 = this.components[3]; + for (int y = 0; y < height; y++) { + final int ys = y * scaleY; + final byte[] component1Line = component1.getLine((int)(ys * component1.scaleY)); + final byte[] component2Line = component2.getLine((int)(ys * component2.scaleY)); + final byte[] component3Line = component3.getLine((int)(ys * component3.scaleY)); + final byte[] component4Line = component4.getLine((int)(ys * component4.scaleY)); + if( ColorSpace.YCbCr == storageCS ) { + if (ColorSpace.YCCK != sourceCS) { + throw new CodecException("Unsupported storage color space "+storageCS+" with source color space "+sourceCS); + } + for (int x = 0; x < width; x++) { + final int xs = x * scaleX; + final byte Y1 = component1Line[(int)(xs * component1.scaleX)]; + final byte C1 = component2Line[(int)(xs * component2.scaleX)]; + final byte C2 = component3Line[(int)(xs * component3.scaleX)]; + // final byte K = component4Line[(int)(xs * component4.scaleX)]; + // FIXME: YCCK is not really YCbCr, since K (black) is missing! + pixelStorage.storeYCbCr(x, y, Y1, C1, C2); + } + } else { + if (ColorSpace.CMYK == sourceCS) { + for (int x = 0; x < width; x++) { + final int xs = x * scaleX; + final int cC = 0x000000FF & component1Line[(int)(xs * component1.scaleX)]; + final int cM = 0x000000FF & component2Line[(int)(xs * component2.scaleX)]; + final int cY = 0x000000FF & component3Line[(int)(xs * component3.scaleX)]; + final int cK = 0x000000FF & component4Line[(int)(xs * component4.scaleX)]; + // CMYK -> RGB + final byte R = clampTo8bit( ( cC * cK ) / 255f ); + final byte G = clampTo8bit( ( cM * cK ) / 255f ); + final byte B = clampTo8bit( ( cY * cK ) / 255f ); + pixelStorage.storeRGB(x, y, R, G, B); + } + } else { // ColorModel.YCCK == sourceCM + for (int x = 0; x < width; x++) { + final int xs = x * scaleX; + final int Y = 0x000000FF & component1Line[(int)(xs * component1.scaleX)]; + final int Cb = 0x000000FF & component2Line[(int)(xs * component2.scaleX)]; + final int Cr = 0x000000FF & component3Line[(int)(xs * component3.scaleX)]; + final int cK = 0x000000FF & component4Line[(int)(xs * component4.scaleX)]; + // YCCK -> 255f - [ R'G'B' ] -> CMYK + final float cC = 255f - ( Y + 1.402f * (Cr - 128f) ); + final float cM = 255f - ( Y - 0.3441363f * (Cb - 128f) - 0.71413636f * (Cr - 128f) ); + final float cY = 255f - ( Y + 1.772f * (Cb - 128f) ); + // CMYK -> RGB + final byte R = clampTo8bit( ( cC * cK ) / 255f ); + final byte G = clampTo8bit( ( cM * cK ) / 255f ); + final byte B = clampTo8bit( ( cY * cK ) / 255f ); + pixelStorage.storeRGB(x, y, R, G, B); + } + } + } + } + } + break; + default: + throw new CodecException("Unsupported color model: Space "+sourceCS+", components "+componentCount); + } + } + + private static byte clampTo8bit(float a) { + return (byte) ( a < 0f ? 0 : a > 255f ? 255 : a ); + } + + private static String toHexString(int v) { + return "0x"+Integer.toHexString(v); + } +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java b/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java index a34f73ab2..5e177b8c3 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/FilterType.java @@ -1,8 +1,10 @@ package jogamp.opengl.util.pngj;
+import java.util.HashMap;
+
/**
* Internal PNG predictor filter, or strategy to select it.
- *
+ *
*/
public enum FilterType {
/**
@@ -26,69 +28,46 @@ 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),
/**
- * Aggresive strategy: select one of the above filters trying each of the filters (this is done 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!)
+ */
+ FILTER_VERYAGGRESSIVE(-3),
+ /**
* Uses all fiters, one for lines, cyciclally. Only for tests.
*/
- FILTER_ALTERNATE(-3),
+ FILTER_CYCLIC(-50),
+
/**
- * Aggresive strategy: select one of the above filters trying each of the filters (this is done for every row!)
+ * Not specified, placeholder for unknown or NA filters.
*/
- FILTER_VERYAGGRESSIVE(-4), ;
+ FILTER_UNKNOWN(-100), ;
public final int val;
private FilterType(int val) {
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 int unfilterRowNone(int r) {
- return (int) (r & 0xFF);
}
- public static int unfilterRowSub(int r, int left) {
- return ((int) (r + left) & 0xFF);
- }
-
- public static int unfilterRowUp(int r, int up) {
- return ((int) (r + up) & 0xFF);
- }
-
- public static int unfilterRowAverage(int r, int left, int up) {
- return (r + (left + up) / 2) & 0xFF;
- }
-
- public static int unfilterRowPaeth(int r, int a, int b, int c) { // a = left, b = above, c = upper left
- return (r + filterPaethPredictor(a, b, c)) & 0xFF;
+ public static FilterType getByVal(int i) {
+ return byVal.get(i);
}
- public static int filterPaethPredictor(int a, int b, int c) {
- // from http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html
- // a = left, b = above, c = upper left
- final int p = a + b - c;// ; initial estimate
- final int pa = p >= a ? p - a : a - p;
- final int pb = p >= b ? p - b : b - p;
- final int pc = p >= c ? p - c : c - p;
- // ; return nearest of a,b,c,
- // ; breaking ties in order a,b,c.
- if (pa <= pb && pa <= pc)
- return a;
- else if (pb <= pc)
- return b;
- else
- return c;
- }
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/FilterWriteStrategy.java b/src/jogl/classes/jogamp/opengl/util/pngj/FilterWriteStrategy.java index 27586b292..79eed8f85 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/FilterWriteStrategy.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/FilterWriteStrategy.java @@ -1,7 +1,7 @@ package jogamp.opengl.util.pngj;
/**
- * Manages the writer strategy for selecting the internal png "filter"
+ * Manages the writer strategy for selecting the internal png predictor filter
*/
class FilterWriteStrategy {
private static final int COMPUTE_STATS_EVERY_N_LINES = 8;
@@ -89,7 +89,7 @@ class FilterWriteStrategy { }
}
}
- if (configuredType == FilterType.FILTER_ALTERNATE) {
+ if (configuredType == FilterType.FILTER_CYCLIC) {
currentType = FilterType.getByVal((currentType.val + 1) % 5);
}
return currentType;
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageInfo.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageInfo.java index 2f6b89e9c..ac7b858e1 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
*/
@@ -13,24 +14,25 @@ public class ImageInfo { private static final int MAX_COLS_ROWS_VAL = 1000000;
/**
- * Image width, in pixels.
+ * Cols= Image width, in pixels.
*/
public final int cols;
/**
- * Image height, in pixels
+ * Rows= Image height, in pixels
*/
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. This is 3 for RGB, 4 for RGBA, 2 for GA (gray with alpha), 1 for
- * grayscales 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,16 @@ public class ImageInfo { public final int samplesPerRow;
/**
- * For internal use only. Samples available for our packed scanline. Equals samplesPerRow if not packed. Elsewhere,
- * it's lower
+ * 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
+ * <p>
+ * For internal use, client code should rarely access this.
*/
- final int samplesPerRowP;
+ public final int samplesPerRowPacked;
/**
* Short constructor: assumes truecolor (RGB/RGBA)
@@ -89,13 +98,14 @@ public class ImageInfo { /**
* Full constructor
- *
+ *
* @param cols
* Width in pixels
* @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
@@ -119,7 +129,7 @@ public class ImageInfo { this.bytesPixel = (bitspPixel + 7) / 8;
this.bytesPerRow = (bitspPixel * cols + 7) / 8;
this.samplesPerRow = channels * this.cols;
- this.samplesPerRowP = packed ? bytesPerRow : samplesPerRow;
+ this.samplesPerRowPacked = packed ? bytesPerRow : samplesPerRow;
// several checks
switch (this.bitDepth) {
case 1:
@@ -147,7 +157,7 @@ public class ImageInfo { public String toString() {
return "ImageInfo [cols=" + cols + ", rows=" + rows + ", bitDepth=" + bitDepth + ", channels=" + channels
+ ", bitspPixel=" + bitspPixel + ", bytesPixel=" + bytesPixel + ", bytesPerRow=" + bytesPerRow
- + ", samplesPerRow=" + samplesPerRow + ", samplesPerRowP=" + samplesPerRowP + ", alpha=" + alpha
+ + ", samplesPerRow=" + samplesPerRow + ", samplesPerRowP=" + samplesPerRowPacked + ", alpha=" + alpha
+ ", greyscale=" + greyscale + ", indexed=" + indexed + ", packed=" + packed + "]";
}
@@ -157,16 +167,11 @@ public class ImageInfo { int result = 1;
result = prime * result + (alpha ? 1231 : 1237);
result = prime * result + bitDepth;
- result = prime * result + bitspPixel;
- result = prime * result + bytesPerRow;
- result = prime * result + bytesPixel;
result = prime * result + channels;
result = prime * result + cols;
result = prime * result + (greyscale ? 1231 : 1237);
result = prime * result + (indexed ? 1231 : 1237);
- result = prime * result + (packed ? 1231 : 1237);
result = prime * result + rows;
- result = prime * result + samplesPerRow;
return result;
}
@@ -183,12 +188,6 @@ public class ImageInfo { return false;
if (bitDepth != other.bitDepth)
return false;
- if (bitspPixel != other.bitspPixel)
- return false;
- if (bytesPerRow != other.bytesPerRow)
- return false;
- if (bytesPixel != other.bytesPixel)
- return false;
if (channels != other.channels)
return false;
if (cols != other.cols)
@@ -197,12 +196,9 @@ public class ImageInfo { return false;
if (indexed != other.indexed)
return false;
- if (packed != other.packed)
- return false;
if (rows != other.rows)
return false;
- if (samplesPerRow != other.samplesPerRow)
- return false;
return true;
}
+
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java index bfbb35b7c..e6afd8694 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLine.java @@ -1,11 +1,12 @@ package jogamp.opengl.util.pngj;
-import java.util.Arrays;
+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,28 +19,97 @@ 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 int is a "sample" (one for channel), (0-255
- * or 0-65535) in the respective PNG sequence sequence : (R G B R G B...) or (R G B A R G B A...) or (g g g ...) or
- * ( i i i) (palette index)
+ * 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 , each element is a PACKED byte! To get an unpacked copy, see <code>tf_pack()</code> and its
- * inverse <code>tf_unpack()</code>
+ * 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.tf_palIdx2RGB()</code> (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; // see explanation above!!
+ 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}
+ */
+ public final byte[] scanlineb;
protected FilterType filterUsed; // informational ; only filled by the reader
- public final int channels; // copied from imgInfo, more handy
- public final int bitDepth; // copied from imgInfo, more handy
+ final int channels; // copied from imgInfo, more handy
+ final int bitDepth; // copied from imgInfo, more handy
+ final int elementsPerRow; // = imgInfo.samplePerRowPacked, if packed:imgInfo.samplePerRow elswhere
+
+ public enum SampleType {
+ INT, // 4 bytes per sample
+ // SHORT, // 2 bytes per sample
+ BYTE // 1 byte per sample
+ }
+ /**
+ * tells if we are using BYTE or INT to store the samples.
+ */
+ public final SampleType sampleType;
+
+ /**
+ * 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
+ */
+ public final boolean samplesUnpacked;
+
+ /**
+ * default mode: INT packed
+ */
public ImageLine(ImageInfo imgInfo) {
+ this(imgInfo, SampleType.INT, false);
+ }
+
+ /**
+ *
+ * @param imgInfo
+ * 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
+ * @param unpackedMode
+ * If true, we use unpacked format, even for packed original
+ * images
+ *
+ */
+ public ImageLine(ImageInfo imgInfo, SampleType stype, boolean unpackedMode) {
+ this(imgInfo, stype, unpackedMode, null, null);
+ }
+
+ /**
+ * If a preallocated array is passed, the copy is shallow
+ */
+ ImageLine(ImageInfo imgInfo, SampleType stype, boolean unpackedMode, int[] sci, byte[] scb) {
this.imgInfo = imgInfo;
channels = imgInfo.channels;
- scanline = new int[imgInfo.samplesPerRowP];
- this.bitDepth = imgInfo.bitDepth;
+ bitDepth = imgInfo.bitDepth;
+ filterUsed = FilterType.FILTER_UNKNOWN;
+ this.sampleType = stype;
+ this.samplesUnpacked = unpackedMode || !imgInfo.packed;
+ elementsPerRow = this.samplesUnpacked ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked;
+ if (stype == SampleType.INT) {
+ scanline = sci != null ? sci : new int[elementsPerRow];
+ scanlineb = null;
+ } else if (stype == SampleType.BYTE) {
+ scanlineb = scb != null ? scb : new byte[elementsPerRow];
+ scanline = null;
+ } else
+ throw new PngjExceptionInternal("bad ImageLine initialization");
+ this.rown = -1;
}
/** This row number inside the image (0 is top) */
@@ -47,129 +117,217 @@ public class ImageLine { return rown;
}
- /** Increments row number */
- public void incRown() {
- this.rown++;
- }
-
- /** Sets row number */
+ /** Sets row number (0 : Rows-1) */
public void setRown(int n) {
this.rown = n;
}
- /** Sets scanline, making copy from passed array */
- public void setScanLine(int[] b) {
- System.arraycopy(b, 0, scanline, 0, scanline.length);
+ /*
+ * Unpacks scanline (for bitdepth 1-2-4)
+ *
+ * Arrays must be prealocated. src : samplesPerRowPacked dst : samplesPerRow
+ *
+ * This usually works in place (with src==dst and length=samplesPerRow)!
+ *
+ * If not, you should only call this only when necesary (bitdepth <8)
+ *
+ * If <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
+ */
+ static void unpackInplaceInt(final ImageInfo iminfo, final int[] src, final int[] dst, final boolean scale) {
+ final int bitDepth = iminfo.bitDepth;
+ if (bitDepth >= 8)
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 * iminfo.samplesPerRowPacked - bitDepth * iminfo.samplesPerRow;
+ int mask, offset, v;
+ if (offset0 != 8) {
+ mask = mask0 << offset0;
+ offset = offset0; // how many bits to shift the mask to the right to recover mask0
+ } else {
+ mask = mask0;
+ offset = 0;
+ }
+ for (int j = iminfo.samplesPerRow - 1, i = iminfo.samplesPerRowPacked - 1; j >= 0; j--) {
+ v = (src[i] & mask) >> offset;
+ if (scale)
+ v <<= scalefactor;
+ dst[j] = v;
+ mask <<= bitDepth;
+ offset += bitDepth;
+ if (offset == 8) {
+ mask = mask0;
+ offset = 0;
+ i--;
+ }
+ }
}
- /**
- * Returns a copy from scanline, in byte array.
- *
- * You can (OPTIONALLY) pass an preallocated array to use.
- **/
- public int[] getScanLineCopy(int[] b) {
- if (b == null || b.length < scanline.length)
- b = new int[scanline.length];
- System.arraycopy(scanline, 0, b, 0, scanline.length);
- return b;
+ /*
+ * Unpacks scanline (for bitdepth 1-2-4)
+ *
+ * Arrays must be prealocated. src : samplesPerRow dst : samplesPerRowPacked
+ *
+ * This usually works in place (with src==dst and length=samplesPerRow)! If not, you should only call this only when
+ * necesary (bitdepth <8)
+ *
+ * The trailing elements are trash
+ *
+ *
+ * If <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
+ */
+ static void packInplaceInt(final ImageInfo iminfo, final int[] src, final int[] dst, final boolean scaled) {
+ final int bitDepth = iminfo.bitDepth;
+ if (bitDepth >= 8)
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 - bitDepth;
+ int v, v0;
+ int offset = 8 - bitDepth;
+ v0 = src[0]; // first value is special for in place
+ dst[0] = 0;
+ if (scaled)
+ v0 >>= scalefactor;
+ v0 = ((v0 & mask0) << offset);
+ for (int i = 0, j = 0; j < iminfo.samplesPerRow; j++) {
+ v = src[j];
+ if (scaled)
+ v >>= scalefactor;
+ dst[i] |= ((v & mask0) << offset);
+ offset -= bitDepth;
+ if (offset < 0) {
+ offset = offset0;
+ i++;
+ dst[i] = 0;
+ }
+ }
+ dst[0] |= v0;
}
- /**
- * Unpacks scanline (for bitdepth 1-2-4) into buffer.
- * <p>
- * You can (OPTIONALLY) pass an preallocated array to use.
- * <p>
- * If scale==TRUE scales the value (just a bit shift).
- */
- public int[] tf_unpack(int[] buf, boolean scale) {
- int len = scanline.length;
- if (bitDepth == 1)
- len *= 8;
- else if (bitDepth == 2)
- len *= 4;
- else if (bitDepth == 4)
- len *= 2;
- if (buf == null)
- buf = new int[len];
+ static void unpackInplaceByte(final ImageInfo iminfo, final byte[] src, final byte[] dst, final boolean scale) {
+ final int bitDepth = iminfo.bitDepth;
if (bitDepth >= 8)
- System.arraycopy(scanline, 0, buf, 0, scanline.length);
- else {
- int mask, offset, v;
- int mask0 = getMaskForPackedFormats();
- int offset0 = 8 - bitDepth;
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 * iminfo.samplesPerRowPacked - bitDepth * iminfo.samplesPerRow;
+ int mask, offset, v;
+ if (offset0 != 8) {
+ mask = mask0 << offset0;
+ offset = offset0; // how many bits to shift the mask to the right to recover mask0
+ } else {
mask = mask0;
- offset = offset0;
- for (int i = 0, j = 0; i < len; i++) {
- v = (scanline[j] & mask) >> offset;
- if (scale)
- v <<= offset0;
- buf[i] = v;
- mask = mask >> bitDepth;
- offset -= bitDepth;
- if (mask == 0) { // new byte in source
- mask = mask0;
- offset = offset0;
- j++;
- }
+ offset = 0;
+ }
+ for (int j = iminfo.samplesPerRow - 1, i = iminfo.samplesPerRowPacked - 1; j >= 0; j--) {
+ v = (src[i] & mask) >> offset;
+ if (scale)
+ v <<= scalefactor;
+ dst[j] = (byte) v;
+ mask <<= bitDepth;
+ offset += bitDepth;
+ if (offset == 8) {
+ mask = mask0;
+ offset = 0;
+ i--;
}
}
- return buf;
}
/**
- * Packs scanline (for bitdepth 1-2-4) from buffer.
- * <p>
- * If scale==TRUE scales the value (just a bit shift).
- */
- public void tf_pack(int[] buf, boolean scale) { // writes scanline
- int len = scanline.length;
- if (bitDepth == 1)
- len *= 8;
- else if (bitDepth == 2)
- len *= 4;
- else if (bitDepth == 4)
- len *= 2;
+ * 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)
- System.arraycopy(buf, 0, scanline, 0, scanline.length);
- else {
- int offset0 = 8 - bitDepth;
- int mask0 = getMaskForPackedFormats() >> offset0;
- int offset, v;
- offset = offset0;
- Arrays.fill(scanline, 0);
- for (int i = 0, j = 0; i < len; i++) {
- v = buf[i];
- if (scale)
- v >>= offset0;
- v = (v & mask0) << offset;
- scanline[j] |= v;
- offset -= bitDepth;
- if (offset < 0) { // new byte in scanline
- offset = offset0;
- j++;
- }
+ return; // nothing to do
+ final int mask0 = ImageLineHelper.getMaskForPackedFormatsLs(bitDepth);
+ final int scalefactor = 8 - bitDepth;
+ final int offset0 = 8 - bitDepth;
+ int v, v0;
+ int offset = 8 - bitDepth;
+ v0 = src[0]; // first value is special
+ dst[0] = 0;
+ if (scaled)
+ v0 >>= scalefactor;
+ v0 = ((v0 & mask0) << offset);
+ for (int i = 0, j = 0; j < iminfo.samplesPerRow; j++) {
+ v = src[j];
+ if (scaled)
+ v >>= scalefactor;
+ dst[i] |= ((v & mask0) << offset);
+ offset -= bitDepth;
+ if (offset < 0) {
+ offset = offset0;
+ i++;
+ dst[i] = 0;
}
}
+ dst[0] |= v0;
}
- private int getMaskForPackedFormats() { // Utility function for pacj/unpack
- if (bitDepth == 1)
- return 0x80;
- if (bitDepth == 2)
- return 0xc0;
- if (bitDepth == 4)
- return 0xf0;
- throw new RuntimeException("?");
+ /**
+ * Creates a new ImageLine similar to this, but unpacked
+ *
+ * The caller must be sure that the original was really packed
+ */
+ public ImageLine unpackToNewImageLine() {
+ ImageLine newline = new ImageLine(imgInfo, sampleType, true);
+ if (sampleType == SampleType.INT)
+ unpackInplaceInt(imgInfo, scanline, newline.scanline, false);
+ else
+ unpackInplaceByte(imgInfo, scanlineb, newline.scanlineb, false);
+ return newline;
+ }
+
+ /**
+ * Creates a new ImageLine similar to this, but packed
+ *
+ * The caller must be sure that the original was really unpacked
+ */
+ public ImageLine packToNewImageLine() {
+ ImageLine newline = new ImageLine(imgInfo, sampleType, false);
+ if (sampleType == SampleType.INT)
+ packInplaceInt(imgInfo, scanline, newline.scanline, false);
+ else
+ packInplaceByte(imgInfo, scanlineb, newline.scanlineb, false);
+ return newline;
}
public FilterType getFilterUsed() {
return filterUsed;
}
+ public void setFilterUsed(FilterType ft) {
+ filterUsed = ft;
+ }
+
+ public int[] getScanlineInt() {
+ return scanline;
+ }
+
+ public byte[] getScanlineByte() {
+ return scanlineb;
+ }
+
/**
* Basic info
*/
+ @Override
public String toString() {
return "row=" + rown + " cols=" + imgInfo.cols + " bpc=" + imgInfo.bitDepth + " size=" + scanline.length;
}
+
+ /**
+ * Prints some statistics - just for debugging
+ */
+ public static void showLineInfo(ImageLine line) {
+ System.out.println(line);
+ ImageLineStats stats = new ImageLineHelper.ImageLineStats(line);
+ System.out.println(stats);
+ System.out.println(ImageLineHelper.infoFirstLastPixels(line));
+ }
+
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java new file mode 100644 index 000000000..4636c3955 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLineHelper.java @@ -0,0 +1,329 @@ +package jogamp.opengl.util.pngj;
+
+import jogamp.opengl.util.pngj.ImageLine.SampleType;
+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.
+ * <p>
+ * 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
+ */
+public class ImageLineHelper {
+
+ private final static double BIG_VALUE = Double.MAX_VALUE * 0.5;
+
+ 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
+ *
+ * @param line
+ * ImageLine as returned from PngReader
+ * @param pal
+ * Palette chunk
+ * @param buf
+ * Preallocated array, optional
+ * @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;
+ int channels = isalpha ? 4 : 3;
+ int nsamples = line.imgInfo.cols * channels;
+ if (buf == null || buf.length < nsamples)
+ buf = new int[nsamples];
+ if (!line.samplesUnpacked)
+ line = line.unpackToNewImageLine();
+ boolean isbyte = line.sampleType == SampleType.BYTE;
+ int nindexesWithAlpha = trns != null ? trns.getPalletteAlpha().length : 0;
+ for (int c = 0; c < line.imgInfo.cols; c++) {
+ int index = isbyte ? (line.scanlineb[c] & 0xFF) : line.scanline[c];
+ pal.getEntryRgb(index, buf, c * channels);
+ if (isalpha) {
+ int alpha = index < nindexesWithAlpha ? trns.getPalletteAlpha()[index] : 255;
+ buf[c * channels + 3] = alpha;
+ }
+ }
+ return buf;
+ }
+
+ public static int[] palette2rgb(ImageLine line, PngChunkPLTE pal, int[] buf) {
+ 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
+ */
+ public static String infoFirstLastPixels(ImageLine line) {
+ return line.imgInfo.channels == 1 ? String.format("first=(%d) last=(%d)", line.scanline[0],
+ line.scanline[line.scanline.length - 1]) : String.format("first=(%d %d %d) last=(%d %d %d)",
+ line.scanline[0], line.scanline[1], line.scanline[2], line.scanline[line.scanline.length
+ - line.imgInfo.channels], line.scanline[line.scanline.length - line.imgInfo.channels + 1],
+ line.scanline[line.scanline.length - line.imgInfo.channels + 2]);
+ }
+
+ public static String infoFull(ImageLine line) {
+ ImageLineStats stats = new ImageLineStats(line);
+ return "row=" + line.getRown() + " " + stats.toString() + "\n " + infoFirstLastPixels(line);
+ }
+
+ /**
+ * 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
+ public double[] maxv = { BIG_VALUE_NEG, BIG_VALUE_NEG, BIG_VALUE_NEG, BIG_VALUE_NEG }; // maximo
+ public double[] minv = { BIG_VALUE, BIG_VALUE, BIG_VALUE, BIG_VALUE };
+ public double promlum = 0.0; // maximum global (luminance)
+ public double maxlum = BIG_VALUE_NEG; // max luminance
+ public double minlum = BIG_VALUE;
+ public double[] maxdif = { BIG_VALUE_NEG, BIG_VALUE_NEG, BIG_VALUE_NEG, BIG_VALUE }; // maxima
+ public final int channels; // diferencia
+
+ @Override
+ public String toString() {
+ return channels == 3 ? String.format(
+ "prom=%.1f (%.1f %.1f %.1f) max=%.1f (%.1f %.1f %.1f) min=%.1f (%.1f %.1f %.1f)", promlum, prom[0],
+ prom[1], prom[2], maxlum, maxv[0], maxv[1], maxv[2], minlum, minv[0], minv[1], minv[2])
+ + String.format(" maxdif=(%.1f %.1f %.1f)", maxdif[0], maxdif[1], maxdif[2]) : String.format(
+ "prom=%.1f (%.1f %.1f %.1f %.1f) max=%.1f (%.1f %.1f %.1f %.1f) min=%.1f (%.1f %.1f %.1f %.1f)",
+ promlum, prom[0], prom[1], prom[2], prom[3], maxlum, maxv[0], maxv[1], maxv[2], maxv[3], minlum,
+ minv[0], minv[1], minv[2], minv[3])
+ + String.format(" maxdif=(%.1f %.1f %.1f %.1f)", maxdif[0], maxdif[1], maxdif[2], maxdif[3]);
+ }
+
+ public ImageLineStats(ImageLine line) {
+ this.channels = line.channels;
+ if (line.channels < 3)
+ throw new PngjException("ImageLineStats only works for RGB - RGBA");
+ int ch = 0;
+ double lum, x, d;
+ for (int i = 0; i < line.imgInfo.cols; i++) {
+ lum = 0;
+ for (ch = channels - 1; ch >= 0; ch--) {
+ x = int2double(line, line.scanline[i * channels]);
+ if (ch < 3)
+ lum += x;
+ prom[ch] += x;
+ if (x > maxv[ch])
+ maxv[ch] = x;
+ if (x < minv[ch])
+ minv[ch] = x;
+ if (i >= channels) {
+ d = Math.abs(x - int2double(line, line.scanline[i - channels]));
+ if (d > maxdif[ch])
+ maxdif[ch] = d;
+ }
+ }
+ promlum += lum;
+ if (lum > maxlum)
+ maxlum = lum;
+ if (lum < minlum)
+ minlum = lum;
+ }
+ for (ch = 0; ch < channels; ch++) {
+ prom[ch] /= line.imgInfo.cols;
+ }
+ promlum /= (line.imgInfo.cols * 3.0);
+ maxlum /= 3.0;
+ minlum /= 3.0;
+ }
+ }
+
+ /**
+ * integer packed R G B only for bitdepth=8! (does not check!)
+ *
+ **/
+ public static int getPixelRGB8(ImageLine line, int column) {
+ int offset = column * line.channels;
+ return (line.scanline[offset] << 16) + (line.scanline[offset + 1] << 8) + (line.scanline[offset + 2]);
+ }
+
+ public static int getPixelARGB8(ImageLine line, int column) {
+ int offset = column * line.channels;
+ return (line.scanline[offset + 3] << 24) + (line.scanline[offset] << 16) + (line.scanline[offset + 1] << 8)
+ + (line.scanline[offset + 2]);
+ }
+
+ public static void setPixelsRGB8(ImageLine line, int[] rgb) {
+ for (int i = 0, j = 0; i < line.imgInfo.cols; i++) {
+ line.scanline[j++] = ((rgb[i] >> 16) & 0xFF);
+ line.scanline[j++] = ((rgb[i] >> 8) & 0xFF);
+ line.scanline[j++] = ((rgb[i] & 0xFF));
+ }
+ }
+
+ public static void setPixelRGB8(ImageLine line, int col, int r, int g, int b) {
+ col *= line.channels;
+ line.scanline[col++] = r;
+ line.scanline[col++] = g;
+ line.scanline[col] = b;
+ }
+
+ public static void setPixelRGB8(ImageLine line, int col, int rgb) {
+ setPixelRGB8(line, col, (rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
+ }
+
+ public static void setPixelsRGBA8(ImageLine line, int[] rgb) {
+ for (int i = 0, j = 0; i < line.imgInfo.cols; i++) {
+ line.scanline[j++] = ((rgb[i] >> 16) & 0xFF);
+ line.scanline[j++] = ((rgb[i] >> 8) & 0xFF);
+ line.scanline[j++] = ((rgb[i] & 0xFF));
+ line.scanline[j++] = ((rgb[i] >> 24) & 0xFF);
+ }
+ }
+
+ public static void setPixelRGBA8(ImageLine line, int col, int r, int g, int b, int a) {
+ col *= line.channels;
+ line.scanline[col++] = r;
+ line.scanline[col++] = g;
+ line.scanline[col++] = b;
+ line.scanline[col] = a;
+ }
+
+ public static void setPixelRGBA8(ImageLine line, int col, int rgb) {
+ setPixelRGBA8(line, col, (rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, (rgb >> 24) & 0xFF);
+ }
+
+ public static void setValD(ImageLine line, int i, double d) {
+ line.scanline[i] = double2int(line, d);
+ }
+
+ public static int interpol(int a, int b, int c, int d, double dx, double dy) {
+ // a b -> x (0-1)
+ // c d
+ //
+ double e = a * (1.0 - dx) + b * dx;
+ double f = c * (1.0 - dx) + d * dx;
+ return (int) (e * (1 - dy) + f * dy + 0.5);
+ }
+
+ public static double int2double(ImageLine line, int p) {
+ return line.bitDepth == 16 ? p / 65535.0 : p / 255.0;
+ // TODO: replace my multiplication? check for other bitdepths
+ }
+
+ public static double int2doubleClamped(ImageLine line, int p) {
+ // TODO: replace my multiplication?
+ double d = line.bitDepth == 16 ? p / 65535.0 : p / 255.0;
+ return d <= 0.0 ? 0 : (d >= 1.0 ? 1.0 : d);
+ }
+
+ public static int double2int(ImageLine line, double d) {
+ d = d <= 0.0 ? 0 : (d >= 1.0 ? 1.0 : d);
+ return line.bitDepth == 16 ? (int) (d * 65535.0 + 0.5) : (int) (d * 255.0 + 0.5); //
+ }
+
+ public static int double2intClamped(ImageLine line, double d) {
+ d = d <= 0.0 ? 0 : (d >= 1.0 ? 1.0 : d);
+ return line.bitDepth == 16 ? (int) (d * 65535.0 + 0.5) : (int) (d * 255.0 + 0.5); //
+ }
+
+ public static int clampTo_0_255(int i) {
+ return i > 255 ? 255 : (i < 0 ? 0 : i);
+ }
+
+ public static int clampTo_0_65535(int i) {
+ return i > 65535 ? 65535 : (i < 0 ? 0 : i);
+ }
+
+ public static int clampTo_128_127(int x) {
+ return x > 127 ? 127 : (x < -128 ? -128 : x);
+ }
+
+ /**
+ * 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
+ * <p>
+ * If
+ * <code>scale==true<code>, it scales the value (just a bit shift) towards 0-255.
+ * <p>
+ * You probably should use {@link ImageLine#unpackToNewImageLine()}
+ *
+ */
+ public static int[] unpack(ImageInfo imgInfo, int[] src, int[] dst, boolean scale) {
+ int len1 = imgInfo.samplesPerRow;
+ int len0 = imgInfo.samplesPerRowPacked;
+ if (dst == null || dst.length < len1)
+ dst = new int[len1];
+ if (imgInfo.packed)
+ ImageLine.unpackInplaceInt(imgInfo, src, dst, scale);
+ else
+ System.arraycopy(src, 0, dst, 0, len0);
+ return dst;
+ }
+
+ public static byte[] unpack(ImageInfo imgInfo, byte[] src, byte[] dst, boolean scale) {
+ int len1 = imgInfo.samplesPerRow;
+ int len0 = imgInfo.samplesPerRowPacked;
+ if (dst == null || dst.length < len1)
+ dst = new byte[len1];
+ if (imgInfo.packed)
+ ImageLine.unpackInplaceByte(imgInfo, src, dst, scale);
+ else
+ System.arraycopy(src, 0, dst, 0, len0);
+ return dst;
+ }
+
+ /**
+ * Packs scanline (for bitdepth 1-2-4) from array into the scanline
+ * <p>
+ * If <code>scale==true<code>, it scales the value (just a bit shift).
+ *
+ * You probably should use {@link ImageLine#packToNewImageLine()}
+ */
+ public static int[] pack(ImageInfo imgInfo, int[] src, int[] dst, boolean scale) {
+ int len0 = imgInfo.samplesPerRowPacked;
+ if (dst == null || dst.length < len0)
+ dst = new int[len0];
+ if (imgInfo.packed)
+ ImageLine.packInplaceInt(imgInfo, src, dst, scale);
+ else
+ System.arraycopy(src, 0, dst, 0, len0);
+ return dst;
+ }
+
+ public static byte[] pack(ImageInfo imgInfo, byte[] src, byte[] dst, boolean scale) {
+ int len0 = imgInfo.samplesPerRowPacked;
+ if (dst == null || dst.length < len0)
+ dst = new byte[len0];
+ if (imgInfo.packed)
+ ImageLine.packInplaceByte(imgInfo, src, dst, scale);
+ else
+ System.arraycopy(src, 0, dst, 0, len0);
+ return dst;
+ }
+
+ static int getMaskForPackedFormats(int bitDepth) { // Utility function for pack/unpack
+ if (bitDepth == 4)
+ return 0xf0;
+ else if (bitDepth == 2)
+ return 0xc0;
+ else
+ return 0x80; // bitDepth == 1
+ }
+
+ static int getMaskForPackedFormatsLs(int bitDepth) { // Utility function for pack/unpack
+ if (bitDepth == 4)
+ return 0x0f;
+ else if (bitDepth == 2)
+ return 0x03;
+ else
+ return 0x01; // bitDepth == 1
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java new file mode 100644 index 000000000..fb2cf5910 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ImageLines.java @@ -0,0 +1,107 @@ +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. + * + * 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 { + + public final ImageInfo imgInfo; + public final int channels; + public final int bitDepth; + public final SampleType sampleType; + public final boolean samplesUnpacked; + public final int elementsPerRow; + public final int rowOffset; + public final int nRows; + public final int rowStep; + public final int[][] scanlines; + public final byte[][] scanlinesb; + + /** + * Allocates a matrix to store {@code nRows} image rows. See + * {@link ImageLine} and {@link PngReader#readRowsInt()} + * {@link PngReader#readRowsByte()} + * + * @param imgInfo + * @param stype + * @param unpackedMode + * @param rowOffset + * @param nRows + * @param rowStep + */ + public ImageLines(ImageInfo imgInfo, SampleType stype, boolean unpackedMode, int rowOffset, int nRows, int rowStep) { + this.imgInfo = imgInfo; + channels = imgInfo.channels; + bitDepth = imgInfo.bitDepth; + this.sampleType = stype; + this.samplesUnpacked = unpackedMode || !imgInfo.packed; + elementsPerRow = unpackedMode ? imgInfo.samplesPerRow : imgInfo.samplesPerRowPacked; + this.rowOffset = rowOffset; + this.nRows = nRows; + this.rowStep = rowStep; + if (stype == SampleType.INT) { + scanlines = new int[nRows][elementsPerRow]; + scanlinesb = null; + } else if (stype == SampleType.BYTE) { + scanlinesb = new byte[nRows][elementsPerRow]; + scanlines = null; + } else + throw new PngjExceptionInternal("bad ImageLine initialization"); + } + + /** + * 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; + return r < 0 ? 0 : (r < nRows ? r : nRows - 1); + } + + /** + * Same as imageRowToMatrixRow, but returns negative if invalid + */ + public int imageRowToMatrixRowStrict(int imrow) { + imrow -= rowOffset; + int mrow = imrow >= 0 && imrow % rowStep == 0 ? imrow / rowStep : -1; + return mrow < nRows ? mrow : -1; + } + + /** + * Converts from matrix row number (0 : nRows-1) to image row number + * + * @param mrow + * Matrix row number + * @return Image row number. Invalid only if mrow is invalid + */ + public int matrixRowToImageRow(int mrow) { + return mrow * rowStep + rowOffset; + } + + /** + * 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 + */ + public ImageLine getImageLineAtMatrixRow(int mrow) { + if (mrow < 0 || mrow > nRows) + throw new PngjException("Bad row " + mrow + ". Should be positive and less than " + nRows); + ImageLine imline = sampleType == SampleType.INT ? new ImageLine(imgInfo, sampleType, samplesUnpacked, + scanlines[mrow], null) : new ImageLine(imgInfo, sampleType, samplesUnpacked, null, scanlinesb[mrow]); + imline.setRown(matrixRowToImageRow(mrow)); + return imline; + } +} diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngDeinterlacer.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngDeinterlacer.java new file mode 100644 index 000000000..e099c4f6a --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngDeinterlacer.java @@ -0,0 +1,277 @@ +package jogamp.opengl.util.pngj; + +import java.util.Random; + +// you really dont' want to peek inside this +class PngDeinterlacer { + private final ImageInfo imi; + private int pass; // 1-7 + private int rows, cols, dY, dX, oY, oX, oXsamples, dXsamples; // at current pass + // current row in the virtual subsampled image; this incrementes from 0 to cols/dy 7 times + private int currRowSubimg = -1; + // in the real image, this will cycle from 0 to im.rows in different steps, 7 times + private int currRowReal = -1; + + private final int packedValsPerPixel; + private final int packedMask; + private final int packedShift; + + private int[][] imageInt; // FULL image -only used for PngWriter as temporary storage + private short[][] imageShort; + private byte[][] imageByte; + + PngDeinterlacer(ImageInfo iminfo) { + this.imi = iminfo; + pass = 0; + if (imi.packed) { + packedValsPerPixel = 8 / imi.bitDepth; + packedShift = imi.bitDepth; + if (imi.bitDepth == 1) + packedMask = 0x80; + else if (imi.bitDepth == 2) + packedMask = 0xc0; + else + packedMask = 0xf0; + } else { + packedMask = packedShift = packedValsPerPixel = 1;// dont care + } + setPass(1); + setRow(0); + } + + /** this refers to the row currRowSubimg */ + void setRow(int n) { + currRowSubimg = n; + currRowReal = n * dY + oY; + if (currRowReal < 0 || currRowReal >= imi.rows) + throw new PngjExceptionInternal("bad row - this should not happen"); + } + + void setPass(int p) { + if (this.pass == p) + return; + pass = p; + switch (pass) { + case 1: + dY = dX = 8; + oX = oY = 0; + break; + case 2: + dY = dX = 8; + oX = 4; + oY = 0; + break; + case 3: + dX = 4; + dY = 8; + oX = 0; + oY = 4; + break; + case 4: + dX = dY = 4; + oX = 2; + oY = 0; + break; + case 5: + dX = 2; + dY = 4; + oX = 0; + oY = 2; + break; + case 6: + dX = dY = 2; + oX = 1; + oY = 0; + break; + case 7: + dX = 1; + dY = 2; + oX = 0; + oY = 1; + break; + default: + throw new PngjExceptionInternal("bad interlace pass" + pass); + } + rows = (imi.rows - oY) / dY + 1; + if ((rows - 1) * dY + oY >= imi.rows) + rows--; // can be 0 + cols = (imi.cols - oX) / dX + 1; + if ((cols - 1) * dX + oX >= imi.cols) + cols--; // can be 0 + if (cols == 0) + rows = 0; // really... + dXsamples = dX * imi.channels; + oXsamples = oX * imi.channels; + } + + // notice that this is a "partial" deinterlace, it will be called several times for the same row! + void deinterlaceInt(int[] src, int[] dst, boolean readInPackedFormat) { + if (!(imi.packed && readInPackedFormat)) + for (int i = 0, j = oXsamples; i < cols * imi.channels; i += imi.channels, j += dXsamples) + for (int k = 0; k < imi.channels; k++) + dst[j + k] = src[i + k]; + else + deinterlaceIntPacked(src, dst); + } + + // interlaced+packed = monster; this is very clumsy! + private void deinterlaceIntPacked(int[] src, int[] dst) { + int spos, smod, smask; // source byte position, bits to shift to left (01,2,3,4 + int tpos, tmod, p, d; + spos = 0; + smask = packedMask; + smod = -1; + // can this really work? + for (int i = 0, j = oX; i < cols; i++, j += dX) { + spos = i / packedValsPerPixel; + smod += 1; + if (smod >= packedValsPerPixel) + smod = 0; + smask >>= packedShift; // the source mask cycles + if (smod == 0) + smask = packedMask; + tpos = j / packedValsPerPixel; + tmod = j % packedValsPerPixel; + p = src[spos] & smask; + d = tmod - smod; + if (d > 0) + p >>= (d * packedShift); + else if (d < 0) + p <<= ((-d) * packedShift); + dst[tpos] |= p; + } + } + + // yes, duplication of code is evil, normally + void deinterlaceByte(byte[] src, byte[] dst, boolean readInPackedFormat) { + if (!(imi.packed && readInPackedFormat)) + for (int i = 0, j = oXsamples; i < cols * imi.channels; i += imi.channels, j += dXsamples) + for (int k = 0; k < imi.channels; k++) + dst[j + k] = src[i + k]; + else + deinterlacePackedByte(src, dst); + } + + private void deinterlacePackedByte(byte[] src, byte[] dst) { + int spos, smod, smask; // source byte position, bits to shift to left (01,2,3,4 + int tpos, tmod, p, d; + // what the heck are you reading here? I told you would not enjoy this. Try Dostoyevsky or Simone Weil instead + spos = 0; + smask = packedMask; + smod = -1; + // Arrays.fill(dst, 0); + for (int i = 0, j = oX; i < cols; i++, j += dX) { + spos = i / packedValsPerPixel; + smod += 1; + if (smod >= packedValsPerPixel) + smod = 0; + smask >>= packedShift; // the source mask cycles + if (smod == 0) + smask = packedMask; + tpos = j / packedValsPerPixel; + tmod = j % packedValsPerPixel; + p = src[spos] & smask; + d = tmod - smod; + if (d > 0) + p >>= (d * packedShift); + else if (d < 0) + p <<= ((-d) * packedShift); + dst[tpos] |= p; + } + } + + /** + * Is current row the last row for the lass pass?? + */ + boolean isAtLastRow() { + return pass == 7 && currRowSubimg == rows - 1; + } + + /** + * current row number inside the "sub image" + */ + int getCurrRowSubimg() { + return currRowSubimg; + } + + /** + * current row number inside the "real image" + */ + int getCurrRowReal() { + return currRowReal; + } + + /** + * current pass number (1-7) + */ + int getPass() { + return pass; + } + + /** + * How many rows has the current pass? + **/ + int getRows() { + return rows; + } + + /** + * How many columns (pixels) are there in the current row + */ + int getCols() { + return cols; + } + + public int getPixelsToRead() { + return getCols(); + } + + int[][] getImageInt() { + return imageInt; + } + + void setImageInt(int[][] imageInt) { + this.imageInt = imageInt; + } + + short[][] getImageShort() { + return imageShort; + } + + void setImageShort(short[][] imageShort) { + this.imageShort = imageShort; + } + + byte[][] getImageByte() { + return imageByte; + } + + void setImageByte(byte[][] imageByte) { + this.imageByte = imageByte; + } + + static void test() { + Random rand = new Random(); + PngDeinterlacer ih = new PngDeinterlacer(new ImageInfo(rand.nextInt(35) + 1, rand.nextInt(52) + 1, 8, true)); + int np = ih.imi.cols * ih.imi.rows; + System.out.println(ih.imi); + for (int p = 1; p <= 7; p++) { + ih.setPass(p); + for (int row = 0; row < ih.getRows(); row++) { + ih.setRow(row); + int b = ih.getCols(); + np -= b; + System.out.printf("Read %d pixels. Pass:%d Realline:%d cols=%d dX=%d oX=%d last:%b\n", b, ih.pass, + ih.currRowReal, ih.cols, ih.dX, ih.oX, ih.isAtLastRow()); + + } + } + if (np != 0) + throw new PngjExceptionInternal("wtf??" + ih.imi); + } + + public static void main(String[] args) { + test(); + } + +} diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngHelper.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngHelperInternal.java index 1016b1b64..9e64c3eb1 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngHelper.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngHelperInternal.java @@ -4,37 +4,52 @@ import java.io.IOException; import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
-import java.util.HashSet;
-import java.util.Set;
import java.util.zip.CRC32;
/**
- * Some utility static methods.
+ * Some utility static methods for internal use.
* <p>
- * See also <code>FileHelper</code> (if not sandboxed).
+ * Client code should not normally use this class
* <p>
- * Client code should rarely need these methods.
*/
-public class PngHelper {
+public class PngHelperInternal {
/**
* Default charset, used internally by PNG for several things
*/
public static Charset charsetLatin1 = Charset.forName("ISO-8859-1");
- public static Charset charsetUTF8 = Charset.forName("UTF-8"); // only for some chunks
+ /**
+ * UTF-8 is only used for some chunks
+ */
+ public static Charset charsetUTF8 = Charset.forName("UTF-8");
static boolean DEBUG = false;
+ /**
+ * PNG magic bytes
+ */
+ public static byte[] getPngIdSignature() {
+ return new byte[] { -119, 80, 78, 71, 13, 10, 26, 10 };
+ }
+
+ public static int doubleToInt100000(double d) {
+ return (int) (d * 100000.0 + 0.5);
+ }
+
+ public static double intToDouble100000(int i) {
+ return i / 100000.0;
+ }
+
public static int readByte(InputStream is) {
try {
return is.read();
} catch (IOException e) {
- throw new PngjOutputException(e);
+ throw new PngjInputException("error reading byte", e);
}
}
/**
* -1 if eof
- *
+ *
* PNG uses "network byte order"
*/
public static int readInt2(InputStream is) {
@@ -111,7 +126,7 @@ public class PngHelper { }
/**
- * guaranteed to read exactly len bytes. throws error if it cant
+ * guaranteed to read exactly len bytes. throws error if it can't
*/
public static void readBytes(InputStream is, byte[] b, int offset, int len) {
if (len == 0)
@@ -121,7 +136,7 @@ public class PngHelper { while (read < len) {
int n = is.read(b, offset + read, len - read);
if (n < 1)
- throw new RuntimeException("error reading bytes, " + n + " !=" + len);
+ throw new PngjInputException("error reading bytes, " + n + " !=" + len);
read += n;
}
} catch (IOException e) {
@@ -129,6 +144,26 @@ public class PngHelper { }
}
+ public static void skipBytes(InputStream is, long len) {
+ try {
+ 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(e);
+ }
+ }
+
public static void writeBytes(OutputStream os, byte[] b) {
try {
os.write(b);
@@ -150,26 +185,8 @@ public class PngHelper { System.out.println(msg);
}
- public static Set<String> asSet(String... values) {
- return new HashSet<String>(java.util.Arrays.asList(values));
- }
-
- public static Set<String> unionSets(Set<String> set1, Set<String> set2) {
- Set<String> s = new HashSet<String>();
- s.addAll(set1);
- s.addAll(set2);
- return s;
- }
-
- public static Set<String> unionSets(Set<String> set1, Set<String> set2, Set<String> set3) {
- Set<String> s = new HashSet<String>();
- s.addAll(set1);
- s.addAll(set2);
- s.addAll(set3);
- return s;
- }
-
private static final ThreadLocal<CRC32> crcProvider = new ThreadLocal<CRC32>() {
+ @Override
protected CRC32 initialValue() {
return new CRC32();
}
@@ -180,34 +197,74 @@ public class PngHelper { return crcProvider.get();
}
- static final byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 }; // png magic
+ // / filters
+ public static int filterRowNone(int r) {
+ return (int) (r & 0xFF);
+ }
- public static double resMetersToDpi(long res) {
- return (double) res * 0.0254;
+ public static int filterRowSub(int r, int left) {
+ return ((int) (r - left) & 0xFF);
}
- public static long resDpiToMeters(double dpi) {
- return (long) (dpi / 0.0254 + 0.5);
+ public static int filterRowUp(int r, int up) {
+ return ((int) (r - up) & 0xFF);
}
- public static int doubleToInt100000(double d) {
- return (int) (d * 100000.0 + 0.5);
+ public static int filterRowAverage(int r, int left, int up) {
+ return (r - (left + up) / 2) & 0xFF;
}
- public static double intToDouble100000(int i) {
- return i / 100000.0;
+ public static int filterRowPaeth(int r, int left, int up, int upleft) { // a = left, b = above, c = upper left
+ return (r - filterPaethPredictor(left, up, upleft)) & 0xFF;
}
- public static int clampTo_0_255(int i) {
- return i > 255 ? 255 : (i < 0 ? 0 : i);
+ public static int unfilterRowNone(int r) {
+ return (int) (r & 0xFF);
}
- public static int clampTo_0_65535(int i) {
- return i > 65535 ? 65535 : (i < 0 ? 0 : i);
+ public static int unfilterRowSub(int r, int left) {
+ return ((int) (r + left) & 0xFF);
+ }
+
+ public static int unfilterRowUp(int r, int up) {
+ return ((int) (r + up) & 0xFF);
+ }
+
+ public static int unfilterRowAverage(int r, int left, int up) {
+ return (r + (left + up) / 2) & 0xFF;
+ }
+
+ public static int unfilterRowPaeth(int r, int left, int up, int upleft) { // a = left, b = above, c = upper left
+ return (r + filterPaethPredictor(left, up, upleft)) & 0xFF;
+ }
+
+ final static int filterPaethPredictor(final int a, final int b, final int c) { // a = left, b = above, c = upper
+ // left
+ // from http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html
+
+ final int p = a + b - c;// ; initial estimate
+ final int pa = p >= a ? p - a : a - p;
+ final int pb = p >= b ? p - b : b - p;
+ final int pc = p >= c ? p - c : c - p;
+ // ; return nearest of a,b,c,
+ // ; breaking ties in order a,b,c.
+ if (pa <= pb && pa <= pc)
+ return a;
+ else if (pb <= pc)
+ return b;
+ else
+ return c;
+ }
+
+ /*
+ * we put this methods here so as to not pollute the public interface of PngReader
+ */
+ public final static void initCrcForTests(PngReader pngr) {
+ pngr.initCrctest();
}
- public static int clampTo_128_127(int x) {
- return x > 127 ? 127 : (x < -128 ? -128 : x);
+ public final static long getCrctestVal(PngReader pngr) {
+ return pngr.getCrctestVal();
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java index 66c4b49f0..cdad09809 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkInputStream.java @@ -11,23 +11,24 @@ import jogamp.opengl.util.pngj.chunks.ChunkHelper; /**
- * Reads IDAT chunks
+ * Reads a sequence of contiguous IDAT chunks
*/
class PngIDatChunkInputStream extends InputStream {
private final InputStream inputStream;
private final CRC32 crcEngine;
+ private boolean checkCrc = true;
private int lenLastChunk;
private byte[] idLastChunk = new byte[4];
private int toReadThisChunk = 0;
private boolean ended = false;
- private long offset; // offset inside inputstream
+ private long offset; // offset inside whole inputstream (counting bytes before IDAT)
// just informational
static class IdatChunkInfo {
public final int len;
- public final int offset;
+ public final long offset;
- private IdatChunkInfo(int len, int offset) {
+ private IdatChunkInfo(int len, long offset) {
this.len = len;
this.offset = offset;
}
@@ -36,18 +37,20 @@ 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, int offset) {
- this.offset = (long) offset;
+ PngIDatChunkInputStream(InputStream iStream, int lenFirstChunk, long offset) {
+ this.offset = offset;
inputStream = iStream;
- crcEngine = new CRC32();
this.lenLastChunk = lenFirstChunk;
toReadThisChunk = lenFirstChunk;
// we know it's a IDAT
System.arraycopy(ChunkHelper.b_IDAT, 0, idLastChunk, 0, 4);
+ crcEngine = new CRC32();
crcEngine.update(idLastChunk, 0, 4);
foundChunksInfo.add(new IdatChunkInfo(lenLastChunk, offset - 8));
+
// PngHelper.logdebug("IDAT Initial fragment: len=" + lenLastChunk);
if (this.lenLastChunk == 0)
endChunkGoForNext(); // rare, but...
@@ -58,31 +61,33 @@ class PngIDatChunkInputStream extends InputStream { */
@Override
public void close() throws IOException {
- super.close(); // nothing
+ super.close(); // thsi does nothing
}
private void endChunkGoForNext() {
- // Called after readging the last byte of chunk
+ // Called after readging the last byte of one IDAT chunk
// Checks CRC, and read ID from next CHUNK
// Those values are left in idLastChunk / lenLastChunk
// Skips empty IDATS
do {
- int crc = PngHelper.readInt4(inputStream); //
+ int crc = PngHelperInternal.readInt4(inputStream); //
offset += 4;
- int crccalc = (int) crcEngine.getValue();
- if (lenLastChunk > 0 && crc != crccalc)
- throw new PngjBadCrcException("error reading idat; offset: " + offset);
- crcEngine.reset();
- lenLastChunk = PngHelper.readInt4(inputStream);
- if (lenLastChunk < 0)
- throw new PngjInputException("invalid len for chunk: " + lenLastChunk);
+ if (checkCrc) {
+ int crccalc = (int) crcEngine.getValue();
+ if (lenLastChunk > 0 && crc != crccalc)
+ throw new PngjBadCrcException("error reading idat; offset: " + offset);
+ crcEngine.reset();
+ }
+ lenLastChunk = PngHelperInternal.readInt4(inputStream);
toReadThisChunk = lenLastChunk;
- PngHelper.readBytes(inputStream, idLastChunk, 0, 4);
+ PngHelperInternal.readBytes(inputStream, idLastChunk, 0, 4);
offset += 8;
+ // found a NON IDAT chunk? this stream is ended
ended = !Arrays.equals(idLastChunk, ChunkHelper.b_IDAT);
if (!ended) {
- foundChunksInfo.add(new IdatChunkInfo(lenLastChunk, (int) (offset - 8)));
- crcEngine.update(idLastChunk, 0, 4);
+ foundChunksInfo.add(new IdatChunkInfo(lenLastChunk, offset - 8));
+ if (checkCrc)
+ crcEngine.update(idLastChunk, 0, 4);
}
// PngHelper.logdebug("IDAT ended. next len= " + lenLastChunk + " idat?" +
// (!ended));
@@ -91,27 +96,33 @@ 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) {
byte[] dummy = new byte[toReadThisChunk];
- PngHelper.readBytes(inputStream, dummy, 0, toReadThisChunk);
- crcEngine.update(dummy, 0, toReadThisChunk);
+ PngHelperInternal.readBytes(inputStream, dummy, 0, toReadThisChunk);
+ if (checkCrc)
+ crcEngine.update(dummy, 0, toReadThisChunk);
endChunkGoForNext();
}
}
/**
- * 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 {
+ if (ended)
+ return -1; // can happen only when raw reading, see Pngreader.readAndSkipsAllRows()
if (toReadThisChunk == 0)
- throw new RuntimeException("this should not happen");
+ throw new PngjExceptionInternal("this should not happen");
int n = inputStream.read(b, off, len >= toReadThisChunk ? toReadThisChunk : len);
if (n > 0) {
- crcEngine.update(b, off, n);
+ if (checkCrc)
+ crcEngine.update(b, off, n);
this.offset += n;
toReadThisChunk -= n;
}
@@ -150,4 +161,11 @@ class PngIDatChunkInputStream extends InputStream { boolean isEnded() {
return ended;
}
+
+ /**
+ * Disables CRC checking. This can make reading faster
+ */
+ void disableCrcCheck() {
+ checkCrc = false;
+ }
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkOutputStream.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkOutputStream.java index 8b9fa5dae..411d18819 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkOutputStream.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngIDatChunkOutputStream.java @@ -7,23 +7,23 @@ import jogamp.opengl.util.pngj.chunks.ChunkRaw; /**
- * outputs the stream for IDAT chunk , fragmented at fixed size (16384 default).
+ * outputs the stream for IDAT chunk , fragmented at fixed size (32k default).
*/
class PngIDatChunkOutputStream extends ProgressiveOutputStream {
- private static final int SIZE_DEFAULT = 16384;
+ private static final int SIZE_DEFAULT = 32768; // 32k
private final OutputStream outputStream;
PngIDatChunkOutputStream(OutputStream outputStream) {
- this(outputStream, SIZE_DEFAULT);
+ this(outputStream, 0);
}
PngIDatChunkOutputStream(OutputStream outputStream, int size) {
- super(size);
+ super(size > 0 ? size : SIZE_DEFAULT);
this.outputStream = outputStream;
}
@Override
- public final void flushBuffer(byte[] b, int len) {
+ protected final void flushBuffer(byte[] b, int len) {
ChunkRaw c = new ChunkRaw(len, ChunkHelper.b_IDAT, false);
c.data = b;
c.writeChunk(outputStream);
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java index 7343893b6..0412beb8c 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngReader.java @@ -1,415 +1,1001 @@ -package jogamp.opengl.util.pngj;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.zip.InflaterInputStream;
-
-import jogamp.opengl.util.pngj.PngIDatChunkInputStream.IdatChunkInfo;
-import jogamp.opengl.util.pngj.chunks.ChunkHelper;
-import jogamp.opengl.util.pngj.chunks.ChunkList;
-import jogamp.opengl.util.pngj.chunks.ChunkLoadBehaviour;
-import jogamp.opengl.util.pngj.chunks.ChunkRaw;
-import jogamp.opengl.util.pngj.chunks.PngChunk;
-import jogamp.opengl.util.pngj.chunks.PngChunkIHDR;
-import jogamp.opengl.util.pngj.chunks.PngMetadata;
-
-
-/**
- * Reads a PNG image, line by line
- */
-public class PngReader {
- /**
- * Basic image info - final and inmutable.
- */
- public final ImageInfo imgInfo;
- protected final String filename; // not necesarily a file, can be a description - merely informative
-
- private static int MAX_BYTES_CHUNKS_TO_LOAD = 640000;
- private ChunkLoadBehaviour chunkLoadBehaviour = ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS;
-
- private final InputStream is;
- private InflaterInputStream idatIstream;
- private PngIDatChunkInputStream iIdatCstream;
-
- protected int currentChunkGroup = -1;
- protected int rowNum = -1; // current row number
- private int offset = 0;
- private int bytesChunksLoaded; // bytes loaded from anciallary chunks
-
- 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
-
- /**
- * All chunks loaded. Criticals are included, except that all IDAT chunks appearance are replaced by a single
- * dummy-marker IDAT chunk. These might be copied to the PngWriter
- */
- private final ChunkList chunksList;
- private final PngMetadata metadata; // this a wrapper over chunks
-
- /**
- * 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.is = inputStream;
- this.chunksList = new ChunkList(null);
- this.metadata = new PngMetadata(chunksList, true);
- // reads header (magic bytes)
- byte[] pngid = new byte[PngHelper.pngIdBytes.length];
- PngHelper.readBytes(is, pngid, 0, pngid.length);
- offset += pngid.length;
- if (!Arrays.equals(pngid, PngHelper.pngIdBytes))
- throw new PngjInputException("Bad PNG signature");
- // reads first chunk
- currentChunkGroup = ChunkList.CHUNK_GROUP_0_IDHR;
- int clen = PngHelper.readInt4(is);
- offset += 4;
- if (clen != 13)
- throw new RuntimeException("IDHR chunk len != 13 ?? " + clen);
- byte[] chunkid = new byte[4];
- PngHelper.readBytes(is, chunkid, 0, 4);
- if (!Arrays.equals(chunkid, ChunkHelper.b_IHDR))
- throw new PngjInputException("IHDR not found as first chunk??? [" + ChunkHelper.toString(chunkid) + "]");
- offset += 4;
- ChunkRaw chunk = new ChunkRaw(clen, chunkid, true);
- String chunkids = ChunkHelper.toString(chunkid);
- offset += chunk.readChunkData(is);
- PngChunkIHDR ihdr = (PngChunkIHDR) addChunkToList(chunk);
- boolean alpha = (ihdr.getColormodel() & 0x04) != 0;
- boolean palette = (ihdr.getColormodel() & 0x01) != 0;
- boolean grayscale = (ihdr.getColormodel() == 0 || ihdr.getColormodel() == 4);
- imgInfo = new ImageInfo(ihdr.getCols(), ihdr.getRows(), ihdr.getBitspc(), alpha, grayscale, palette);
- imgLine = new ImageLine(imgInfo);
- if (ihdr.getInterlaced() != 0)
- throw new PngjUnsupportedException("PNG interlaced not supported by this library");
- if (ihdr.getFilmeth() != 0 || ihdr.getCompmeth() != 0)
- throw new PngjInputException("compmethod o filtermethod 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());
- // 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];
- }
-
- private static class FoundChunkInfo {
- public final String id;
- public final int len;
- public final int offset;
- public final boolean loaded;
-
- private FoundChunkInfo(String id, int len, int offset, boolean loaded) {
- this.id = id;
- this.len = len;
- this.offset = offset;
- this.loaded = loaded;
- }
-
- public String toString() {
- return "chunk " + id + " len=" + len + " offset=" + offset + (this.loaded ? " " : " X ");
- }
- }
-
- private PngChunk addChunkToList(ChunkRaw chunk) {
- // this requires that the currentChunkGroup is ok
- PngChunk chunkType = PngChunk.factory(chunk, imgInfo);
- if (!chunkType.crit) {
- bytesChunksLoaded += chunk.len;
- }
- if (bytesChunksLoaded > MAX_BYTES_CHUNKS_TO_LOAD) {
- throw new PngjInputException("Chunk exceeded available space (" + MAX_BYTES_CHUNKS_TO_LOAD + ") chunk: "
- + chunk + " See PngReader.MAX_BYTES_CHUNKS_TO_LOAD\n");
- }
- chunksList.appendReadChunk(chunkType, currentChunkGroup);
- return chunkType;
- }
-
- /**
- * Reads chunks before first IDAT. Position before: after IDHR (crc included) Position after: just after the first
- * IDAT chunk id
- *
- * This can be called several times (tentatively), it does nothing if already run
- *
- * (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(), getChunks())
- *
- **/
- public 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 = ChunkList.CHUNK_GROUP_1_AFTERIDHR;
- while (!found) {
- clen = PngHelper.readInt4(is);
- offset += 4;
- if (clen < 0)
- break;
- PngHelper.readBytes(is, chunkid, 0, 4);
- offset += 4;
- if (Arrays.equals(chunkid, ChunkHelper.b_IDAT)) {
- found = true;
- currentChunkGroup = ChunkList.CHUNK_GROUP_4_IDAT;
- // add dummy idat chunk to list
- ChunkRaw chunk = new ChunkRaw(0, chunkid, false);
- addChunkToList(chunk);
- break;
- } else if (Arrays.equals(chunkid, ChunkHelper.b_IEND)) {
- throw new PngjInputException("END chunk found before image data (IDAT) at offset=" + offset);
- }
- ChunkRaw chunk = new ChunkRaw(clen, chunkid, true);
- String chunkids = ChunkHelper.toString(chunkid);
- boolean loadchunk = ChunkHelper.shouldLoad(chunkids, chunkLoadBehaviour);
- offset += chunk.readChunkData(is);
- if (chunkids.equals(ChunkHelper.PLTE))
- currentChunkGroup = ChunkList.CHUNK_GROUP_2_PLTE;
- if (loadchunk)
- addChunkToList(chunk);
- if (chunkids.equals(ChunkHelper.PLTE))
- currentChunkGroup = ChunkList.CHUNK_GROUP_3_AFTERPLTE;
- }
- int idatLen = found ? clen : -1;
- if (idatLen < 0)
- throw new PngjInputException("first idat chunk not found!");
- iIdatCstream = new PngIDatChunkInputStream(is, idatLen, offset);
- idatIstream = new InflaterInputStream(iIdatCstream);
- }
-
- /**
- * Reads (and processes) chunks after last IDAT.
- **/
- private void readLastChunks() {
- // PngHelper.logdebug("idat ended? " + iIdatCstream.isEnded());
- currentChunkGroup = ChunkList.CHUNK_GROUP_5_AFTERIDAT;
- if (!iIdatCstream.isEnded())
- iIdatCstream.forceChunkEnd();
- int clen = iIdatCstream.getLenLastChunk();
- byte[] chunkid = iIdatCstream.getIdLastChunk();
- boolean endfound = false;
- boolean first = true;
- boolean ignore = false;
- while (!endfound) {
- ignore = false;
- if (!first) {
- clen = PngHelper.readInt4(is);
- offset += 4;
- if (clen < 0)
- throw new PngjInputException("bad len " + clen);
- PngHelper.readBytes(is, chunkid, 0, 4);
- offset += 4;
- }
- first = false;
- if (Arrays.equals(chunkid, ChunkHelper.b_IDAT)) {
- // PngHelper.logdebug("extra IDAT chunk len - ignoring : ");
- ignore = true;
- } else if (Arrays.equals(chunkid, ChunkHelper.b_IEND)) {
- currentChunkGroup = ChunkList.CHUNK_GROUP_6_END;
- endfound = true;
- }
- ChunkRaw chunk = new ChunkRaw(clen, chunkid, true);
- String chunkids = ChunkHelper.toString(chunkid);
- boolean loadchunk = ChunkHelper.shouldLoad(chunkids, chunkLoadBehaviour);
- offset += chunk.readChunkData(is);
- if (loadchunk && !ignore) {
- addChunkToList(chunk);
- }
- }
- if (!endfound)
- throw new PngjInputException("end chunk not found - offset=" + offset);
- // PngHelper.logdebug("end chunk found ok offset=" + offset);
- }
-
- /**
- * Calls <code>readRow(int[] buffer, int nrow)</code> using internal ImageLine as buffer. This doesn't allocate or
- * copy anything.
- *
- * @return The ImageLine that also is available inside this object.
- */
- public ImageLine readRow(int nrow) {
- readRow(imgLine.scanline, nrow);
- imgLine.filterUsed = FilterType.getByVal(rowbfilter[0]);
- imgLine.setRown(nrow);
- return imgLine;
- }
-
- /**
- * Reads a line and returns it as a int[] array.
- *
- * You can pass (optionally) a prealocatted buffer.
- *
- * @param buffer
- * Prealocated buffer, or null.
- * @param nrow
- * Row number (0 is top). This is mostly for checking, because this library reads rows in sequence.
- *
- * @return The scanline in the same passwd buffer if it was allocated, a newly allocated one otherwise
- */
- public int[] readRow(int[] buffer, int nrow) {
- if (nrow < 0 || nrow >= imgInfo.rows)
- throw new PngjInputException("invalid line");
- if (nrow != rowNum + 1)
- throw new PngjInputException("invalid line (expected: " + (rowNum + 1));
- if (nrow == 0 && firstChunksNotYetRead())
- readFirstChunks();
- rowNum++;
- if (buffer == null || buffer.length < imgInfo.samplesPerRowP)
- buffer = new int[imgInfo.samplesPerRowP];
- // swap
- byte[] tmp = rowb;
- rowb = rowbprev;
- rowbprev = tmp;
- // loads in rowbfilter "raw" bytes, with filter
- PngHelper.readBytes(idatIstream, rowbfilter, 0, rowbfilter.length);
- rowb[0] = 0;
- unfilterRow();
- rowb[0] = rowbfilter[0];
- convertRowFromBytes(buffer);
- return buffer;
- }
-
- /**
- * This should be called after having read the last line. It reads extra chunks after IDAT, if present.
- */
- public void end() {
- offset = (int) iIdatCstream.getOffset();
- try {
- idatIstream.close();
- } catch (Exception e) {
- }
- readLastChunks();
- try {
- is.close();
- } catch (Exception e) {
- throw new PngjInputException("error closing input stream!", e);
- }
- }
-
- private void convertRowFromBytes(int[] buffer) {
- // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html
- int i, j;
- if (imgInfo.bitDepth <= 8) {
- for (i = 0, j = 1; i < imgInfo.samplesPerRowP; i++) {
- buffer[i] = (rowb[j++] & 0xFF);
- }
- } else { // 16 bitspc
- for (i = 0, j = 1; i < imgInfo.samplesPerRowP; i++) {
- buffer[i] = ((rowb[j++] & 0xFF) << 8) + (rowb[j++] & 0xFF);
- }
- }
- }
-
- private void unfilterRow() {
- 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();
- break;
- case FILTER_SUB:
- unfilterRowSub();
- break;
- case FILTER_UP:
- unfilterRowUp();
- break;
- case FILTER_AVERAGE:
- unfilterRowAverage();
- break;
- case FILTER_PAETH:
- unfilterRowPaeth();
- break;
- default:
- throw new PngjInputException("Filter type " + ftn + " not implemented");
- }
- }
-
- private void unfilterRowNone() {
- for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
- rowb[i] = (byte) (rowbfilter[i]);
- }
- }
-
- private void unfilterRowSub() {
- int i, j;
- for (i = 1; i <= imgInfo.bytesPixel; i++) {
- rowb[i] = (byte) (rowbfilter[i]);
- }
- for (j = 1, i = imgInfo.bytesPixel + 1; i <= imgInfo.bytesPerRow; i++, j++) {
- rowb[i] = (byte) (rowbfilter[i] + rowb[j]);
- }
- }
-
- private void unfilterRowUp() {
- for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
- rowb[i] = (byte) (rowbfilter[i] + rowbprev[i]);
- }
- }
-
- private void unfilterRowAverage() {
- int i, j, x;
- for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imgInfo.bytesPerRow; i++, j++) {
- x = j > 0 ? (rowb[j] & 0xff) : 0;
- rowb[i] = (byte) (rowbfilter[i] + (x + (rowbprev[i] & 0xFF)) / 2);
- }
- }
-
- private void unfilterRowPaeth() {
- int i, j, x, y;
- for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imgInfo.bytesPerRow; i++, j++) {
- x = j > 0 ? (rowb[j] & 0xFF) : 0;
- y = j > 0 ? (rowbprev[j] & 0xFF) : 0;
- rowb[i] = (byte) (rowbfilter[i] + FilterType.filterPaethPredictor(x, rowbprev[i] & 0xFF, y));
- }
- }
-
- public ChunkLoadBehaviour getChunkLoadBehaviour() {
- return chunkLoadBehaviour;
- }
-
- public void setChunkLoadBehaviour(ChunkLoadBehaviour chunkLoadBehaviour) {
- this.chunkLoadBehaviour = chunkLoadBehaviour;
- }
-
- private boolean firstChunksNotYetRead() {
- return currentChunkGroup < ChunkList.CHUNK_GROUP_1_AFTERIDHR;
- }
-
- public ChunkList getChunksList() {
- if (firstChunksNotYetRead())
- readFirstChunks();
- return chunksList;
- }
-
- public PngMetadata getMetadata() {
- if (firstChunksNotYetRead())
- readFirstChunks();
- return metadata;
- }
-
- 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. + */ + @Override + 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 ee8472bf0..2f475aab1 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java @@ -7,54 +7,88 @@ import java.util.List; import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
+import jogamp.opengl.util.pngj.ImageLine.SampleType;
import jogamp.opengl.util.pngj.chunks.ChunkCopyBehaviour;
import jogamp.opengl.util.pngj.chunks.ChunkHelper;
-import jogamp.opengl.util.pngj.chunks.ChunkList;
+import jogamp.opengl.util.pngj.chunks.ChunksList;
+import jogamp.opengl.util.pngj.chunks.ChunksListForWrite;
import jogamp.opengl.util.pngj.chunks.PngChunk;
import jogamp.opengl.util.pngj.chunks.PngChunkIEND;
import jogamp.opengl.util.pngj.chunks.PngChunkIHDR;
+import jogamp.opengl.util.pngj.chunks.PngChunkSkipped;
import jogamp.opengl.util.pngj.chunks.PngChunkTextVar;
import jogamp.opengl.util.pngj.chunks.PngMetadata;
-
/**
- * Writes a PNG image, line by line.
+ * Writes a PNG image
*/
public class PngWriter {
public final ImageInfo imgInfo;
- protected int compLevel = 6; // zip compression level 0 - 9
- private int deflaterStrategy = Deflater.FILTERED;
- protected FilterWriteStrategy filterStrat;
+ private final String filename; // optional, can be a description
+
+ /**
+ * last read row number, starting from 0
+ */
+ protected int rowNum = -1;
+ private final ChunksListForWrite chunksList;
+
+ private final PngMetadata metadata; // high level wrapper over chunkList
+
+ /**
+ * Current chunk grounp, (0-6) already read or reading
+ * <p>
+ * see {@link ChunksList}
+ */
protected int currentChunkGroup = -1;
- protected int rowNum = -1; // current line number
- // current line, one (packed) sample per element (layout differnt from rowb!)
- protected int[] scanline = null;
- protected byte[] rowb = null; // element 0 is filter type!
- protected byte[] rowbprev = null; // rowb prev
- protected byte[] rowbfilter = null; // current line with filter
+ /**
+ * PNG filter strategy
+ */
+ protected FilterWriteStrategy filterStrat;
- protected final OutputStream os;
- protected final String filename; // optional, can be a description
+ /**
+ * zip compression level 0 - 9
+ */
+ private int compLevel = 6;
+ private boolean shouldCloseStream = true; // true: closes stream after ending write
private PngIDatChunkOutputStream datStream;
+
private DeflaterOutputStream datStreamDeflated;
- private final ChunkList chunkList;
- private final PngMetadata metadata; // high level wrapper over chunkList
+ /**
+ * Deflate algortithm compression strategy
+ */
+ private int deflaterStrategy = Deflater.FILTERED;
+
+ private int[] histox = new int[256]; // auxiliar buffer, only used by reportResultsForFilter
+
+ private int idatMaxSize = 0; // 0=use default (PngIDatChunkOutputStream 32768)
+
+ private final OutputStream os;
+
+ protected byte[] rowb = null; // element 0 is filter type!
+ protected byte[] rowbfilter = null; // current line with filter
+
+ protected byte[] rowbprev = null; // rowb prev
+
+ // this only influences the 1-2-4 bitdepth format - and if we pass a ImageLine to writeRow, this is ignored
+ private boolean unpackedMode = false;
public PngWriter(OutputStream outputStream, ImageInfo imgInfo) {
this(outputStream, imgInfo, "[NO FILENAME AVAILABLE]");
}
/**
- * Constructs a new PngWriter from a output stream.
+ * 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.
- *
+ *
* @param outputStream
* Opened stream for binary writing
* @param imgInfo
@@ -67,171 +101,156 @@ public class PngWriter { this.os = outputStream;
this.imgInfo = imgInfo;
// prealloc
- scanline = new int[imgInfo.samplesPerRowP];
rowb = new byte[imgInfo.bytesPerRow + 1];
rowbprev = new byte[rowb.length];
rowbfilter = new byte[rowb.length];
- datStream = new PngIDatChunkOutputStream(this.os);
- chunkList = new ChunkList(imgInfo);
- metadata = new PngMetadata(chunkList, false);
- filterStrat = new FilterWriteStrategy(imgInfo, FilterType.FILTER_DEFAULT);
+ chunksList = new ChunksListForWrite(imgInfo);
+ metadata = new PngMetadata(chunksList);
+ filterStrat = new FilterWriteStrategy(imgInfo, FilterType.FILTER_DEFAULT); // can be changed
}
- /**
- * Write id signature and also "IHDR" chunk
- */
- private void writeSignatureAndIHDR() {
- currentChunkGroup = ChunkList.CHUNK_GROUP_0_IDHR;
- if (datStreamDeflated == null) {
- Deflater def = new Deflater(compLevel);
- def.setStrategy(deflaterStrategy);
- datStreamDeflated = new DeflaterOutputStream(datStream, def, 8192);
+ private void init() {
+ datStream = new PngIDatChunkOutputStream(this.os, idatMaxSize);
+ Deflater def = new Deflater(compLevel);
+ def.setStrategy(deflaterStrategy);
+ datStreamDeflated = new DeflaterOutputStream(datStream, def);
+ writeSignatureAndIHDR();
+ writeFirstChunks();
+ }
+
+ private void reportResultsForFilter(int rown, FilterType type, boolean tentative) {
+ Arrays.fill(histox, 0);
+ int s = 0, v;
+ for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
+ v = rowbfilter[i];
+ if (v < 0)
+ s -= (int) v;
+ else
+ s += (int) v;
+ histox[v & 0xFF]++;
}
- PngHelper.writeBytes(os, PngHelper.pngIdBytes); // signature
- PngChunkIHDR ihdr = new PngChunkIHDR(imgInfo);
- // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
- ihdr.setCols(imgInfo.cols);
- ihdr.setRows(imgInfo.rows);
- ihdr.setBitspc(imgInfo.bitDepth);
- int colormodel = 0;
- if (imgInfo.alpha)
- colormodel += 0x04;
- if (imgInfo.indexed)
- colormodel += 0x01;
- if (!imgInfo.greyscale)
- colormodel += 0x02;
- ihdr.setColormodel(colormodel);
- ihdr.setCompmeth(0); // compression method 0=deflate
- ihdr.setFilmeth(0); // filter method (0)
- ihdr.setInterlaced(0); // we never interlace
- ihdr.createChunk().writeChunk(os);
+ filterStrat.fillResultsForFilter(rown, type, s, histox, tentative);
+ }
+ private void writeEndChunk() {
+ PngChunkIEND c = new PngChunkIEND(imgInfo);
+ c.createRawChunk().writeChunk(os);
}
private void writeFirstChunks() {
int nw = 0;
- currentChunkGroup = ChunkList.CHUNK_GROUP_1_AFTERIDHR;
- nw = chunkList.writeChunks(os, currentChunkGroup);
- currentChunkGroup = ChunkList.CHUNK_GROUP_2_PLTE;
- nw = chunkList.writeChunks(os, currentChunkGroup);
+ currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR;
+ nw = chunksList.writeChunks(os, currentChunkGroup);
+ currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE;
+ nw = chunksList.writeChunks(os, currentChunkGroup);
if (nw > 0 && imgInfo.greyscale)
throw new PngjOutputException("cannot write palette for this format");
if (nw == 0 && imgInfo.indexed)
throw new PngjOutputException("missing palette");
- currentChunkGroup = ChunkList.CHUNK_GROUP_3_AFTERPLTE;
- nw = chunkList.writeChunks(os, currentChunkGroup);
- currentChunkGroup = ChunkList.CHUNK_GROUP_4_IDAT;
+ currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE;
+ nw = chunksList.writeChunks(os, currentChunkGroup);
+ currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT;
}
private void writeLastChunks() { // not including end
- currentChunkGroup = ChunkList.CHUNK_GROUP_5_AFTERIDAT;
- chunkList.writeChunks(os, currentChunkGroup);
+ currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT;
+ chunksList.writeChunks(os, currentChunkGroup);
// should not be unwriten chunks
- List<PngChunk> pending = chunkList.getQueuedChunks();
+ List<PngChunk> pending = chunksList.getQueuedChunks();
if (!pending.isEmpty())
throw new PngjOutputException(pending.size() + " chunks were not written! Eg: " + pending.get(0).toString());
- currentChunkGroup = ChunkList.CHUNK_GROUP_6_END;
- }
-
- private void writeEndChunk() {
- PngChunkIEND c = new PngChunkIEND(imgInfo);
- c.createChunk().writeChunk(os);
+ currentChunkGroup = ChunksList.CHUNK_GROUP_6_END;
}
/**
- * Writes a full image row. 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.
- *
- * @param newrow
- * Array of pixel values
- * @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
+ * Write id signature and also "IHDR" chunk
*/
- public void writeRow(int[] newrow, int rown) {
- if (rown == 0) {
- writeSignatureAndIHDR();
- writeFirstChunks();
- }
- if (rown < -1 || rown > imgInfo.rows)
- throw new RuntimeException("invalid value for row " + rown);
- rowNum++;
- if (rown >= 0 && rowNum != rown)
- throw new RuntimeException("rows must be written in strict consecutive order: tried to write row " + rown
- + ", expected=" + rowNum);
- scanline = newrow;
- // swap
- byte[] tmp = rowb;
- rowb = rowbprev;
- rowbprev = tmp;
- convertRowToBytes();
- filterRow(rown);
- try {
- datStreamDeflated.write(rowbfilter, 0, imgInfo.bytesPerRow + 1);
- } catch (IOException e) {
- throw new PngjOutputException(e);
- }
- }
+ private void writeSignatureAndIHDR() {
+ currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR;
- /**
- * Same as writeRow(int[] newrow, int rown), but does not check row number
- *
- * @param newrow
- */
- public void writeRow(int[] newrow) {
- writeRow(newrow, -1);
- }
+ PngHelperInternal.writeBytes(os, PngHelperInternal.getPngIdSignature()); // signature
+ PngChunkIHDR ihdr = new PngChunkIHDR(imgInfo);
+ // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
+ ihdr.setCols(imgInfo.cols);
+ ihdr.setRows(imgInfo.rows);
+ ihdr.setBitspc(imgInfo.bitDepth);
+ int colormodel = 0;
+ if (imgInfo.alpha)
+ colormodel += 0x04;
+ if (imgInfo.indexed)
+ colormodel += 0x01;
+ if (!imgInfo.greyscale)
+ colormodel += 0x02;
+ ihdr.setColormodel(colormodel);
+ ihdr.setCompmeth(0); // compression method 0=deflate
+ ihdr.setFilmeth(0); // filter method (0)
+ ihdr.setInterlaced(0); // we never interlace
+ ihdr.createRawChunk().writeChunk(os);
- /**
- * Writes line. See writeRow(int[] newrow, int rown)
- */
- public void writeRow(ImageLine imgline, int rownumber) {
- writeRow(imgline.scanline, rownumber);
}
- /**
- * 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)
- */
- public void writeRow(ImageLine imgline) {
- writeRow(imgline.scanline, imgline.getRown());
- }
+ protected void encodeRowFromByte(byte[] row) {
+ if (row.length == imgInfo.samplesPerRowPacked) {
+ // some duplication of code - because this case is typical and it works faster this way
+ int j = 1;
+ if (imgInfo.bitDepth <= 8) {
+ for (byte x : row) { // optimized
+ rowb[j++] = x;
+ }
+ } else { // 16 bitspc
+ for (byte x : row) { // optimized
+ rowb[j] = x;
+ j += 2;
+ }
+ }
+ } else {
+ // perhaps we need to pack?
+ if (row.length >= imgInfo.samplesPerRow && unpackedMode)
+ ImageLine.packInplaceByte(imgInfo, row, row, false); // row is packed in place!
+ if (imgInfo.bitDepth <= 8) {
+ for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) {
+ rowb[j++] = row[i];
+ }
+ } else { // 16 bitspc
+ for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) {
+ rowb[j++] = row[i];
+ rowb[j++] = 0;
+ }
+ }
- /**
- * Finalizes the image creation and closes the stream. This MUST be called after writing the lines.
- */
- public void end() {
- if (rowNum != imgInfo.rows - 1)
- throw new PngjOutputException("all rows have not been written");
- try {
- datStreamDeflated.finish();
- datStream.flush();
- writeLastChunks();
- writeEndChunk();
- os.close();
- } catch (IOException e) {
- throw new PngjOutputException(e);
}
}
- private int[] histox = new int[256]; // auxiliar buffer, only used by reportResultsForFilter
-
- private void reportResultsForFilter(int rown, FilterType type, boolean tentative) {
- Arrays.fill(histox, 0);
- int s = 0, v;
- for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
- v = rowbfilter[i];
- if (v < 0)
- s -= (int) v;
- else
- s += (int) v;
- histox[v & 0xFF]++;
+ protected void encodeRowFromInt(int[] row) {
+ // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html
+ if (row.length == imgInfo.samplesPerRowPacked) {
+ // some duplication of code - because this case is typical and it works faster this way
+ int j = 1;
+ if (imgInfo.bitDepth <= 8) {
+ for (int x : row) { // optimized
+ rowb[j++] = (byte) x;
+ }
+ } else { // 16 bitspc
+ for (int x : row) { // optimized
+ rowb[j++] = (byte) (x >> 8);
+ rowb[j++] = (byte) (x);
+ }
+ }
+ } else {
+ // perhaps we need to pack?
+ if (row.length >= imgInfo.samplesPerRow && unpackedMode)
+ ImageLine.packInplaceInt(imgInfo, row, row, false); // row is packed in place!
+ if (imgInfo.bitDepth <= 8) {
+ for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) {
+ rowb[j++] = (byte) (row[i]);
+ }
+ } else { // 16 bitspc
+ for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) {
+ rowb[j++] = (byte) (row[i] >> 8);
+ rowb[j++] = (byte) (row[i]);
+ }
+ }
}
- filterStrat.fillResultsForFilter(rown, type, s, histox, tentative);
}
private void filterRow(int rown) {
@@ -268,123 +287,98 @@ public class PngWriter { filterRowPaeth();
break;
default:
- throw new PngjOutputException("Filter type " + filterType + " not implemented");
+ throw new PngjUnsupportedException("Filter type " + filterType + " not implemented");
}
reportResultsForFilter(rown, filterType, false);
}
- protected int sumRowbfilter() { // sums absolute value
- int s = 0;
- for (int i = 1; i <= imgInfo.bytesPerRow; i++)
- if (rowbfilter[i] < 0)
- s -= (int) rowbfilter[i];
- else
- s += (int) rowbfilter[i];
- return s;
+ private void prepareEncodeRow(int rown) {
+ if (datStream == null)
+ init();
+ rowNum++;
+ if (rown >= 0 && rowNum != rown)
+ throw new PngjOutputException("rows must be written in order: expected:" + rowNum + " passed:" + rown);
+ // swap
+ byte[] tmp = rowb;
+ rowb = rowbprev;
+ rowbprev = tmp;
}
- protected void filterRowNone() {
- for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
- rowbfilter[i] = (byte) rowb[i];
+ private void filterAndSend(int rown) {
+ filterRow(rown);
+ try {
+ datStreamDeflated.write(rowbfilter, 0, imgInfo.bytesPerRow + 1);
+ } catch (IOException e) {
+ throw new PngjOutputException(e);
}
}
- protected void filterRowSub() {
- int i, j;
- for (i = 1; i <= imgInfo.bytesPixel; i++)
- rowbfilter[i] = (byte) rowb[i];
- for (j = 1, i = imgInfo.bytesPixel + 1; i <= imgInfo.bytesPerRow; i++, j++) {
- rowbfilter[i] = (byte) (rowb[i] - rowb[j]);
+ protected void filterRowAverage() {
+ int i, j, imax;
+ imax = imgInfo.bytesPerRow;
+ for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imax; i++, j++) {
+ rowbfilter[i] = (byte) (rowb[i] - ((rowbprev[i] & 0xFF) + (j > 0 ? (rowb[j] & 0xFF) : 0)) / 2);
}
}
- protected void filterRowUp() {
+ protected void filterRowNone() {
for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
- rowbfilter[i] = (byte) (rowb[i] - rowbprev[i]);
- }
- }
-
- protected void filterRowAverage() {
- int i, j;
- for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imgInfo.bytesPerRow; i++, j++) {
- rowbfilter[i] = (byte) (rowb[i] - ((rowbprev[i] & 0xFF) + (j > 0 ? (rowb[j] & 0xFF) : 0)) / 2);
+ rowbfilter[i] = (byte) rowb[i];
}
}
protected void filterRowPaeth() {
- int i, j;
- for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imgInfo.bytesPerRow; i++, j++) {
- rowbfilter[i] = (byte) (rowb[i] - FilterType.filterPaethPredictor(j > 0 ? (rowb[j] & 0xFF) : 0,
- rowbprev[i] & 0xFF, j > 0 ? (rowbprev[j] & 0xFF) : 0));
+ int i, j, imax;
+ imax = imgInfo.bytesPerRow;
+ for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imax; i++, j++) {
+ // rowbfilter[i] = (byte) (rowb[i] - PngHelperInternal.filterPaethPredictor(j > 0 ? (rowb[j] & 0xFF) : 0,
+ // rowbprev[i] & 0xFF, j > 0 ? (rowbprev[j] & 0xFF) : 0));
+ rowbfilter[i] = (byte) PngHelperInternal.filterRowPaeth(rowb[i], j > 0 ? (rowb[j] & 0xFF) : 0,
+ rowbprev[i] & 0xFF, j > 0 ? (rowbprev[j] & 0xFF) : 0);
}
}
- protected void convertRowToBytes() {
- // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html
+ protected void filterRowSub() {
int i, j;
- if (imgInfo.bitDepth <= 8) {
- for (i = 0, j = 1; i < imgInfo.samplesPerRowP; i++) {
- rowb[j++] = (byte) (scanline[i]);
- }
- } else { // 16 bitspc
- for (i = 0, j = 1; i < imgInfo.samplesPerRowP; i++) {
- // x = (int) (scanline[i]) & 0xFFFF;
- rowb[j++] = (byte) (scanline[i] >> 8);
- rowb[j++] = (byte) (scanline[i]);
- }
+ for (i = 1; i <= imgInfo.bytesPixel; i++)
+ rowbfilter[i] = (byte) rowb[i];
+ for (j = 1, i = imgInfo.bytesPixel + 1; i <= imgInfo.bytesPerRow; i++, j++) {
+ // !!! rowbfilter[i] = (byte) (rowb[i] - rowb[j]);
+ rowbfilter[i] = (byte) PngHelperInternal.filterRowSub(rowb[i], rowb[j]);
}
}
- // /// several getters / setters - all this setters are optional
-
- /**
- * Filename or description, from the optional constructor argument.
- */
- public String getFilename() {
- return filename;
+ protected void filterRowUp() {
+ for (int i = 1; i <= imgInfo.bytesPerRow; i++) {
+ // rowbfilter[i] = (byte) (rowb[i] - rowbprev[i]); !!!
+ rowbfilter[i] = (byte) PngHelperInternal.filterRowUp(rowb[i], rowbprev[i]);
+ }
}
- /**
- * Sets internal prediction filter type, or strategy to choose it.
- * <p>
- * This must be called just after constructor, before starting writing.
- * <p>
- * 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
- */
- public void setFilterType(FilterType filterType) {
- filterStrat = new FilterWriteStrategy(imgInfo, filterType);
+ protected int sumRowbfilter() { // sums absolute value
+ int s = 0;
+ for (int i = 1; i <= imgInfo.bytesPerRow; i++)
+ if (rowbfilter[i] < 0)
+ s -= (int) rowbfilter[i];
+ else
+ s += (int) rowbfilter[i];
+ return s;
}
/**
- * Sets compression level of ZIP algorithm.
+ * copy chunks from reader - copy_mask : see ChunksToWrite.COPY_XXX
* <p>
- * This must be called just after constructor, before starting writing.
+ * If we are after idat, only considers those chunks after IDAT in PngReader
* <p>
- * See also setFilterType()
- *
- * @param compLevel
- * between 0 and 9 (default:6 , recommended: 6 or more)
- */
- public void setCompLevel(int compLevel) {
- if (compLevel < 0 || compLevel > 9)
- throw new PngjException("Compression level invalid (" + compLevel + ") Must be 0..9");
- this.compLevel = compLevel;
- }
-
- /**
- * copy chunks from reader - copy_mask : see ChunksToWrite.COPY_XXX
- *
- * If we are after idat, only considers those chunks after IDAT in PngReader TODO: this should be more customizable
+ * TODO: this should be more customizable
*/
private void copyChunks(PngReader reader, int copy_mask, boolean onlyAfterIdat) {
- boolean idatDone = currentChunkGroup >= ChunkList.CHUNK_GROUP_4_IDAT;
+ boolean idatDone = currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT;
+ if (onlyAfterIdat && reader.getCurrentChunkGroup() < ChunksList.CHUNK_GROUP_6_END)
+ throw new PngjExceptionInternal("tried to copy last chunks but reader has not ended");
for (PngChunk chunk : reader.getChunksList().getChunks()) {
int group = chunk.getChunkGroup();
- if (group < ChunkList.CHUNK_GROUP_4_IDAT && idatDone)
+ if (group < ChunksList.CHUNK_GROUP_4_IDAT && idatDone)
continue;
boolean copy = false;
if (chunk.crit) {
@@ -413,9 +407,11 @@ public class PngWriter { && !(ChunkHelper.isUnknown(chunk) || text || chunk.id.equals(ChunkHelper.hIST) || chunk.id
.equals(ChunkHelper.tIME)))
copy = true;
+ if (chunk instanceof PngChunkSkipped)
+ copy = false;
}
if (copy) {
- chunkList.queueChunk(PngChunk.cloneChunk(chunk, imgInfo), !chunk.allowsMultiple(), false);
+ chunksList.queue(PngChunk.cloneChunk(chunk, imgInfo));
}
}
}
@@ -423,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);
@@ -438,25 +436,254 @@ 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);
}
- public ChunkList getChunkList() {
- return chunkList;
+ /**
+ * Computes compressed size/raw size, approximate.
+ * <p>
+ * Actually: compressed size = total size of IDAT data , raw size =
+ * uncompressed pixel bytes = rows * (bytesPerRow + 1).
+ *
+ * This must be called after pngw.end()
+ */
+ public double computeCompressionRatio() {
+ if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END)
+ throw new PngjOutputException("must be called after end()");
+ double compressed = (double) datStream.getCountFlushed();
+ double raw = (imgInfo.bytesPerRow + 1) * imgInfo.rows;
+ return compressed / raw;
}
+ /**
+ * Finalizes the image creation and closes the stream. This MUST be called
+ * after writing the lines.
+ */
+ public void end() {
+ if (rowNum != imgInfo.rows - 1)
+ throw new PngjOutputException("all rows have not been written");
+ try {
+ datStreamDeflated.finish();
+ datStream.flush();
+ writeLastChunks();
+ writeEndChunk();
+ if (shouldCloseStream)
+ os.close();
+ } catch (IOException e) {
+ throw new PngjOutputException(e);
+ }
+ }
+
+ /**
+ * returns the chunks list (queued and writen chunks)
+ */
+ public ChunksListForWrite getChunksList() {
+ return chunksList;
+ }
+
+ /**
+ * Filename or description, from the optional constructor argument.
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * High level wrapper over chunksList for metadata handling
+ */
public PngMetadata getMetadata() {
return metadata;
}
+ /**
+ * Sets compression level of ZIP algorithm.
+ * <p>
+ * This must be called just after constructor, before starting writing.
+ * <p>
+ * See also setFilterType()
+ *
+ * @param compLevel
+ * between 0 and 9 (default:6 , recommended: 6 or more)
+ */
+ public void setCompLevel(int compLevel) {
+ if (compLevel < 0 || compLevel > 9)
+ throw new PngjOutputException("Compression level invalid (" + compLevel + ") Must be 0..9");
+ this.compLevel = compLevel;
+ }
+
+ /**
+ * Sets internal prediction filter type, or strategy to choose it.
+ * <p>
+ * This must be called just after constructor, before starting writing.
+ * <p>
+ * 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
+ */
+ 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
+ * <p>
+ *
+ * @param idatMaxSize
+ * default=0 : use defaultSize (32K)
+ */
+ public void setIdatMaxSize(int idatMaxSize) {
+ this.idatMaxSize = idatMaxSize;
+ }
+
+ /**
+ * if true, input stream will be closed after ending write
+ * <p>
+ * default=true
+ */
+ public void setShouldCloseStream(boolean shouldCloseStream) {
+ this.shouldCloseStream = shouldCloseStream;
+ }
+
+ /**
+ * Deflater strategy: one of Deflater.FILTERED Deflater.HUFFMAN_ONLY
+ * Deflater.DEFAULT_STRATEGY
+ * <p>
+ * Default: Deflater.FILTERED . This should be changed very rarely.
+ */
+ public void setDeflaterStrategy(int deflaterStrategy) {
+ this.deflaterStrategy = deflaterStrategy;
+ }
+
+ /**
+ * 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)
+ */
+ public void writeRow(ImageLine imgline) {
+ writeRow(imgline.scanline, imgline.getRown());
+ }
+
+ /**
+ * Writes line. See writeRow(int[] newrow, int rown)
+ *
+ * The <tt>packed</tt> flag of the imageline is honoured!
+ *
+ * @see #writeRowInt(int[], int)
+ */
+ public void writeRow(ImageLine imgline, int rownumber) {
+ unpackedMode = imgline.samplesUnpacked;
+ if (imgline.sampleType == SampleType.INT)
+ writeRowInt(imgline.scanline, rownumber);
+ else
+ writeRowByte(imgline.scanlineb, rownumber);
+ }
+
+ /**
+ * Same as writeRow(int[] newrow, int rown), but does not check row number
+ *
+ * @param newrow
+ */
+ public void writeRow(int[] newrow) {
+ writeRow(newrow, -1);
+ }
+
+ /**
+ * Alias to writeRowInt
+ *
+ * @see #writeRowInt(int[], int)
+ */
+ public void writeRow(int[] newrow, int rown) {
+ writeRowInt(newrow, rown);
+ }
+
+ /**
+ * 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.
+ * <p>
+ * 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)
+ * @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
+ */
+ public void writeRowInt(int[] newrow, int rown) {
+ prepareEncodeRow(rown);
+ encodeRowFromInt(newrow);
+ filterAndSend(rown);
+ }
+
+ /**
+ * 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)
+ */
+ public void writeRowByte(byte[] newrow, int rown) {
+ prepareEncodeRow(rown);
+ encodeRowFromByte(newrow);
+ filterAndSend(rown);
+ }
+
+ /**
+ * Writes all the pixels, calling writeRowInt() for each image row
+ */
+ public void writeRowsInt(int[][] image) {
+ for (int i = 0; i < imgInfo.rows; i++)
+ writeRowInt(image[i], i);
+ }
+
+ /**
+ * Writes all the pixels, calling writeRowByte() for each image row
+ */
+ public void writeRowsByte(byte[][] image) {
+ for (int i = 0; i < imgInfo.rows; i++)
+ writeRowByte(image[i], i);
+ }
+
+ public boolean isUnpackedMode() {
+ return unpackedMode;
+ }
+
+ /**
+ * 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.
+ * <p>
+ * 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/PngjException.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngjException.java index 4a45cb5bf..97e24fc73 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngjException.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngjException.java @@ -2,9 +2,9 @@ package jogamp.opengl.util.pngj; /**
* Generic exception
- *
+ *
* @author Hernan J Gonzalez
- *
+ *
*/
public class PngjException extends RuntimeException {
private static final long serialVersionUID = 1L;
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java new file mode 100644 index 000000000..5da70de7b --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngjExceptionInternal.java @@ -0,0 +1,24 @@ +package jogamp.opengl.util.pngj;
+
+/**
+ * Exception for anomalous internal problems (sort of asserts) that point to
+ * some issue with the library
+ *
+ * @author Hernan J Gonzalez
+ *
+ */
+public class PngjExceptionInternal extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public PngjExceptionInternal(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public PngjExceptionInternal(String message) {
+ super(message);
+ }
+
+ public PngjExceptionInternal(Throwable cause) {
+ super(cause);
+ }
+}
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 bbec247fb..4516a0886 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/ProgressiveOutputStream.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/ProgressiveOutputStream.java @@ -4,10 +4,12 @@ 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;
+ private long countFlushed = 0;
public ProgressiveOutputStream(int size) {
this.size = size;
@@ -49,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) {
@@ -60,6 +62,7 @@ abstract class ProgressiveOutputStream extends ByteArrayOutputStream { if (nb == 0)
return;
flushBuffer(buf, nb);
+ countFlushed += nb;
int bytesleft = count - nb;
count = bytesleft;
if (bytesleft > 0)
@@ -67,5 +70,9 @@ abstract class ProgressiveOutputStream extends ByteArrayOutputStream { }
}
- public abstract void flushBuffer(byte[] b, int n);
+ protected abstract void flushBuffer(byte[] b, int n);
+
+ public long getCountFlushed() {
+ return countFlushed;
+ }
}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkCopyBehaviour.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkCopyBehaviour.java index 43c0cb135..a2d976fac 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkCopyBehaviour.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkCopyBehaviour.java @@ -1,8 +1,9 @@ package jogamp.opengl.util.pngj.chunks;
/**
- * Chunk copy policy to apply when copyng from a pngReader to a pngWriter http://www.w3.org/TR/PNG/#14
+ * Chunk copy policy to apply when copyng from a pngReader to a pngWriter.
* <p>
+ * http://www.w3.org/TR/PNG/#14 <br>
* These are masks, can be OR-ed
**/
public class ChunkCopyBehaviour {
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 26dafd4eb..4e8bf5635 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkHelper.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkHelper.java @@ -1,134 +1,299 @@ -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.Set;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.InflaterInputStream;
-
-import jogamp.opengl.util.pngj.PngHelper;
-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";
-
- public static Set<String> KNOWN_CHUNKS_CRITICAL = PngHelper.asSet(IHDR, PLTE, IDAT, IEND);
-
- public static byte[] toBytes(String x) {
- return x.getBytes(PngHelper.charsetLatin1);
- }
-
- public static String toString(byte[] x) {
- return new String(x, PngHelper.charsetLatin1);
- }
-
- public static boolean isCritical(String id) { // critical chunk ?
- // first letter is uppercase
- return (Character.isUpperCase(id.charAt(0)));
- }
-
- public static boolean isPublic(String id) { // public chunk?
- // second letter is uppercase
- return (Character.isUpperCase(id.charAt(1)));
- }
-
- /**
- * "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;
- }
-
- public static boolean isSafeToCopy(String id) { // safe to copy?
- // fourth letter is lower case
- return (!Character.isUpperCase(id.charAt(3)));
- }
-
- public static int posNullByte(byte[] b) {
- for (int i = 0; i < b.length; i++)
- if (b[i] == 0)
- return i;
- return -1;
- }
-
- 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;
- }
-
-}
+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>() { + @Override + protected Inflater initialValue() { + return new Inflater(); + } + }; + + private static final ThreadLocal<Deflater> deflaterProvider = new ThreadLocal<Deflater>() { + @Override + 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/ChunkList.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkList.java deleted file mode 100644 index badbbd0e8..000000000 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkList.java +++ /dev/null @@ -1,282 +0,0 @@ -package jogamp.opengl.util.pngj.chunks;
-
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngjException;
-
-
-/**
- * All chunks that form an image, read or to be written
- *
- * chunks include all chunks, but IDAT is a single pseudo chunk without data
- **/
-public class ChunkList {
- // ref: http://www.w3.org/TR/PNG/#table53
- public static final int CHUNK_GROUP_0_IDHR = 0; // required - single
- public static final int CHUNK_GROUP_1_AFTERIDHR = 1; // optional - multiple
- public static final int CHUNK_GROUP_2_PLTE = 2; // optional - single
- public static final int CHUNK_GROUP_3_AFTERPLTE = 3; // optional - multple
- public static final int CHUNK_GROUP_4_IDAT = 4; // required (single pseudo chunk)
- public static final int CHUNK_GROUP_5_AFTERIDAT = 5; // optional - multple
- public static final int CHUNK_GROUP_6_END = 6; // only 1 chunk - requried
-
- /**
- * All chunks, read, written (does not include IHDR, IDAT, END for written)
- */
- private List<PngChunk> chunks = new ArrayList<PngChunk>();
-
- /**
- * chunks not yet writen - does not include IHDR, IDAT, END, perhaps yes PLTE
- */
- private Set<PngChunk> queuedChunks = new LinkedHashSet<PngChunk>();
-
- final ImageInfo imageInfo; // only required for writing
-
- public ChunkList(ImageInfo imfinfo) {
- this.imageInfo = imfinfo;
- }
-
- /**
- * Adds chunk in next position. This is used when reading
- */
- public void appendReadChunk(PngChunk chunk, int chunkGroup) {
- chunk.setChunkGroup(chunkGroup);
- chunks.add(chunk);
- }
-
- public List<PngChunk> getById(String id, boolean includeQueued, boolean includeProcessed) {
- List<PngChunk> list = new ArrayList<PngChunk>();
- if (includeQueued)
- for (PngChunk c : queuedChunks)
- if (c.id.equals(id))
- list.add(c);
- if (includeProcessed)
- for (PngChunk c : chunks)
- if (c.id.equals(id))
- list.add(c);
- return list;
- }
-
- /**
- * Remove Chunk: only from queued
- */
- public boolean removeChunk(PngChunk c) {
- return queuedChunks.remove(c);
- }
-
- /**
- * add chunk to write queue
- */
- public void queueChunk(PngChunk chunk, boolean replace, boolean priority) {
- chunk.setPriority(priority);
- if (replace) {
- List<PngChunk> current = getById(chunk.id, true, false);
- for (PngChunk chunk2 : current)
- removeChunk(chunk2);
- }
- queuedChunks.add(chunk);
- }
-
- /**
- * 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)
- return c.id.equals(ChunkHelper.PLTE);
- if (currentGroup % 2 == 0)
- throw new RuntimeException("?");
- int minChunkGroup, maxChunkGroup;
- if (c.mustGoBeforePLTE())
- minChunkGroup = maxChunkGroup = ChunkList.CHUNK_GROUP_1_AFTERIDHR;
- else if (c.mustGoBeforeIDAT()) {
- maxChunkGroup = ChunkList.CHUNK_GROUP_3_AFTERPLTE;
- minChunkGroup = c.mustGoAfterPLTE() ? ChunkList.CHUNK_GROUP_3_AFTERPLTE : ChunkList.CHUNK_GROUP_1_AFTERIDHR;
- } else {
- maxChunkGroup = ChunkList.CHUNK_GROUP_5_AFTERIDAT;
- minChunkGroup = ChunkList.CHUNK_GROUP_1_AFTERIDHR;
- }
-
- int preferred = maxChunkGroup;
- if (c.isWritePriority())
- preferred = minChunkGroup;
- if (ChunkHelper.isUnknown(c) && c.getChunkGroup() > 0)
- preferred = c.getChunkGroup();
- if (currentGroup == preferred)
- return true;
- if (currentGroup > preferred && currentGroup <= maxChunkGroup)
- return true;
- return false;
- }
-
- public int writeChunks(OutputStream os, int currentGroup) {
- int cont = 0;
- Iterator<PngChunk> it = queuedChunks.iterator();
- while (it.hasNext()) {
- PngChunk c = it.next();
- if (!shouldWrite(c, currentGroup))
- continue;
- c.write(os);
- chunks.add(c);
- c.setChunkGroup(currentGroup);
- it.remove();
- cont++;
- }
- return cont;
- }
-
- /**
- * returns a copy of processed (read or writen) chunks
- */
- public List<PngChunk> getChunks() {
- return new ArrayList<PngChunk>(chunks);
- }
-
- public List<String> getChunksUnkown() {
- List<String> l = new ArrayList<String>();
- for (PngChunk chunk : chunks)
- if (ChunkHelper.isUnknown(chunk))
- l.add(chunk.id);
- return l;
- }
-
- /**
- * returns a copy of queued (for write) chunks
- */
- public List<PngChunk> getQueuedChunks() {
- return new ArrayList<PngChunk>(queuedChunks);
- }
-
- /**
- * behaviour:
- *
- * a chunk already processed matches : exception a chunk queued matches and overwrite=true: replace it , return true
- * a chunk queued matches and overwrite=false: do nothing, return false no matching: set it, return true
- *
- * @param c
- * @param overwriteIfPresent
- * @return true if added chunk
- */
- public boolean setChunk(PngChunk c, boolean overwriteIfPresent) {
- List<PngChunk> list = getMatching(c, false, true); // processed
- if (!list.isEmpty())
- throw new PngjException("chunk " + c.id + " already set ");
- list = getMatching(c, true, false); // queued
- if (!list.isEmpty()) {
- if (overwriteIfPresent) {
- for (PngChunk cx : list)
- removeChunk(cx);
- queueChunk(c, false, false);
- return true;
- }
- return false;
- }
- queueChunk(c, false, false);
- return true;
- }
-
- /**
- * returns only one chunk or null if nothing found - does not include queued
- *
- * If innerid!=null , the chunk is assumed to be PngChunkTextVar or PngChunkSPLT, and filtered by that id
- *
- * If more than one chunk (after filtering by inner id) is found, then an exception is thrown (failifMultiple=true)
- * or the last one is returned (failifMultiple=false)
- **/
- public PngChunk getChunk1(String id, String innerid, boolean failIfMultiple) {
- List<PngChunk> list = getChunks(id);
- if (list.isEmpty())
- return null;
- if (innerid != null) {
- List<PngChunk> list2 = new ArrayList<PngChunk>();
- for (PngChunk c : list) {
- if (c instanceof PngChunkTextVar)
- if (((PngChunkTextVar) c).getKey().equals(innerid))
- list2.add(c);
- if (c instanceof PngChunkSPLT)
- if (((PngChunkSPLT) c).getPalName().equals(innerid))
- list2.add(c);
- }
- list = list2;
- }
- if (list.isEmpty())
- return null;
- if (list.size() > 1 && failIfMultiple)
- throw new PngjException("unexpected multiple chunks id=" + id);
- return list.get(list.size() - 1);
- }
-
- public PngChunk getChunk1(String id) {
- return getChunk1(id, null, true);
- }
-
- public List<PngChunk> getChunks(String id) { // not including queued
- return getById(id, false, true);
- }
-
- private List<PngChunk> getMatching(PngChunk cnew, boolean includeQueued, boolean includeProcessed) {
- List<PngChunk> list = new ArrayList<PngChunk>();
- if (includeQueued)
- for (PngChunk c : getQueuedChunks())
- if (matches(cnew, c))
- list.add(c);
- if (includeProcessed)
- for (PngChunk c : getChunks())
- if (matches(cnew, c))
- list.add(c);
- return list;
- }
-
- /**
- * MY adhoc criteria: two chunks "match" 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)
- *
- * @return true if "matches"
- */
- public static boolean matches(PngChunk c2, PngChunk c1) {
- 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 String toString() {
- return "ChunkList: processed: " + chunks.size() + " queue: " + queuedChunks.size();
- }
-
- /**
- * for debugging
- */
- public String toStringFull() {
- StringBuilder sb = new StringBuilder(toString());
- sb.append("\n Processed:\n");
- for (PngChunk chunk : chunks) {
- sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n");
- }
- if (!queuedChunks.isEmpty()) {
- sb.append(" Queued:\n");
- for (PngChunk chunk : chunks) {
- sb.append(chunk).append("\n");
- }
-
- }
- return sb.toString();
- }
-
-}
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 a3f85355c..1fa00380a 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkLoadBehaviour.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkLoadBehaviour.java @@ -1,10 +1,28 @@ package jogamp.opengl.util.pngj.chunks;
+/**
+ * Defines gral strategy about what to do with ancillary (non-critical) chunks
+ * when reading
+ */
public enum ChunkLoadBehaviour {
- // what to do with non critical chunks when reading?
- LOAD_CHUNK_NEVER, /* ignore non-critical chunks */
- LOAD_CHUNK_KNOWN, /* load chunk if 'known' */
- LOAD_CHUNK_IF_SAFE, /* load chunk if 'known' or safe to copy */
- LOAD_CHUNK_ALWAYS /* load chunk always */
- ;
+ /**
+ * All non-critical chunks are skipped
+ */
+ LOAD_CHUNK_NEVER,
+ /**
+ * Ancillary chunks are loaded only if 'known' (registered with the
+ * factory).
+ */
+ LOAD_CHUNK_KNOWN,
+ /**
+ *
+ * Load chunk if "known" or "safe to copy".
+ */
+ LOAD_CHUNK_IF_SAFE,
+ /**
+ * Load all chunks. <br>
+ * 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/ChunkPredicate.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkPredicate.java new file mode 100644 index 000000000..4695ccf44 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkPredicate.java @@ -0,0 +1,14 @@ +package jogamp.opengl.util.pngj.chunks; + +/** + * Decides if another chunk "matches", according to some criterion + */ +public interface ChunkPredicate { + /** + * The other chunk matches with this one + * + * @param chunk + * @return true if match + */ + boolean match(PngChunk chunk); +} 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 6770d5e95..dcb1958df 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkRaw.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunkRaw.java @@ -5,24 +5,51 @@ import java.io.InputStream; import java.io.OutputStream;
import java.util.zip.CRC32;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjBadCrcException;
import jogamp.opengl.util.pngj.PngjOutputException;
/**
- * Wraps the raw chunk data Short lived object, to be created while serialing/deserializing Do not reuse it for
- * different chunks
- *
- * see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
+ * Raw (physical) chunk.
+ * <p>
+ * 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.
+ */
public final int len;
- public final byte[] idbytes = new byte[4]; // 4 bytes
- public byte[] data = null; // crc not included
+
+ /**
+ * A 4-byte chunk type code. uppercase and lowercase ASCII letters
+ */
+ 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
+ */
+ 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.
+ */
private int crcval = 0;
- // public int offset=-1; // only for read chunks - informational
+ /**
+ * @param len
+ * : data len
+ * @param idbytes
+ * : chunk type (deep copied)
+ * @param alloc
+ * : it true, the data array will be allocced
+ */
public ChunkRaw(int len, byte[] idbytes, boolean alloc) {
this.len = len;
System.arraycopy(idbytes, 0, this.idbytes, 0, 4);
@@ -30,54 +57,60 @@ public class ChunkRaw { allocData();
}
- public void writeChunk(OutputStream os) {
- if (idbytes.length != 4)
- throw new PngjOutputException("bad chunkid [" + ChunkHelper.toString(idbytes) + "]");
- computeCrc();
- PngHelper.writeInt4(os, len);
- PngHelper.writeBytes(os, idbytes);
- if (len > 0)
- PngHelper.writeBytes(os, data, 0, len);
- // System.err.println("writing chunk " + this.toString() + "crc=" + crcval);
-
- PngHelper.writeInt4(os, crcval);
+ private void allocData() {
+ if (data == null || data.length < len)
+ data = new byte[len];
}
/**
- * called after setting data, before writing to os
+ * this is called after setting data, before writing to os
*/
- private void computeCrc() {
- CRC32 crcengine = PngHelper.getCRC();
+ private int computeCrc() {
+ CRC32 crcengine = PngHelperInternal.getCRC();
crcengine.reset();
crcengine.update(idbytes, 0, 4);
if (len > 0)
crcengine.update(data, 0, len); //
- crcval = (int) crcengine.getValue();
+ return (int) crcengine.getValue();
}
- public String toString() {
- return "chunkid=" + ChunkHelper.toString(idbytes) + " len=" + len;
+ /**
+ * Computes the CRC and writes to the stream. If error, a
+ * PngjOutputException is thrown
+ */
+ public void writeChunk(OutputStream os) {
+ if (idbytes.length != 4)
+ throw new PngjOutputException("bad chunkid [" + ChunkHelper.toString(idbytes) + "]");
+ crcval = computeCrc();
+ PngHelperInternal.writeInt4(os, len);
+ PngHelperInternal.writeBytes(os, idbytes);
+ if (len > 0)
+ PngHelperInternal.writeBytes(os, data, 0, len);
+ PngHelperInternal.writeInt4(os, crcval);
}
/**
- * 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) {
- PngHelper.readBytes(is, data, 0, len);
- int crcori = PngHelper.readInt4(is);
- computeCrc();
- if (crcori != crcval)
- throw new PngjBadCrcException("crc invalid for chunk " + toString() + " calc=" + crcval + " read=" + crcori);
+ public int readChunkData(InputStream is, boolean checkCrc) {
+ PngHelperInternal.readBytes(is, data, 0, len);
+ crcval = PngHelperInternal.readInt4(is);
+ if (checkCrc) {
+ int crc = computeCrc();
+ if (crc != crcval)
+ throw new PngjBadCrcException("chunk: " + this + " crc calc=" + crc + " read=" + crcval);
+ }
return len + 4;
}
- public ByteArrayInputStream getAsByteStream() { // only the data
+ ByteArrayInputStream getAsByteStream() { // only the data
return new ByteArrayInputStream(data);
}
- private void allocData() {
- if (data == null || data.length < len)
- data = new byte[len];
+ @Override
+ public String toString() {
+ return "chunkid=" + ChunkHelper.toString(idbytes) + " len=" + 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 new file mode 100644 index 000000000..75107d761 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksList.java @@ -0,0 +1,181 @@ +package jogamp.opengl.util.pngj.chunks;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import jogamp.opengl.util.pngj.ImageInfo;
+import jogamp.opengl.util.pngj.PngjException;
+
+/**
+ * All chunks that form an image, read or to be written.
+ * <p>
+ * chunks include all chunks, but IDAT is a single pseudo chunk without data
+ **/
+public class ChunksList {
+ // ref: http://www.w3.org/TR/PNG/#table53
+ public static final int CHUNK_GROUP_0_IDHR = 0; // required - single
+ public static final int CHUNK_GROUP_1_AFTERIDHR = 1; // optional - multiple
+ public static final int CHUNK_GROUP_2_PLTE = 2; // optional - single
+ public static final int CHUNK_GROUP_3_AFTERPLTE = 3; // optional - multple
+ public static final int CHUNK_GROUP_4_IDAT = 4; // required (single pseudo chunk)
+ public static final int CHUNK_GROUP_5_AFTERIDAT = 5; // optional - multple
+ public static final int CHUNK_GROUP_6_END = 6; // only 1 chunk - requried
+
+ /**
+ * All chunks, read (or written)
+ *
+ * But IDAT is a single pseudo chunk without data
+ */
+ protected List<PngChunk> chunks = new ArrayList<PngChunk>();
+
+ final ImageInfo imageInfo; // only required for writing
+
+ public ChunksList(ImageInfo imfinfo) {
+ this.imageInfo = imfinfo;
+ }
+
+ /**
+ * Keys of processed (read or writen) chunks
+ *
+ * @return key:chunk id, val: number of occurrences
+ */
+ public HashMap<String, Integer> getChunksKeys() {
+ HashMap<String, Integer> ck = new HashMap<String, Integer>();
+ for (PngChunk c : chunks) {
+ ck.put(c.id, ck.containsKey(c.id) ? ck.get(c.id) + 1 : 1);
+ }
+ return ck;
+ }
+
+ /**
+ * 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);
+ }
+
+ protected static List<PngChunk> getXById(final List<PngChunk> list, final String id, final String innerid) {
+ if (innerid == null)
+ return ChunkHelper.filterList(list, new ChunkPredicate() {
+ @Override
+ public boolean match(PngChunk c) {
+ return c.id.equals(id);
+ }
+ });
+ else
+ return ChunkHelper.filterList(list, new ChunkPredicate() {
+ @Override
+ public boolean match(PngChunk c) {
+ if (!c.id.equals(id))
+ return false;
+ if (c instanceof PngChunkTextVar && !((PngChunkTextVar) c).getKey().equals(innerid))
+ return false;
+ if (c instanceof PngChunkSPLT && !((PngChunkSPLT) c).getPalName().equals(innerid))
+ return false;
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Adds chunk in next position. This is used onyl by the pngReader
+ */
+ public void appendReadChunk(PngChunk chunk, int chunkGroup) {
+ chunk.setChunkGroup(chunkGroup);
+ chunks.add(chunk);
+ }
+
+ /**
+ * All chunks with this ID
+ *
+ * @param id
+ * @return List, empty if none
+ */
+ public List<? extends PngChunk> getById(final String id) {
+ return getById(id, null);
+ }
+
+ /**
+ * 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
+ * @return List, empty if none
+ */
+ public List<? extends PngChunk> getById(final String id, final String innerid) {
+ return getXById(chunks, id, innerid);
+ }
+
+ /**
+ * Returns only one chunk
+ *
+ * @param id
+ * @return First chunk found, null if not found
+ */
+ public PngChunk getById1(final String id) {
+ return getById1(id, false);
+ }
+
+ /**
+ * 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)
+ **/
+ public PngChunk getById1(final String id, final boolean failIfMultiple) {
+ return getById1(id, null, failIfMultiple);
+ }
+
+ /**
+ * 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)
+ **/
+ public PngChunk getById1(final String id, final String innerid, final boolean failIfMultiple) {
+ List<? extends PngChunk> list = getById(id, innerid);
+ if (list.isEmpty())
+ return null;
+ if (list.size() > 1 && (failIfMultiple || !list.get(0).allowsMultiple()))
+ throw new PngjException("unexpected multiple chunks id=" + id);
+ return list.get(list.size() - 1);
+ }
+
+ /**
+ * Finds all chunks "equivalent" to this one
+ *
+ * @param c2
+ * @return Empty if nothing found
+ */
+ public List<PngChunk> getEquivalent(final PngChunk c2) {
+ return ChunkHelper.filterList(chunks, new ChunkPredicate() {
+ @Override
+ public boolean match(PngChunk c) {
+ return ChunkHelper.equivalent(c, c2);
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ return "ChunkList: read: " + chunks.size();
+ }
+
+ /**
+ * for debugging
+ */
+ public String toStringFull() {
+ StringBuilder sb = new StringBuilder(toString());
+ sb.append("\n Read:\n");
+ for (PngChunk chunk : chunks) {
+ sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n");
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java new file mode 100644 index 000000000..c502e9071 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/ChunksListForWrite.java @@ -0,0 +1,176 @@ +package jogamp.opengl.util.pngj.chunks; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import jogamp.opengl.util.pngj.ImageInfo; +import jogamp.opengl.util.pngj.PngjException; +import jogamp.opengl.util.pngj.PngjOutputException; + +public class ChunksListForWrite extends ChunksList { + + /** + * chunks not yet writen - does not include IHDR, IDAT, END, perhaps yes + * PLTE + */ + private final List<PngChunk> queuedChunks = new ArrayList<PngChunk>(); + + // redundant, just for eficciency + private HashMap<String, Integer> alreadyWrittenKeys = new HashMap<String, Integer>(); + + public ChunksListForWrite(ImageInfo imfinfo) { + super(imfinfo); + } + + /** + * Same as getById(), but looking in the queued chunks + */ + public List<? extends PngChunk> getQueuedById(final String id) { + return getQueuedById(id, null); + } + + /** + * Same as getById(), but looking in the queued chunks + */ + public List<? extends PngChunk> getQueuedById(final String id, final String innerid) { + return getXById(queuedChunks, id, innerid); + } + + /** + * Same as getById1(), but looking in the queued chunks + **/ + public PngChunk getQueuedById1(final String id, final String innerid, final boolean failIfMultiple) { + List<? extends PngChunk> list = getQueuedById(id, innerid); + if (list.isEmpty()) + return null; + if (list.size() > 1 && (failIfMultiple || !list.get(0).allowsMultiple())) + throw new PngjException("unexpected multiple chunks id=" + id); + return list.get(list.size() - 1); + } + + /** + * Same as getById1(), but looking in the queued chunks + **/ + public PngChunk getQueuedById1(final String id, final boolean failIfMultiple) { + return getQueuedById1(id, null, failIfMultiple); + } + + /** + * Same as getById1(), but looking in the queued chunks + **/ + public PngChunk getQueuedById1(final String id) { + return getQueuedById1(id, false); + } + + /** + * 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! + */ + public boolean removeChunk(PngChunk c) { + return queuedChunks.remove(c); + } + + /** + * Adds chunk to queue + * + * Does not check for duplicated or anything + * + * @param c + */ + public boolean queue(PngChunk c) { + queuedChunks.add(c); + return true; + } + + /** + * 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) + return c.id.equals(ChunkHelper.PLTE); + if (currentGroup % 2 == 0) + throw new PngjOutputException("bad chunk group?"); + int minChunkGroup, maxChunkGroup; + if (c.getOrderingConstraint().mustGoBeforePLTE()) + minChunkGroup = maxChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + else if (c.getOrderingConstraint().mustGoBeforeIDAT()) { + maxChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; + minChunkGroup = c.getOrderingConstraint().mustGoAfterPLTE() ? ChunksList.CHUNK_GROUP_3_AFTERPLTE + : ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } else { + maxChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; + minChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + } + + int preferred = maxChunkGroup; + if (c.hasPriority()) + preferred = minChunkGroup; + if (ChunkHelper.isUnknown(c) && c.getChunkGroup() > 0) + preferred = c.getChunkGroup(); + if (currentGroup == preferred) + return true; + if (currentGroup > preferred && currentGroup <= maxChunkGroup) + return true; + return false; + } + + public int writeChunks(OutputStream os, int currentGroup) { + int cont = 0; + Iterator<PngChunk> it = queuedChunks.iterator(); + while (it.hasNext()) { + PngChunk c = it.next(); + if (!shouldWrite(c, currentGroup)) + continue; + if (ChunkHelper.isCritical(c.id) && !c.id.equals(ChunkHelper.PLTE)) + throw new PngjOutputException("bad chunk queued: " + c); + if (alreadyWrittenKeys.containsKey(c.id) && !c.allowsMultiple()) + throw new PngjOutputException("duplicated chunk does not allow multiple: " + c); + c.write(os); + chunks.add(c); + alreadyWrittenKeys.put(c.id, alreadyWrittenKeys.containsKey(c.id) ? alreadyWrittenKeys.get(c.id) + 1 : 1); + c.setChunkGroup(currentGroup); + it.remove(); + cont++; + } + return cont; + } + + /** + * warning: this is NOT a copy, do not modify + */ + public List<PngChunk> getQueuedChunks() { + return queuedChunks; + } + + @Override + public String toString() { + return "ChunkList: written: " + chunks.size() + " queue: " + queuedChunks.size(); + } + + /** + * for debugging + */ + @Override + public String toStringFull() { + StringBuilder sb = new StringBuilder(toString()); + sb.append("\n Written:\n"); + for (PngChunk chunk : chunks) { + sb.append(chunk).append(" G=" + chunk.getChunkGroup() + "\n"); + } + if (!queuedChunks.isEmpty()) { + sb.append(" Queued:\n"); + for (PngChunk chunk : queuedChunks) { + sb.append(chunk).append("\n"); + } + + } + return sb.toString(); + } +} 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 2df9fd1f3..6cd86eb98 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunk.java @@ -6,26 +6,91 @@ import java.util.HashMap; import java.util.Map;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngjException;
+import jogamp.opengl.util.pngj.PngjExceptionInternal;
-
-// see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
+/**
+ * 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>
+ * <p>
+ * 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>
+ * and the rest are instance-specific
+ */
public abstract class PngChunk {
- public final String id; // 4 letters
+ /**
+ * Chunk-id: 4 letters
+ */
+ public final String id;
+ /**
+ * Autocomputed at creation time
+ */
public final boolean crit, pub, safe;
- private int lenori = -1; // merely informational, for read chunks
- private boolean writePriority = false; // for queued chunks
protected final ImageInfo imgInfo;
- private int chunkGroup = -1; // chunk group where it was read or writen
+ /**
+ * 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 {
+ /**
+ * no ordering constraint
+ */
+ NONE,
+ /**
+ * Must go before PLTE (and hence, also before IDAT)
+ */
+ BEFORE_PLTE_AND_IDAT,
+ /**
+ * Must go after PLTE but before IDAT
+ */
+ AFTER_PLTE_BEFORE_IDAT,
+ /**
+ * Must before IDAT (before or after PLTE)
+ */
+ BEFORE_IDAT,
+ /**
+ * Does not apply
+ */
+ NA;
+
+ public boolean mustGoBeforePLTE() {
+ return this == BEFORE_PLTE_AND_IDAT;
+ }
+
+ public boolean mustGoBeforeIDAT() {
+ return this == BEFORE_IDAT || this == BEFORE_PLTE_AND_IDAT || this == AFTER_PLTE_BEFORE_IDAT;
+ }
+
+ public boolean mustGoAfterPLTE() {
+ return this == AFTER_PLTE_BEFORE_IDAT;
+ }
+ }
+
+ private boolean priority = false; // For writing. Queued chunks with high priority will be written as soon as
+ // possible
+
+ protected int chunkGroup = -1; // chunk group where it was read or writen
+ protected int length = -1; // merely informational, for read chunks
+ protected long offset = 0; // merely informational, for read chunks
/**
- * This static map defines which PngChunk class correspond to which ChunkID The client can add other chunks to this
- * map statically, before reading
+ * 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)
*/
- public final static Map<String, Class<? extends PngChunk>> factoryMap = new HashMap<String, Class<? extends PngChunk>>();
+ private final static Map<String, Class<? extends PngChunk>> factoryMap = new HashMap<String, Class<? extends PngChunk>>();
static {
factoryMap.put(ChunkHelper.IDAT, PngChunkIDAT.class);
factoryMap.put(ChunkHelper.IHDR, PngChunkIHDR.class);
@@ -45,6 +110,35 @@ public abstract class PngChunk { factoryMap.put(ChunkHelper.sRGB, PngChunkSRGB.class);
factoryMap.put(ChunkHelper.hIST, PngChunkHIST.class);
factoryMap.put(ChunkHelper.sPLT, PngChunkSPLT.class);
+ // extended
+ factoryMap.put(PngChunkOFFS.ID, PngChunkOFFS.class);
+ factoryMap.put(PngChunkSTER.ID, PngChunkSTER.class);
+ }
+
+ /**
+ * 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.
+ */
+ public static void factoryRegister(String chunkId, Class<? extends PngChunk> chunkClass) {
+ factoryMap.put(chunkId, chunkClass);
+ }
+
+ /**
+ * True if the chunk-id type is known.
+ * <p>
+ * 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
+ * <p>
+ * Unknown chunks will be parsed as instances of {@link PngChunkUNKNOWN}
+ */
+ public static boolean isKnown(String id) {
+ return factoryMap.containsKey(id);
}
protected PngChunk(String id, ImageInfo imgInfo) {
@@ -55,29 +149,21 @@ public abstract class PngChunk { this.safe = ChunkHelper.isSafeToCopy(id);
}
- public abstract ChunkRaw createChunk();
-
- public abstract void parseFromChunk(ChunkRaw c);
-
- // override to make deep copy from read data to write
- public abstract void cloneDataFromRead(PngChunk other);
-
- @SuppressWarnings("unchecked")
- public static <T extends PngChunk> T cloneChunk(T chunk, ImageInfo info) {
- PngChunk cn = factoryFromId(chunk.id, info);
- if (cn.getClass() != chunk.getClass())
- throw new PngjException("bad class cloning chunk: " + cn.getClass() + " " + chunk.getClass());
- cn.cloneDataFromRead(chunk);
- return (T) cn;
- }
-
+ /**
+ * 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);
- c.lenori = chunk.len;
- c.parseFromChunk(chunk);
+ c.length = chunk.len;
+ c.parseFromRaw(chunk);
return c;
}
+ /**
+ * 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;
try {
@@ -87,66 +173,112 @@ public abstract class PngChunk { chunk = constr.newInstance(info);
}
} catch (Exception e) {
- // this can happend for unkown chunks
+ // this can happen for unkown chunks
}
if (chunk == null)
chunk = new PngChunkUNKNOWN(cid, info);
return chunk;
}
- protected ChunkRaw createEmptyChunk(int len, boolean alloc) {
+ protected final ChunkRaw createEmptyChunk(int len, boolean alloc) {
ChunkRaw c = new ChunkRaw(len, ChunkHelper.toBytes(id), alloc);
return c;
}
- @Override
- public String toString() {
- return "chunk id= " + id + " (" + lenori + ") c=" + getClass().getSimpleName();
+ /**
+ * Makes a clone (deep copy) calling {@link #cloneDataFromRead(PngChunk)}
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends PngChunk> T cloneChunk(T chunk, ImageInfo info) {
+ PngChunk cn = factoryFromId(chunk.id, info);
+ if (cn.getClass() != chunk.getClass())
+ throw new PngjExceptionInternal("bad class cloning chunk: " + cn.getClass() + " " + chunk.getClass());
+ cn.cloneDataFromRead(chunk);
+ return (T) cn;
}
- void setPriority(boolean highPrioriy) {
- writePriority = highPrioriy;
+ /**
+ * In which "chunkGroup" (see {@link ChunksList}for definition) this chunks
+ * instance was read or written.
+ * <p>
+ * -1 if not read or written (eg, queued)
+ */
+ final public int getChunkGroup() {
+ return chunkGroup;
}
- void write(OutputStream os) {
- ChunkRaw c = createChunk();
- if (c == null)
- throw new PngjException("null chunk ! creation failed for " + this);
- c.writeChunk(os);
+ /**
+ * @see #getChunkGroup()
+ */
+ final public void setChunkGroup(int chunkGroup) {
+ this.chunkGroup = chunkGroup;
}
- public boolean isWritePriority() {
- return writePriority;
+ public boolean hasPriority() {
+ return priority;
}
- /** must be overriden - only relevant for ancillary chunks */
- public boolean allowsMultiple() {
- return false; // override if allows multiple ocurrences
+ public void setPriority(boolean priority) {
+ this.priority = priority;
}
- /** mustGoBeforeXX/After must be overriden - only relevant for ancillary chunks */
- public boolean mustGoBeforeIDAT() {
- return false;
+ final void write(OutputStream os) {
+ ChunkRaw c = createRawChunk();
+ if (c == null)
+ throw new PngjExceptionInternal("null chunk ! creation failed for " + this);
+ c.writeChunk(os);
}
- public boolean mustGoBeforePLTE() {
- return false;
+ public int getLength() {
+ return length;
}
- public boolean mustGoAfterPLTE() {
- return false;
- }
+ /*
+ * public void setLength(int length) { this.length = length; }
+ */
- static boolean isKnown(String id) {
- return factoryMap.containsKey(id);
+ public long getOffset() {
+ return offset;
}
- public int getChunkGroup() {
- return chunkGroup;
+ public void setOffset(long offset) {
+ this.offset = offset;
}
- public void setChunkGroup(int chunkGroup) {
- this.chunkGroup = chunkGroup;
+ /**
+ * 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.
+ */
+ public abstract void parseFromRaw(ChunkRaw c);
+
+ /**
+ * Makes a copy of the chunk.
+ * <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
+ */
+ public abstract void cloneDataFromRead(PngChunk other);
+
+ public abstract boolean allowsMultiple(); // this is implemented in PngChunkMultiple/PngChunSingle
+
+ /**
+ * see {@link ChunkOrderingConstraint}
+ */
+ public abstract ChunkOrderingConstraint getOrderingConstraint();
+
+ @Override
+ public String toString() {
+ return "chunk id= " + id + " (len=" + length + " offset=" + offset + ") c=" + getClass().getSimpleName();
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkBKGD.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkBKGD.java index 51bbcb832..ea6235432 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkBKGD.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkBKGD.java @@ -1,14 +1,18 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * bKGD Chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11bKGD
+ * <p>
+ * this chunk structure depends on the image type
*/
-public class PngChunkBKGD extends PngChunk {
- // http://www.w3.org/TR/PNG/#11bKGD
- // this chunk structure depends on the image type
+public class PngChunkBKGD extends PngChunkSingle {
+ public final static String ID = ChunkHelper.bKGD;
// only one of these is meaningful
private int gray;
private int red, green, blue;
@@ -19,43 +23,38 @@ public class PngChunkBKGD extends PngChunk { }
@Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT;
}
@Override
- public boolean mustGoAfterPLTE() {
- return true;
- }
-
- @Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = null;
if (imgInfo.greyscale) {
c = createEmptyChunk(2, true);
- PngHelper.writeInt2tobytes(gray, c.data, 0);
+ PngHelperInternal.writeInt2tobytes(gray, c.data, 0);
} else if (imgInfo.indexed) {
c = createEmptyChunk(1, true);
c.data[0] = (byte) paletteIndex;
} else {
c = createEmptyChunk(6, true);
- PngHelper.writeInt2tobytes(red, c.data, 0);
- PngHelper.writeInt2tobytes(green, c.data, 0);
- PngHelper.writeInt2tobytes(blue, c.data, 0);
+ PngHelperInternal.writeInt2tobytes(red, c.data, 0);
+ PngHelperInternal.writeInt2tobytes(green, c.data, 0);
+ PngHelperInternal.writeInt2tobytes(blue, c.data, 0);
}
return c;
}
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
if (imgInfo.greyscale) {
- gray = PngHelper.readInt2fromBytes(c.data, 0);
+ gray = PngHelperInternal.readInt2fromBytes(c.data, 0);
} else if (imgInfo.indexed) {
paletteIndex = (int) (c.data[0] & 0xff);
} else {
- red = PngHelper.readInt2fromBytes(c.data, 0);
- green = PngHelper.readInt2fromBytes(c.data, 2);
- blue = PngHelper.readInt2fromBytes(c.data, 4);
+ red = PngHelperInternal.readInt2fromBytes(c.data, 0);
+ green = PngHelperInternal.readInt2fromBytes(c.data, 2);
+ blue = PngHelperInternal.readInt2fromBytes(c.data, 4);
}
}
@@ -71,7 +70,7 @@ public class PngChunkBKGD extends PngChunk { /**
* Set gray value (0-255 if bitdept=8)
- *
+ *
* @param gray
*/
public void setGray(int gray) {
@@ -88,7 +87,7 @@ public class PngChunkBKGD extends PngChunk { /**
* Set pallette index
- *
+ *
*/
public void setPaletteIndex(int i) {
if (!imgInfo.indexed)
@@ -104,7 +103,7 @@ public class PngChunkBKGD extends PngChunk { /**
* Set rgb values
- *
+ *
*/
public void setRGB(int r, int g, int b) {
if (imgInfo.greyscale || imgInfo.indexed)
@@ -119,4 +118,5 @@ public class PngChunkBKGD extends PngChunk { throw new PngjException("only rgb or rgba images support this");
return new int[] { red, green, blue };
}
+
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkCHRM.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkCHRM.java index 4380761c7..25a4bf2d6 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkCHRM.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkCHRM.java @@ -1,12 +1,17 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * cHRM chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11cHRM
*/
-public class PngChunkCHRM extends PngChunk {
+public class PngChunkCHRM extends PngChunkSingle {
+ public final static String ID = ChunkHelper.cHRM;
+
// http://www.w3.org/TR/PNG/#11cHRM
private double whitex, whitey;
private double redx, redy;
@@ -14,46 +19,41 @@ public class PngChunkCHRM extends PngChunk { private double bluex, bluey;
public PngChunkCHRM(ImageInfo info) {
- super(ChunkHelper.cHRM, info);
- }
-
- @Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ super(ID, info);
}
@Override
- public boolean mustGoBeforePLTE() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = null;
c = createEmptyChunk(32, true);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(whitex), c.data, 0);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(whitey), c.data, 4);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(redx), c.data, 8);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(redy), c.data, 12);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(greenx), c.data, 16);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(greeny), c.data, 20);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(bluex), c.data, 24);
- PngHelper.writeInt4tobytes(PngHelper.doubleToInt100000(bluey), c.data, 28);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(whitex), c.data, 0);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(whitey), c.data, 4);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(redx), c.data, 8);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(redy), c.data, 12);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(greenx), c.data, 16);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(greeny), c.data, 20);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(bluex), c.data, 24);
+ PngHelperInternal.writeInt4tobytes(PngHelperInternal.doubleToInt100000(bluey), c.data, 28);
return c;
}
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
if (c.len != 32)
throw new PngjException("bad chunk " + c);
- whitex = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 0));
- whitey = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 4));
- redx = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 8));
- redy = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 12));
- greenx = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 16));
- greeny = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 20));
- bluex = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 24));
- bluey = PngHelper.intToDouble100000(PngHelper.readInt4fromBytes(c.data, 28));
+ whitex = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 0));
+ whitey = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 4));
+ redx = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 8));
+ redy = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 12));
+ greenx = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 16));
+ greeny = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 20));
+ bluex = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 24));
+ bluey = PngHelperInternal.intToDouble100000(PngHelperInternal.readInt4fromBytes(c.data, 28));
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkGAMA.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkGAMA.java index 184ee9ffa..74640746e 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkGAMA.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkGAMA.java @@ -1,42 +1,42 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * gAMA chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11gAMA
*/
-public class PngChunkGAMA extends PngChunk {
+public class PngChunkGAMA extends PngChunkSingle {
+ public final static String ID = ChunkHelper.gAMA;
+
// http://www.w3.org/TR/PNG/#11gAMA
private double gamma;
public PngChunkGAMA(ImageInfo info) {
- super(ChunkHelper.gAMA, info);
- }
-
- @Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ super(ID, info);
}
@Override
- public boolean mustGoBeforePLTE() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = createEmptyChunk(4, true);
int g = (int) (gamma * 100000 + 0.5);
- PngHelper.writeInt4tobytes(g, c.data, 0);
+ PngHelperInternal.writeInt4tobytes(g, c.data, 0);
return c;
}
@Override
- public void parseFromChunk(ChunkRaw chunk) {
+ public void parseFromRaw(ChunkRaw chunk) {
if (chunk.len != 4)
throw new PngjException("bad chunk " + chunk);
- int g = PngHelper.readInt4fromBytes(chunk.data, 0);
+ int g = PngHelperInternal.readInt4fromBytes(chunk.data, 0);
gamma = ((double) g) / 100000.0;
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkHIST.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkHIST.java index b0f02ea37..6dc3fd9ec 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkHIST.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkHIST.java @@ -1,50 +1,48 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * hIST chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11hIST <br>
+ * only for palette images
*/
-public class PngChunkHIST extends PngChunk {
- // http://www.w3.org/TR/PNG/#11hIST
- // only for palette images
+public class PngChunkHIST extends PngChunkSingle {
+ public final static String ID = ChunkHelper.hIST;
private int[] hist = new int[0]; // should have same lenght as palette
public PngChunkHIST(ImageInfo info) {
- super(ChunkHelper.hIST, info);
+ super(ID, info);
}
@Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.AFTER_PLTE_BEFORE_IDAT;
}
@Override
- public boolean mustGoAfterPLTE() {
- return true;
- }
-
- @Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
if (!imgInfo.indexed)
throw new PngjException("only indexed images accept a HIST chunk");
int nentries = c.data.length / 2;
hist = new int[nentries];
for (int i = 0; i < hist.length; i++) {
- hist[i] = PngHelper.readInt2fromBytes(c.data, i * 2);
+ hist[i] = PngHelperInternal.readInt2fromBytes(c.data, i * 2);
}
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
if (!imgInfo.indexed)
throw new PngjException("only indexed images accept a HIST chunk");
ChunkRaw c = null;
c = createEmptyChunk(hist.length * 2, true);
for (int i = 0; i < hist.length; i++) {
- PngHelper.writeInt2tobytes(hist[i], c.data, i * 2);
+ PngHelperInternal.writeInt2tobytes(hist[i], c.data, i * 2);
}
return c;
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkICCP.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkICCP.java index db1c1ba64..399577d72 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkICCP.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkICCP.java @@ -1,31 +1,32 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
+import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * iCCP chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11iCCP
*/
-public class PngChunkICCP extends PngChunk {
+public class PngChunkICCP extends PngChunkSingle {
+ public final static String ID = ChunkHelper.iCCP;
+
// http://www.w3.org/TR/PNG/#11iCCP
private String profileName;
private byte[] compressedProfile; // copmression/decopmresion is done in getter/setter
public PngChunkICCP(ImageInfo info) {
- super(ChunkHelper.iCCP, info);
- }
-
- @Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ super(ID, info);
}
@Override
- public boolean mustGoBeforePLTE() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = createEmptyChunk(profileName.length() + compressedProfile.length + 2, true);
System.arraycopy(ChunkHelper.toBytes(profileName), 0, c.data, 0, profileName.length());
c.data[profileName.length()] = 0;
@@ -35,12 +36,12 @@ public class PngChunkICCP extends PngChunk { }
@Override
- public void parseFromChunk(ChunkRaw chunk) {
+ public void parseFromRaw(ChunkRaw chunk) {
int pos0 = ChunkHelper.posNullByte(chunk.data);
- profileName = new String(chunk.data, 0, pos0, PngHelper.charsetLatin1);
+ profileName = new String(chunk.data, 0, pos0, PngHelperInternal.charsetLatin1);
int comp = (chunk.data[pos0 + 1] & 0xff);
if (comp != 0)
- throw new RuntimeException("bad compression for ChunkTypeICCP");
+ throw new PngjException("bad compression for ChunkTypeICCP");
int compdatasize = chunk.data.length - (pos0 + 2);
compressedProfile = new byte[compdatasize];
System.arraycopy(chunk.data, pos0 + 2, compressedProfile, 0, compdatasize);
@@ -64,7 +65,7 @@ public class PngChunkICCP extends PngChunk { }
public void setProfileNameAndContent(String name, String profile) {
- setProfileNameAndContent(name, profile.getBytes(PngHelper.charsetLatin1));
+ setProfileNameAndContent(name, profile.getBytes(PngHelperInternal.charsetLatin1));
}
public String getProfileName() {
@@ -79,7 +80,7 @@ public class PngChunkICCP extends PngChunk { }
public String getProfileAsString() {
- return new String(getProfile(), PngHelper.charsetLatin1);
+ return new String(getProfile(), PngHelperInternal.charsetLatin1);
}
}
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 a7cb95dbf..911513c0d 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIDAT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIDAT.java @@ -2,21 +2,36 @@ package jogamp.opengl.util.pngj.chunks; import jogamp.opengl.util.pngj.ImageInfo;
-public class PngChunkIDAT extends PngChunk {
+/**
+ * IDAT chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11IDAT
+ * <p>
+ * 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;
+
// http://www.w3.org/TR/PNG/#11IDAT
- // This is dummy placeholder - we write/read this chunk (actually several)
- // by special code.
- public PngChunkIDAT(ImageInfo i) {
- super(ChunkHelper.IDAT, i);
+ public PngChunkIDAT(ImageInfo i, int len, long offset) {
+ super(ID, i);
+ this.length = len;
+ this.offset = offset;
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NA;
}
@Override
- public ChunkRaw createChunk() {// does nothing
+ public ChunkRaw createRawChunk() {// does nothing
return null;
}
@Override
- public void parseFromChunk(ChunkRaw c) { // does nothing
+ public void parseFromRaw(ChunkRaw c) { // does nothing
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIEND.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIEND.java index 0d5b266da..fbec564d8 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIEND.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIEND.java @@ -2,21 +2,33 @@ package jogamp.opengl.util.pngj.chunks; import jogamp.opengl.util.pngj.ImageInfo;
-public class PngChunkIEND extends PngChunk {
+/**
+ * IEND chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11IEND
+ */
+public class PngChunkIEND extends PngChunkSingle {
+ public final static String ID = ChunkHelper.IEND;
+
// http://www.w3.org/TR/PNG/#11IEND
// this is a dummy placeholder
public PngChunkIEND(ImageInfo info) {
- super(ChunkHelper.IEND, info);
+ super(ID, info);
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NA;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = new ChunkRaw(0, ChunkHelper.b_IEND, false);
return c;
}
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
// this is not used
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIHDR.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIHDR.java index fcb4150ff..94bfedd38 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIHDR.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkIHDR.java @@ -3,14 +3,20 @@ package jogamp.opengl.util.pngj.chunks; import java.io.ByteArrayInputStream;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
/**
- * this is a special chunk!
+ * IHDR chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11IHDR
+ * <p>
+ * This is a special critical Chunk.
*/
-public class PngChunkIHDR extends PngChunk {
+public class PngChunkIHDR extends PngChunkSingle {
+ public final static String ID = ChunkHelper.IHDR;
+
private int cols;
private int rows;
private int bitspc;
@@ -22,16 +28,21 @@ public class PngChunkIHDR extends PngChunk { // http://www.w3.org/TR/PNG/#11IHDR
//
public PngChunkIHDR(ImageInfo info) {
- super(ChunkHelper.IHDR, info);
+ super(ID, info);
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NA;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = new ChunkRaw(13, ChunkHelper.b_IHDR, true);
int offset = 0;
- PngHelper.writeInt4tobytes(cols, c.data, offset);
+ PngHelperInternal.writeInt4tobytes(cols, c.data, offset);
offset += 4;
- PngHelper.writeInt4tobytes(rows, c.data, offset);
+ PngHelperInternal.writeInt4tobytes(rows, c.data, offset);
offset += 4;
c.data[offset++] = (byte) bitspc;
c.data[offset++] = (byte) colormodel;
@@ -42,18 +53,18 @@ public class PngChunkIHDR extends PngChunk { }
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
if (c.len != 13)
throw new PngjException("Bad IDHR len " + c.len);
ByteArrayInputStream st = c.getAsByteStream();
- cols = PngHelper.readInt4(st);
- rows = PngHelper.readInt4(st);
+ cols = PngHelperInternal.readInt4(st);
+ rows = PngHelperInternal.readInt4(st);
// bit depth: number of bits per channel
- bitspc = PngHelper.readByte(st);
- colormodel = PngHelper.readByte(st);
- compmeth = PngHelper.readByte(st);
- filmeth = PngHelper.readByte(st);
- interlaced = PngHelper.readByte(st);
+ bitspc = PngHelperInternal.readByte(st);
+ colormodel = PngHelperInternal.readByte(st);
+ compmeth = PngHelperInternal.readByte(st);
+ filmeth = PngHelperInternal.readByte(st);
+ interlaced = PngHelperInternal.readByte(st);
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkITXT.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkITXT.java index 4e5c7c74a..ab52d7c90 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkITXT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkITXT.java @@ -4,14 +4,17 @@ import java.io.ByteArrayOutputStream; import java.io.IOException;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
/**
- * UNTESTED!
+ * iTXt chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11iTXt
*/
public class PngChunkITXT extends PngChunkTextVar {
+ public final static String ID = ChunkHelper.iTXt;
private boolean compressed = false;
private String langTag = "";
@@ -19,24 +22,24 @@ public class PngChunkITXT extends PngChunkTextVar { // http://www.w3.org/TR/PNG/#11iTXt
public PngChunkITXT(ImageInfo info) {
- super(ChunkHelper.iTXt, info);
+ super(ID, info);
}
@Override
- public ChunkRaw createChunk() {
- if (val.isEmpty() || key.isEmpty())
- return null;
+ public ChunkRaw createRawChunk() {
+ if (key.isEmpty())
+ throw new PngjException("Text chunk key must be non empty");
try {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
- ba.write(key.getBytes(PngHelper.charsetLatin1));
+ ba.write(ChunkHelper.toBytes(key));
ba.write(0); // separator
ba.write(compressed ? 1 : 0);
ba.write(0); // compression method (always 0)
- ba.write(langTag.getBytes(PngHelper.charsetUTF8));
+ ba.write(ChunkHelper.toBytes(langTag));
ba.write(0); // separator
- ba.write(translatedTag.getBytes(PngHelper.charsetUTF8));
+ ba.write(ChunkHelper.toBytesUTF8(translatedTag));
ba.write(0); // separator
- byte[] textbytes = val.getBytes(PngHelper.charsetUTF8);
+ byte[] textbytes = ChunkHelper.toBytesUTF8(val);
if (compressed) {
textbytes = ChunkHelper.compressBytes(textbytes, true);
}
@@ -51,7 +54,7 @@ public class PngChunkITXT extends PngChunkTextVar { }
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
int nullsFound = 0;
int[] nullsIdx = new int[3];
for (int i = 0; i < c.data.length; i++) {
@@ -66,20 +69,21 @@ public class PngChunkITXT extends PngChunkTextVar { }
if (nullsFound != 3)
throw new PngjException("Bad formed PngChunkITXT chunk");
- key = new String(c.data, 0, nullsIdx[0], PngHelper.charsetLatin1);
+ key = ChunkHelper.toString(c.data, 0, nullsIdx[0]);
int i = nullsIdx[0] + 1;
compressed = c.data[i] == 0 ? false : true;
i++;
if (compressed && c.data[i] != 0)
throw new PngjException("Bad formed PngChunkITXT chunk - bad compression method ");
- langTag = new String(c.data, i, nullsIdx[1] - i, PngHelper.charsetLatin1);
- translatedTag = new String(c.data, nullsIdx[1] + 1, nullsIdx[2] - nullsIdx[1] - 1, PngHelper.charsetUTF8);
+ langTag = new String(c.data, i, nullsIdx[1] - i, PngHelperInternal.charsetLatin1);
+ translatedTag = new String(c.data, nullsIdx[1] + 1, nullsIdx[2] - nullsIdx[1] - 1,
+ PngHelperInternal.charsetUTF8);
i = nullsIdx[2] + 1;
if (compressed) {
byte[] bytes = ChunkHelper.compressBytes(c.data, i, c.data.length - i, false);
- val = new String(bytes, PngHelper.charsetUTF8);
+ val = ChunkHelper.toStringUTF8(bytes);
} else {
- val = new String(c.data, i, c.data.length - i, PngHelper.charsetUTF8);
+ val = ChunkHelper.toStringUTF8(c.data, i, c.data.length - i);
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java new file mode 100644 index 000000000..057f6c25e --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkMultiple.java @@ -0,0 +1,28 @@ +package jogamp.opengl.util.pngj.chunks;
+
+import jogamp.opengl.util.pngj.ImageInfo;
+
+/**
+ * PNG chunk type (abstract) that allows multiple instances in same image.
+ */
+public abstract class PngChunkMultiple extends PngChunk {
+
+ protected PngChunkMultiple(String id, ImageInfo imgInfo) {
+ super(id, imgInfo);
+ }
+
+ @Override
+ public final boolean allowsMultiple() {
+ return true;
+ }
+
+ /**
+ * NOTE: this chunk uses the default Object's equals() hashCode()
+ * implementation.
+ *
+ * This is the right thing to do, normally.
+ *
+ * This is important, eg see ChunkList.removeFromList()
+ */
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkOFFS.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkOFFS.java new file mode 100644 index 000000000..a3bab4995 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkOFFS.java @@ -0,0 +1,89 @@ +package jogamp.opengl.util.pngj.chunks;
+
+import jogamp.opengl.util.pngj.ImageInfo;
+import jogamp.opengl.util.pngj.PngHelperInternal;
+import jogamp.opengl.util.pngj.PngjException;
+
+/**
+ * oFFs chunk.
+ * <p>
+ * see http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.oFFs
+ */
+public class PngChunkOFFS extends PngChunkSingle {
+ public final static String ID = "oFFs";
+
+ // http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.oFFs
+ private long posX;
+ private long posY;
+ private int units; // 0: pixel 1:micrometer
+
+ public PngChunkOFFS(ImageInfo info) {
+ super(ID, info);
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_IDAT;
+ }
+
+ @Override
+ public ChunkRaw createRawChunk() {
+ ChunkRaw c = createEmptyChunk(9, true);
+ PngHelperInternal.writeInt4tobytes((int) posX, c.data, 0);
+ PngHelperInternal.writeInt4tobytes((int) posY, c.data, 4);
+ c.data[8] = (byte) units;
+ return c;
+ }
+
+ @Override
+ public void parseFromRaw(ChunkRaw chunk) {
+ if (chunk.len != 9)
+ throw new PngjException("bad chunk length " + chunk);
+ posX = PngHelperInternal.readInt4fromBytes(chunk.data, 0);
+ if (posX < 0)
+ posX += 0x100000000L;
+ posY = PngHelperInternal.readInt4fromBytes(chunk.data, 4);
+ if (posY < 0)
+ posY += 0x100000000L;
+ units = PngHelperInternal.readInt1fromByte(chunk.data, 8);
+ }
+
+ @Override
+ public void cloneDataFromRead(PngChunk other) {
+ PngChunkOFFS otherx = (PngChunkOFFS) other;
+ this.posX = otherx.posX;
+ this.posY = otherx.posY;
+ this.units = otherx.units;
+ }
+
+ /**
+ * 0: pixel, 1:micrometer
+ */
+ public int getUnits() {
+ return units;
+ }
+
+ /**
+ * 0: pixel, 1:micrometer
+ */
+ public void setUnits(int units) {
+ this.units = units;
+ }
+
+ public long getPosX() {
+ return posX;
+ }
+
+ public void setPosX(long posX) {
+ this.posX = posX;
+ }
+
+ public long getPosY() {
+ return posY;
+ }
+
+ public void setPosY(long posY) {
+ this.posY = posY;
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPHYS.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPHYS.java index 47e2c492c..b0a1bb898 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPHYS.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPHYS.java @@ -1,44 +1,50 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
+/**
+ * pHYs chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11pHYs
+ */
+public class PngChunkPHYS extends PngChunkSingle {
+ public final static String ID = ChunkHelper.pHYs;
-public class PngChunkPHYS extends PngChunk {
// http://www.w3.org/TR/PNG/#11pHYs
private long pixelsxUnitX;
private long pixelsxUnitY;
private int units; // 0: unknown 1:metre
public PngChunkPHYS(ImageInfo info) {
- super(ChunkHelper.pHYs, info);
+ super(ID, info);
}
@Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_IDAT;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = createEmptyChunk(9, true);
- PngHelper.writeInt4tobytes((int) pixelsxUnitX, c.data, 0);
- PngHelper.writeInt4tobytes((int) pixelsxUnitY, c.data, 4);
+ PngHelperInternal.writeInt4tobytes((int) pixelsxUnitX, c.data, 0);
+ PngHelperInternal.writeInt4tobytes((int) pixelsxUnitY, c.data, 4);
c.data[8] = (byte) units;
return c;
}
@Override
- public void parseFromChunk(ChunkRaw chunk) {
+ public void parseFromRaw(ChunkRaw chunk) {
if (chunk.len != 9)
throw new PngjException("bad chunk length " + chunk);
- pixelsxUnitX = PngHelper.readInt4fromBytes(chunk.data, 0);
+ pixelsxUnitX = PngHelperInternal.readInt4fromBytes(chunk.data, 0);
if (pixelsxUnitX < 0)
pixelsxUnitX += 0x100000000L;
- pixelsxUnitY = PngHelper.readInt4fromBytes(chunk.data, 4);
+ pixelsxUnitY = PngHelperInternal.readInt4fromBytes(chunk.data, 4);
if (pixelsxUnitY < 0)
pixelsxUnitY += 0x100000000L;
- units = PngHelper.readInt1fromByte(chunk.data, 8);
+ units = PngHelperInternal.readInt1fromByte(chunk.data, 8);
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPLTE.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPLTE.java index 123080bb3..dbf5e53c0 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPLTE.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkPLTE.java @@ -3,10 +3,16 @@ package jogamp.opengl.util.pngj.chunks; import jogamp.opengl.util.pngj.ImageInfo;
import jogamp.opengl.util.pngj.PngjException;
-/*
- * Palette chunk *this is critical*
+/**
+ * PLTE chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11PLTE
+ * <p>
+ * Critical chunk
*/
-public class PngChunkPLTE extends PngChunk {
+public class PngChunkPLTE extends PngChunkSingle {
+ public final static String ID = ChunkHelper.PLTE;
+
// http://www.w3.org/TR/PNG/#11PLTE
private int nentries = 0;
/**
@@ -15,11 +21,16 @@ public class PngChunkPLTE extends PngChunk { private int[] entries;
public PngChunkPLTE(ImageInfo info) {
- super(ChunkHelper.PLTE, info);
+ super(ID, info);
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NA;
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
int len = 3 * nentries;
int[] rgb = new int[3];
ChunkRaw c = createEmptyChunk(len, true);
@@ -33,7 +44,7 @@ public class PngChunkPLTE extends PngChunk { }
@Override
- public void parseFromChunk(ChunkRaw chunk) {
+ public void parseFromRaw(ChunkRaw chunk) {
setNentries(chunk.len / 3);
for (int n = 0, i = 0; n < nentries; n++) {
setEntry(n, (int) (chunk.data[i++] & 0xff), (int) (chunk.data[i++] & 0xff), (int) (chunk.data[i++] & 0xff));
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSBIT.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSBIT.java index 6850d260d..3a490654a 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSBIT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSBIT.java @@ -1,31 +1,31 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * sBIT chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11sBIT
+ * <p>
+ * this chunk structure depends on the image type
*/
-public class PngChunkSBIT extends PngChunk {
+public class PngChunkSBIT extends PngChunkSingle {
+ public final static String ID = ChunkHelper.sBIT;
// http://www.w3.org/TR/PNG/#11sBIT
- // this chunk structure depends on the image type
// significant bits
private int graysb, alphasb;
private int redsb, greensb, bluesb;
public PngChunkSBIT(ImageInfo info) {
- super(ChunkHelper.sBIT, info);
+ super(ID, info);
}
@Override
- public boolean mustGoBeforeIDAT() {
- return true;
- }
-
- @Override
- public boolean mustGoBeforePLTE() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT;
}
private int getLen() {
@@ -36,24 +36,24 @@ public class PngChunkSBIT extends PngChunk { }
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
if (c.len != getLen())
throw new PngjException("bad chunk length " + c);
if (imgInfo.greyscale) {
- graysb = PngHelper.readInt1fromByte(c.data, 0);
+ graysb = PngHelperInternal.readInt1fromByte(c.data, 0);
if (imgInfo.alpha)
- alphasb = PngHelper.readInt1fromByte(c.data, 1);
+ alphasb = PngHelperInternal.readInt1fromByte(c.data, 1);
} else {
- redsb = PngHelper.readInt1fromByte(c.data, 0);
- greensb = PngHelper.readInt1fromByte(c.data, 1);
- bluesb = PngHelper.readInt1fromByte(c.data, 2);
+ redsb = PngHelperInternal.readInt1fromByte(c.data, 0);
+ greensb = PngHelperInternal.readInt1fromByte(c.data, 1);
+ bluesb = PngHelperInternal.readInt1fromByte(c.data, 2);
if (imgInfo.alpha)
- alphasb = PngHelper.readInt1fromByte(c.data, 3);
+ alphasb = PngHelperInternal.readInt1fromByte(c.data, 3);
}
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = null;
c = createEmptyChunk(getLen(), true);
if (imgInfo.greyscale) {
@@ -106,7 +106,7 @@ public class PngChunkSBIT extends PngChunk { /**
* Set rgb values
- *
+ *
*/
public void setRGB(int r, int g, int b) {
if (imgInfo.greyscale || imgInfo.indexed)
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSPLT.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSPLT.java index 953adb7d9..2ff65834d 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSPLT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSPLT.java @@ -4,11 +4,17 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import jogamp.opengl.util.pngj.ImageInfo; -import jogamp.opengl.util.pngj.PngHelper; +import jogamp.opengl.util.pngj.PngHelperInternal; import jogamp.opengl.util.pngj.PngjException; +/** + * sPLT chunk. + * <p> + * see http://www.w3.org/TR/PNG/#11sPLT + */ +public class PngChunkSPLT extends PngChunkMultiple { + public final static String ID = ChunkHelper.sPLT; -public class PngChunkSPLT extends PngChunk { // http://www.w3.org/TR/PNG/#11sPLT private String palName; @@ -16,35 +22,30 @@ public class PngChunkSPLT extends PngChunk { private int[] palette; // 5 elements per entry public PngChunkSPLT(ImageInfo info) { - super(ChunkHelper.sPLT, info); + super(ID, info); } @Override - public boolean allowsMultiple() { - return true; // allows multiple, but pallete name should be different + public ChunkOrderingConstraint getOrderingConstraint() { + return ChunkOrderingConstraint.BEFORE_IDAT; } @Override - public boolean mustGoBeforeIDAT() { - return true; - } - - @Override - public ChunkRaw createChunk() { + public ChunkRaw createRawChunk() { try { ByteArrayOutputStream ba = new ByteArrayOutputStream(); - ba.write(palName.getBytes(PngHelper.charsetLatin1)); + ba.write(palName.getBytes(PngHelperInternal.charsetLatin1)); ba.write(0); // separator ba.write((byte) sampledepth); int nentries = getNentries(); for (int n = 0; n < nentries; n++) { for (int i = 0; i < 4; i++) { if (sampledepth == 8) - PngHelper.writeByte(ba, (byte) palette[n * 5 + i]); + PngHelperInternal.writeByte(ba, (byte) palette[n * 5 + i]); else - PngHelper.writeInt2(ba, palette[n * 5 + i]); + PngHelperInternal.writeInt2(ba, palette[n * 5 + i]); } - PngHelper.writeInt2(ba, palette[n * 5 + 4]); + PngHelperInternal.writeInt2(ba, palette[n * 5 + 4]); } byte[] b = ba.toByteArray(); ChunkRaw chunk = createEmptyChunk(b.length, false); @@ -56,7 +57,7 @@ public class PngChunkSPLT extends PngChunk { } @Override - public void parseFromChunk(ChunkRaw c) { + public void parseFromRaw(ChunkRaw c) { int t = -1; for (int i = 0; i < c.data.length; i++) { // look for first zero if (c.data[i] == 0) { @@ -66,8 +67,8 @@ public class PngChunkSPLT extends PngChunk { } if (t <= 0 || t > c.data.length - 2) throw new PngjException("bad sPLT chunk: no separator found"); - palName = new String(c.data, 0, t, PngHelper.charsetLatin1); - sampledepth = PngHelper.readInt1fromByte(c.data, t + 1); + palName = new String(c.data, 0, t, PngHelperInternal.charsetLatin1); + sampledepth = PngHelperInternal.readInt1fromByte(c.data, t + 1); t += 2; int nentries = (c.data.length - t) / (sampledepth == 8 ? 6 : 10); palette = new int[nentries * 5]; @@ -75,21 +76,21 @@ public class PngChunkSPLT extends PngChunk { ne = 0; for (int i = 0; i < nentries; i++) { if (sampledepth == 8) { - r = PngHelper.readInt1fromByte(c.data, t++); - g = PngHelper.readInt1fromByte(c.data, t++); - b = PngHelper.readInt1fromByte(c.data, t++); - a = PngHelper.readInt1fromByte(c.data, t++); + r = PngHelperInternal.readInt1fromByte(c.data, t++); + g = PngHelperInternal.readInt1fromByte(c.data, t++); + b = PngHelperInternal.readInt1fromByte(c.data, t++); + a = PngHelperInternal.readInt1fromByte(c.data, t++); } else { - r = PngHelper.readInt2fromBytes(c.data, t); + r = PngHelperInternal.readInt2fromBytes(c.data, t); t += 2; - g = PngHelper.readInt2fromBytes(c.data, t); + g = PngHelperInternal.readInt2fromBytes(c.data, t); t += 2; - b = PngHelper.readInt2fromBytes(c.data, t); + b = PngHelperInternal.readInt2fromBytes(c.data, t); t += 2; - a = PngHelper.readInt2fromBytes(c.data, t); + a = PngHelperInternal.readInt2fromBytes(c.data, t); t += 2; } - f = PngHelper.readInt2fromBytes(c.data, t); + f = PngHelperInternal.readInt2fromBytes(c.data, t); t += 2; palette[ne++] = r; palette[ne++] = g; diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSRGB.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSRGB.java index 774558785..e4d77d40a 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSRGB.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSRGB.java @@ -1,12 +1,17 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-/*
+/**
+ * sRGB chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11sRGB
*/
-public class PngChunkSRGB extends PngChunk {
+public class PngChunkSRGB extends PngChunkSingle {
+ public final static String ID = ChunkHelper.sRGB;
+
// http://www.w3.org/TR/PNG/#11sRGB
public static final int RENDER_INTENT_Perceptual = 0;
@@ -17,28 +22,23 @@ public class PngChunkSRGB extends PngChunk { private int intent;
public PngChunkSRGB(ImageInfo info) {
- super(ChunkHelper.sRGB, info);
- }
-
- @Override
- public boolean mustGoBeforeIDAT() {
- return true;
+ super(ID, info);
}
@Override
- public boolean mustGoBeforePLTE() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_PLTE_AND_IDAT;
}
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
if (c.len != 1)
throw new PngjException("bad chunk length " + c);
- intent = PngHelper.readInt1fromByte(c.data, 0);
+ intent = PngHelperInternal.readInt1fromByte(c.data, 0);
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkRaw createRawChunk() {
ChunkRaw c = null;
c = createEmptyChunk(1, true);
c.data[0] = (byte) intent;
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSTER.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSTER.java new file mode 100644 index 000000000..4dc5edec5 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSTER.java @@ -0,0 +1,60 @@ +package jogamp.opengl.util.pngj.chunks;
+
+import jogamp.opengl.util.pngj.ImageInfo;
+import jogamp.opengl.util.pngj.PngjException;
+
+/**
+ * sTER chunk.
+ * <p>
+ * see http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.sTER
+ */
+public class PngChunkSTER extends PngChunkSingle {
+ public final static String ID = "sTER";
+
+ // http://www.libpng.org/pub/png/spec/register/pngext-1.3.0-pdg.html#C.sTER
+ private byte mode; // 0: cross-fuse layout 1: diverging-fuse layout
+
+ public PngChunkSTER(ImageInfo info) {
+ super(ID, info);
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.BEFORE_IDAT;
+ }
+
+ @Override
+ public ChunkRaw createRawChunk() {
+ ChunkRaw c = createEmptyChunk(1, true);
+ c.data[0] = (byte) mode;
+ return c;
+ }
+
+ @Override
+ public void parseFromRaw(ChunkRaw chunk) {
+ if (chunk.len != 1)
+ throw new PngjException("bad chunk length " + chunk);
+ mode = chunk.data[0];
+ }
+
+ @Override
+ public void cloneDataFromRead(PngChunk other) {
+ PngChunkSTER otherx = (PngChunkSTER) other;
+ this.mode = otherx.mode;
+ }
+
+ /**
+ * 0: cross-fuse layout 1: diverging-fuse layout
+ */
+ public byte getMode() {
+ return mode;
+ }
+
+ /**
+ * 0: cross-fuse layout 1: diverging-fuse layout
+ */
+ public void setMode(byte mode) {
+ this.mode = mode;
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java new file mode 100644 index 000000000..7df5ba021 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSingle.java @@ -0,0 +1,45 @@ +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.
+ */
+public abstract class PngChunkSingle extends PngChunk {
+
+ protected PngChunkSingle(String id, ImageInfo imgInfo) {
+ super(id, imgInfo);
+ }
+
+ @Override
+ public final boolean allowsMultiple() {
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PngChunkSingle other = (PngChunkSingle) obj;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSkipped.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSkipped.java new file mode 100644 index 000000000..f4c77b4e1 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkSkipped.java @@ -0,0 +1,41 @@ +package jogamp.opengl.util.pngj.chunks;
+
+import jogamp.opengl.util.pngj.ImageInfo;
+import jogamp.opengl.util.pngj.PngjException;
+
+/**
+ * Pseudo chunk type, for chunks that were skipped on reading
+ */
+public class PngChunkSkipped extends PngChunk {
+
+ public PngChunkSkipped(String id, ImageInfo info, int clen) {
+ super(id, info);
+ this.length = clen;
+ }
+
+ @Override
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NONE;
+ }
+
+ @Override
+ public ChunkRaw createRawChunk() {
+ throw new PngjException("Non supported for a skipped chunk");
+ }
+
+ @Override
+ public void parseFromRaw(ChunkRaw c) {
+ throw new PngjException("Non supported for a skipped chunk");
+ }
+
+ @Override
+ public void cloneDataFromRead(PngChunk other) {
+ throw new PngjException("Non supported for a skipped chunk");
+ }
+
+ @Override
+ public boolean allowsMultiple() {
+ return true;
+ }
+
+}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTEXT.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTEXT.java index c535fe34a..d97cd63c5 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTEXT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTEXT.java @@ -1,28 +1,40 @@ package jogamp.opengl.util.pngj.chunks;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
+import jogamp.opengl.util.pngj.PngjException;
+/**
+ * tEXt chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11tEXt
+ */
public class PngChunkTEXT extends PngChunkTextVar {
+ public final static String ID = ChunkHelper.tEXt;
+
public PngChunkTEXT(ImageInfo info) {
- super(ChunkHelper.tEXt, info);
+ super(ID, info);
}
@Override
- public ChunkRaw createChunk() {
- if (val.isEmpty() || key.isEmpty())
- return null;
- byte[] b = (key + "\0" + val).getBytes(PngHelper.charsetLatin1);
+ public ChunkRaw createRawChunk() {
+ if (key.isEmpty())
+ throw new PngjException("Text chunk key must be non empty");
+ byte[] b = (key + "\0" + val).getBytes(PngHelperInternal.charsetLatin1);
ChunkRaw chunk = createEmptyChunk(b.length, false);
chunk.data = b;
return chunk;
}
@Override
- public void parseFromChunk(ChunkRaw c) {
- String[] k = (new String(c.data, PngHelper.charsetLatin1)).split("\0");
- key = k[0];
- val = k[1];
+ public void parseFromRaw(ChunkRaw c) {
+ int i;
+ for (i = 0; i < c.data.length; i++)
+ if (c.data[i] == 0)
+ break;
+ key = new String(c.data, 0, i, PngHelperInternal.charsetLatin1);
+ i++;
+ val = i < c.data.length ? new String(c.data, i, c.data.length - i, PngHelperInternal.charsetLatin1) : "";
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTIME.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTIME.java index 37e617acb..8f34c78fe 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTIME.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTIME.java @@ -3,22 +3,33 @@ package jogamp.opengl.util.pngj.chunks; import java.util.Calendar;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
+/**
+ * tIME chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11tIME
+ */
+public class PngChunkTIME extends PngChunkSingle {
+ public final static String ID = ChunkHelper.tIME;
-public class PngChunkTIME extends PngChunk {
// http://www.w3.org/TR/PNG/#11tIME
private int year, mon, day, hour, min, sec;
public PngChunkTIME(ImageInfo info) {
- super(ChunkHelper.tIME, info);
+ super(ID, info);
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NONE;
+ }
+
+ @Override
+ public ChunkRaw createRawChunk() {
ChunkRaw c = createEmptyChunk(7, true);
- PngHelper.writeInt2tobytes(year, c.data, 0);
+ PngHelperInternal.writeInt2tobytes(year, c.data, 0);
c.data[2] = (byte) mon;
c.data[3] = (byte) day;
c.data[4] = (byte) hour;
@@ -28,15 +39,15 @@ public class PngChunkTIME extends PngChunk { }
@Override
- public void parseFromChunk(ChunkRaw chunk) {
+ public void parseFromRaw(ChunkRaw chunk) {
if (chunk.len != 7)
throw new PngjException("bad chunk " + chunk);
- year = PngHelper.readInt2fromBytes(chunk.data, 0);
- mon = PngHelper.readInt1fromByte(chunk.data, 2);
- day = PngHelper.readInt1fromByte(chunk.data, 3);
- hour = PngHelper.readInt1fromByte(chunk.data, 4);
- min = PngHelper.readInt1fromByte(chunk.data, 5);
- sec = PngHelper.readInt1fromByte(chunk.data, 6);
+ year = PngHelperInternal.readInt2fromBytes(chunk.data, 0);
+ mon = PngHelperInternal.readInt1fromByte(chunk.data, 2);
+ day = PngHelperInternal.readInt1fromByte(chunk.data, 3);
+ hour = PngHelperInternal.readInt1fromByte(chunk.data, 4);
+ min = PngHelperInternal.readInt1fromByte(chunk.data, 5);
+ sec = PngHelperInternal.readInt1fromByte(chunk.data, 6);
}
@Override
@@ -69,15 +80,14 @@ public class PngChunkTIME extends PngChunk { min = minx;
sec = secx;
}
+
public int[] getYMDHMS() {
return new int[] { year, mon, day, hour, min, sec };
}
/** format YYYY/MM/DD HH:mm:SS */
public String getAsString() {
- return String.format("%04/%02d/%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
+ return String.format("%04d/%02d/%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
}
-
-
}
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 9365e5e8e..867e34861 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTRNS.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTRNS.java @@ -1,129 +1,141 @@ -package jogamp.opengl.util.pngj.chunks;
-
-import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
-import jogamp.opengl.util.pngj.PngjException;
-
-/*
- */
-public class PngChunkTRNS extends PngChunk {
- // http://www.w3.org/TR/PNG/#11tRNS
- // this chunk structure depends on the image type
- // only one of these is meaningful
- private int gray;
- private int red, green, blue;
- private int[] paletteAlpha = new int[] {};
-
- public PngChunkTRNS(ImageInfo info) {
- super(ChunkHelper.tRNS, info);
- }
-
- @Override
- public boolean mustGoBeforeIDAT() {
- return true;
- }
-
- @Override
- public boolean mustGoAfterPLTE() {
- return true;
- }
-
- @Override
- public ChunkRaw createChunk() {
- ChunkRaw c = null;
- if (imgInfo.greyscale) {
- c = createEmptyChunk(2, true);
- PngHelper.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);
- PngHelper.writeInt2tobytes(red, c.data, 0);
- PngHelper.writeInt2tobytes(green, c.data, 0);
- PngHelper.writeInt2tobytes(blue, c.data, 0);
- }
- return c;
- }
-
- @Override
- public void parseFromChunk(ChunkRaw c) {
- if (imgInfo.greyscale) {
- gray = PngHelper.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 = PngHelper.readInt2fromBytes(c.data, 0);
- green = PngHelper.readInt2fromBytes(c.data, 2);
- blue = PngHelper.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;
- }
-
- /**
- * 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/PngChunkTextVar.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTextVar.java index 3d92a806f..ba3ffc30c 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTextVar.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkTextVar.java @@ -3,11 +3,9 @@ package jogamp.opengl.util.pngj.chunks; import jogamp.opengl.util.pngj.ImageInfo;
/**
- * superclass for three textual chunks (TEXT, ITXT, ZTXT)
- *
- * @author Hernan J Gonzalez
+ * Superclass (abstract) for three textual chunks (TEXT, ITXT, ZTXT)
*/
-public abstract class PngChunkTextVar extends PngChunk {
+public abstract class PngChunkTextVar extends PngChunkMultiple {
protected String key; // key/val: only for tEXt. lazy computed
protected String val;
@@ -28,8 +26,8 @@ public abstract class PngChunkTextVar extends PngChunk { }
@Override
- public boolean allowsMultiple() {
- return true;
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NONE;
}
public static class PngTxtInfo {
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkUNKNOWN.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkUNKNOWN.java index 15a35935a..3803428e6 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkUNKNOWN.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkUNKNOWN.java @@ -2,7 +2,12 @@ package jogamp.opengl.util.pngj.chunks; import jogamp.opengl.util.pngj.ImageInfo;
-public class PngChunkUNKNOWN extends PngChunk { // unkown, custom or not
+/**
+ * Placeholder for UNKNOWN (custom or not) chunks.
+ * <p>
+ * For PngReader, a chunk is unknown if it's not registered in the chunk factory
+ */
+public class PngChunkUNKNOWN extends PngChunkMultiple { // unkown, custom or not
private byte[] data;
@@ -10,25 +15,25 @@ public class PngChunkUNKNOWN extends PngChunk { // unkown, custom or not super(id, info);
}
- @Override
- public boolean allowsMultiple() {
- return true;
- }
-
private PngChunkUNKNOWN(PngChunkUNKNOWN c, ImageInfo info) {
super(c.id, info);
System.arraycopy(c.data, 0, data, 0, c.data.length);
}
@Override
- public ChunkRaw createChunk() {
+ public ChunkOrderingConstraint getOrderingConstraint() {
+ return ChunkOrderingConstraint.NONE;
+ }
+
+ @Override
+ public ChunkRaw createRawChunk() {
ChunkRaw p = createEmptyChunk(data.length, false);
p.data = this.data;
return p;
}
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
data = c.data;
}
diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkZTXT.java b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkZTXT.java index fd6c08273..64593eae4 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkZTXT.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngChunkZTXT.java @@ -4,26 +4,32 @@ import java.io.ByteArrayOutputStream; import java.io.IOException;
import jogamp.opengl.util.pngj.ImageInfo;
-import jogamp.opengl.util.pngj.PngHelper;
+import jogamp.opengl.util.pngj.PngHelperInternal;
import jogamp.opengl.util.pngj.PngjException;
-
+/**
+ * zTXt chunk.
+ * <p>
+ * see http://www.w3.org/TR/PNG/#11zTXt
+ */
public class PngChunkZTXT extends PngChunkTextVar {
+ public final static String ID = ChunkHelper.zTXt;
+
// http://www.w3.org/TR/PNG/#11zTXt
public PngChunkZTXT(ImageInfo info) {
- super(ChunkHelper.zTXt, info);
+ super(ID, info);
}
@Override
- public ChunkRaw createChunk() {
- if (val.isEmpty() || key.isEmpty())
- return null;
+ public ChunkRaw createRawChunk() {
+ if (key.isEmpty())
+ throw new PngjException("Text chunk key must be non empty");
try {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
- ba.write(key.getBytes(PngHelper.charsetLatin1));
+ ba.write(key.getBytes(PngHelperInternal.charsetLatin1));
ba.write(0); // separator
ba.write(0); // compression method: 0
- byte[] textbytes = ChunkHelper.compressBytes(val.getBytes(PngHelper.charsetLatin1), true);
+ byte[] textbytes = ChunkHelper.compressBytes(val.getBytes(PngHelperInternal.charsetLatin1), true);
ba.write(textbytes);
byte[] b = ba.toByteArray();
ChunkRaw chunk = createEmptyChunk(b.length, false);
@@ -35,7 +41,7 @@ public class PngChunkZTXT extends PngChunkTextVar { }
@Override
- public void parseFromChunk(ChunkRaw c) {
+ public void parseFromRaw(ChunkRaw c) {
int nullsep = -1;
for (int i = 0; i < c.data.length; i++) { // look for first zero
if (c.data[i] != 0)
@@ -45,12 +51,12 @@ public class PngChunkZTXT extends PngChunkTextVar { }
if (nullsep < 0 || nullsep > c.data.length - 2)
throw new PngjException("bad zTXt chunk: no separator found");
- key = new String(c.data, 0, nullsep, PngHelper.charsetLatin1);
+ key = new String(c.data, 0, nullsep, PngHelperInternal.charsetLatin1);
int compmet = (int) c.data[nullsep + 1];
if (compmet != 0)
throw new PngjException("bad zTXt chunk: unknown compression method");
byte[] uncomp = ChunkHelper.compressBytes(c.data, nullsep + 2, c.data.length - nullsep - 2, false); // uncompress
- val = new String(uncomp, PngHelper.charsetLatin1);
+ val = new String(uncomp, PngHelperInternal.charsetLatin1);
}
@Override
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 a82754588..139603448 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngMetadata.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/chunks/PngMetadata.java @@ -1,68 +1,72 @@ package jogamp.opengl.util.pngj.chunks; -import jogamp.opengl.util.pngj.PngHelper; +import java.util.ArrayList; +import java.util.List; + +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 - * - * 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 - * + * <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 */ public class PngMetadata { - private final ChunkList chunkList; + private final ChunksList chunkList; private final boolean readonly; - public PngMetadata(ChunkList chunks, boolean readonly) { + public PngMetadata(ChunksList chunks) { this.chunkList = chunks; - this.readonly = readonly; + if (chunks instanceof ChunksListForWrite) { + this.readonly = false; + } else { + this.readonly = true; + } } /** * 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. */ - public boolean setChunk(PngChunk c, boolean overwriteIfPresent) { + public void queueChunk(final PngChunk c, boolean lazyOverwrite) { + ChunksListForWrite cl = getChunkListW(); if (readonly) throw new PngjException("cannot set chunk : readonly metadata"); - return chunkList.setChunk(c, overwriteIfPresent); + if (lazyOverwrite) { + ChunkHelper.trimList(cl.getQueuedChunks(), new ChunkPredicate() { + @Override + public boolean match(PngChunk c2) { + return ChunkHelper.equivalent(c, c2); + } + }); + } + cl.queue(c); } - - /** - * Returns only one chunk or null if nothing found - does not include queued chunks - * - * If more than one chunk (after filtering by inner id) is found, then an exception is thrown (failifMultiple=true) - * or the last one is returned (failifMultiple=false) - * - * @param id Chunk id - * @param innerid if not null, the chunk is assumed to be PngChunkTextVar or PngChunkSPLT, and filtered by that 'internal id' - * @param failIfMultiple throw exception if more that one - * @return chunk (not cloned) - */ - public PngChunk getChunk1(String id, String innerid, boolean failIfMultiple) { - return chunkList.getChunk1(id, innerid, failIfMultiple); + public void queueChunk(final PngChunk c) { + queueChunk(c, true); } - /** - * Same as getChunk1(id, innerid=null, failIfMultiple=true); - */ - public PngChunk getChunk1(String id) { - return chunkList.getChunk1(id); + private ChunksListForWrite getChunkListW() { + return (ChunksListForWrite) chunkList; } // ///// high level utility methods follow //////////// // //////////// DPI - /** - * returns -1 if not found or dimension unknown - **/ + /** + * returns -1 if not found or dimension unknown + */ public double[] getDpi() { - PngChunk c = getChunk1(ChunkHelper.pHYs, null, true); + PngChunk c = chunkList.getById1(ChunkHelper.pHYs, true); if (c == null) return new double[] { -1, -1 }; else @@ -76,31 +80,71 @@ public class PngMetadata { public void setDpi(double x, double y) { PngChunkPHYS c = new PngChunkPHYS(chunkList.imageInfo); c.setAsDpi2(x, y); - setChunk(c, true); + queueChunk(c); } // //////////// TIME - public void setTimeNow(int secsAgo) { + /** + * 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 + */ + public PngChunkTIME setTimeNow(int secsAgo) { PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); c.setNow(secsAgo); - setChunk(c, true); + queueChunk(c); + return c; + } + + public PngChunkTIME setTimeNow() { + return setTimeNow(0); } - public void setTimeYMDHMS(int yearx, int monx, int dayx, int hourx, int minx, int secx) { + /** + * 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 + */ + public PngChunkTIME setTimeYMDHMS(int yearx, int monx, int dayx, int hourx, int minx, int secx) { PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); c.setYMDHMS(yearx, monx, dayx, hourx, minx, secx); - setChunk(c, true); + queueChunk(c, true); + return c; + } + + /** + * null if not found + */ + public PngChunkTIME getTime() { + return (PngChunkTIME) chunkList.getById1(ChunkHelper.tIME); } public String getTimeAsString() { - PngChunk c = getChunk1(ChunkHelper.tIME, null, true); - return c != null ? ((PngChunkTIME) c).getAsString() : ""; + PngChunkTIME c = getTime(); + return c == null ? "" : c.getAsString(); } // //////////// TEXT - public void setText(String k, String val, boolean useLatin1, boolean compress) { + /** + * Creates a text chunk and queue it. + * <p> + * + * @param k + * : key (latin1) + * @param val + * (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 + */ + public PngChunkTextVar setText(String k, String val, boolean useLatin1, boolean compress) { if (compress && !useLatin1) throw new PngjException("cannot compress non latin text"); PngChunkTextVar c; @@ -115,21 +159,83 @@ public class PngMetadata { ((PngChunkITXT) c).setLangtag(k); // we use the same orig tag (this is not quite right) } c.setKeyVal(k, val); - setChunk(c, true); + queueChunk(c, true); + return c; } - public void setText(String k, String val) { - setText(k, val, false, val.length() > 400); + public PngChunkTextVar setText(String k, String val) { + return setText(k, val, false, false); } - /** tries all text chunks - returns null if not found */ + /** + * gets all text chunks with a given key + * <p> + * returns null if not found + * <p> + * Warning: this does not check the "lang" key of iTxt + */ + @SuppressWarnings("unchecked") + public List<? extends PngChunkTextVar> getTxtsForKey(String k) { + @SuppressWarnings("rawtypes") + List c = new ArrayList(); + c.addAll(chunkList.getById(ChunkHelper.tEXt, k)); + c.addAll(chunkList.getById(ChunkHelper.zTXt, k)); + c.addAll(chunkList.getById(ChunkHelper.iTXt, k)); + return c; + } + + /** + * Returns empty if not found, concatenated (with newlines) if multiple! - + * and trimmed + * <p> + * Use getTxtsForKey() if you don't want this behaviour + */ public String getTxtForKey(String k) { - PngChunk c = getChunk1(ChunkHelper.tEXt, k, true); - if (c == null) - c = getChunk1(ChunkHelper.zTXt, k, true); - if (c == null) - c = getChunk1(ChunkHelper.iTXt, k, true); - return c != null ? ((PngChunkTextVar) c).getVal() : null; + List<? extends PngChunkTextVar> li = getTxtsForKey(k); + if (li.isEmpty()) + return ""; + StringBuilder t = new StringBuilder(); + for (PngChunkTextVar c : li) + t.append(c.getVal()).append("\n"); + return t.toString().trim(); + } + + /** + * Returns the palette chunk, if present + * + * @return null if not present + */ + public PngChunkPLTE getPLTE() { + return (PngChunkPLTE) chunkList.getById1(PngChunkPLTE.ID); + } + + /** + * 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); + queueChunk(plte); + return plte; + } + + /** + * Returns the TRNS chunk, if present + * + * @return null if not present + */ + public PngChunkTRNS getTRNS() { + return (PngChunkTRNS) chunkList.getById1(PngChunkTRNS.ID); + } + + /** + * 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); + queueChunk(trns); + return trns; } } diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/package.html b/src/jogl/classes/jogamp/opengl/util/pngj/package.html index 209b39c59..0b0e2c8c1 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/package.html +++ b/src/jogl/classes/jogamp/opengl/util/pngj/package.html @@ -1,11 +1,10 @@ <html>
<body bgcolor="white">
<p>
-Contains the main classes for the PNGJ library.<p>
-Client code should rarely need more than the public members of this package.
+PNGJ main package
</p>
<p>
-See also the <code>nosandbox</code> package if available.
+Client code should rarely need more than the public members of this package.
</p>
</body>
</html>
diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java index 5ff63d93b..feacdb951 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLGLCapabilities.java @@ -28,6 +28,8 @@ package jogamp.opengl.windows.wgl; +import java.nio.IntBuffer; + import jogamp.nativewindow.windows.GDI; import jogamp.nativewindow.windows.PIXELFORMATDESCRIPTOR; @@ -52,6 +54,7 @@ public class WGLGLCapabilities extends GLCapabilities { public boolean setValuesByGDI() { arb_pixelformat = -1; + // ALPHA shall be set at last - due to it's auto setting by !opaque / samples setRedBits(pfd.getCRedBits()); setGreenBits(pfd.getCGreenBits()); setBlueBits(pfd.getCBlueBits()); @@ -62,10 +65,11 @@ public class WGLGLCapabilities extends GLCapabilities { setAccumAlphaBits(pfd.getCAccumAlphaBits()); setDepthBits(pfd.getCDepthBits()); setStencilBits(pfd.getCStencilBits()); - setDoubleBuffered((pfd.getDwFlags() & GDI.PFD_DOUBLEBUFFER) != 0); - setStereo((pfd.getDwFlags() & GDI.PFD_STEREO) != 0); - setHardwareAccelerated((pfd.getDwFlags() & GDI.PFD_GENERIC_FORMAT) == 0 - || (pfd.getDwFlags() & GDI.PFD_GENERIC_ACCELERATED) != 0); + final int dwFlags = pfd.getDwFlags(); + setDoubleBuffered((dwFlags & GDI.PFD_DOUBLEBUFFER) != 0); + setStereo((dwFlags & GDI.PFD_STEREO) != 0); + setHardwareAccelerated((dwFlags & GDI.PFD_GENERIC_FORMAT) == 0 + || (dwFlags & GDI.PFD_GENERIC_ACCELERATED) != 0); // n/a with non ARB/GDI method: // multisample // opaque @@ -74,11 +78,49 @@ public class WGLGLCapabilities extends GLCapabilities { return true; } - public boolean setValuesByARB(final int[] iattribs, final int niattribs, final int[] iresults) { + public static final String PFD2String(PIXELFORMATDESCRIPTOR pfd, int pfdID) { + final int dwFlags = pfd.getDwFlags(); + StringBuilder sb = new StringBuilder(); + boolean sep = false; + + if( 0 != (GDI.PFD_DRAW_TO_WINDOW & dwFlags ) ) { + sep = true; + sb.append("window"); + } + if( 0 != (GDI.PFD_DRAW_TO_BITMAP & dwFlags ) ) { + if(sep) { sb.append(CSEP); } sep=true; + sb.append("bitmap"); + } + if( 0 != (GDI.PFD_SUPPORT_OPENGL & dwFlags ) ) { + if(sep) { sb.append(CSEP); } sep=true; + sb.append("opengl"); + } + if( 0 != (GDI.PFD_DOUBLEBUFFER & dwFlags ) ) { + if(sep) { sb.append(CSEP); } sep=true; + sb.append("dblbuf"); + } + if( 0 != (GDI.PFD_STEREO & dwFlags ) ) { + if(sep) { sb.append(CSEP); } sep=true; + sb.append("stereo"); + } + if( 0 == (GDI.PFD_GENERIC_FORMAT & dwFlags ) || 0 == (GDI.PFD_GENERIC_ACCELERATED & dwFlags ) ) { + if(sep) { sb.append(CSEP); } sep=true; + sb.append("hw-accel"); + } + return "PFD[id = "+pfdID+" (0x"+Integer.toHexString(pfdID)+ + "), colorBits "+pfd.getCColorBits()+", rgba "+pfd.getCRedBits()+ESEP+pfd.getCGreenBits()+ESEP+pfd.getCBlueBits()+ESEP+pfd.getCAlphaBits()+ + ", accum-rgba "+pfd.getCAccumRedBits()+ESEP+pfd.getCAccumGreenBits()+ESEP+pfd.getCAccumBlueBits()+ESEP+pfd.getCAccumAlphaBits()+ + ", dp/st/ms: "+pfd.getCDepthBits()+ESEP+pfd.getCStencilBits()+ESEP+"0"+ + ", flags: "+sb.toString(); + } + + public boolean setValuesByARB(final IntBuffer iattribs, final int niattribs, final IntBuffer iresults) { arb_pixelformat = 1; + int alphaBits = 0; for (int i = 0; i < niattribs; i++) { - int attr = iattribs[i]; + final int attr = iattribs.get(i); + final int res = iresults.get(i); switch (attr) { case WGLExt.WGL_DRAW_TO_WINDOW_ARB: case WGLExt.WGL_DRAW_TO_BITMAP_ARB: @@ -86,101 +128,99 @@ public class WGLGLCapabilities extends GLCapabilities { break; case WGLExt.WGL_ACCELERATION_ARB: - setHardwareAccelerated(iresults[i] == WGLExt.WGL_FULL_ACCELERATION_ARB); + setHardwareAccelerated(res == WGLExt.WGL_FULL_ACCELERATION_ARB); break; case WGLExt.WGL_SUPPORT_OPENGL_ARB: - if (iresults[i] != GL.GL_TRUE) { + if (res != GL.GL_TRUE) { return false; } break; case WGLExt.WGL_DEPTH_BITS_ARB: - setDepthBits(iresults[i]); + setDepthBits(res); break; case WGLExt.WGL_STENCIL_BITS_ARB: - setStencilBits(iresults[i]); + setStencilBits(res); break; case WGLExt.WGL_DOUBLE_BUFFER_ARB: - setDoubleBuffered(iresults[i] == GL.GL_TRUE); + setDoubleBuffered(res == GL.GL_TRUE); break; case WGLExt.WGL_STEREO_ARB: - setStereo(iresults[i] == GL.GL_TRUE); + setStereo(res == GL.GL_TRUE); break; case WGLExt.WGL_PIXEL_TYPE_ARB: - if(iresults[i] == WGLExt.WGL_TYPE_COLORINDEX_ARB) { + if(res == WGLExt.WGL_TYPE_COLORINDEX_ARB) { return false; // color index not supported } - if (iresults[i] == WGLExt.WGL_TYPE_RGBA_FLOAT_ARB) { - setPbufferFloatingPointBuffers(true); + if (res == WGLExt.WGL_TYPE_RGBA_FLOAT_ARB) { + return false; // not supported } - + // normal RGBA FB: WGLExt.WGL_TYPE_RGBA_ARB // ignore unknown results here break; - case WGLExt.WGL_FLOAT_COMPONENTS_NV: - if (iresults[i] != 0) { - setPbufferFloatingPointBuffers(true); - } - break; - case WGLExt.WGL_RED_BITS_ARB: - setRedBits(iresults[i]); + setRedBits(res); break; case WGLExt.WGL_GREEN_BITS_ARB: - setGreenBits(iresults[i]); + setGreenBits(res); break; case WGLExt.WGL_BLUE_BITS_ARB: - setBlueBits(iresults[i]); + setBlueBits(res); break; case WGLExt.WGL_ALPHA_BITS_ARB: - setAlphaBits(iresults[i]); + // ALPHA shall be set at last - due to it's auto setting by !opaque / samples + alphaBits = res; break; case WGLExt.WGL_ACCUM_RED_BITS_ARB: - setAccumRedBits(iresults[i]); + setAccumRedBits(res); break; case WGLExt.WGL_ACCUM_GREEN_BITS_ARB: - setAccumGreenBits(iresults[i]); + setAccumGreenBits(res); break; case WGLExt.WGL_ACCUM_BLUE_BITS_ARB: - setAccumBlueBits(iresults[i]); + setAccumBlueBits(res); break; case WGLExt.WGL_ACCUM_ALPHA_BITS_ARB: - setAccumAlphaBits(iresults[i]); + setAccumAlphaBits(res); break; case WGLExt.WGL_SAMPLE_BUFFERS_ARB: - setSampleBuffers(iresults[i] != 0); + setSampleBuffers(res != 0); break; case WGLExt.WGL_SAMPLES_ARB: - setNumSamples(iresults[i]); + setNumSamples(res); break; default: - throw new GLException("Unknown pixel format attribute " + iattribs[i]); + throw new GLException("Unknown pixel format attribute " + attr); } } + setAlphaBits(alphaBits); return true; } + @Override public Object cloneMutable() { return clone(); } + @Override public Object clone() { try { return super.clone(); @@ -191,11 +231,11 @@ public class WGLGLCapabilities extends GLCapabilities { final public PIXELFORMATDESCRIPTOR getPFD() { return pfd; } final public int getPFDID() { return pfdID; } - + final public boolean isSetByARB() { return 0 < arb_pixelformat; } final public boolean isSetByGDI() { return 0 > arb_pixelformat; } final public boolean isSet() { return 0 != arb_pixelformat; } - + @Override final public int getVisualID(VIDType type) throws NativeWindowException { switch(type) { @@ -205,16 +245,17 @@ public class WGLGLCapabilities extends GLCapabilities { return getPFDID(); default: throw new NativeWindowException("Invalid type <"+type+">"); - } + } } - + + @Override public StringBuilder toString(StringBuilder sink) { if(null == sink) { sink = new StringBuilder(); } - sink.append("wgl vid 0x").append(Integer.toHexString(pfdID)).append(" "); + sink.append("wgl vid ").append(pfdID).append(" "); switch (arb_pixelformat) { - case -1: + case -1: sink.append("gdi"); break; case 0: @@ -229,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..6454a34b5 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WGLUtil.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WGLUtil.java @@ -33,15 +33,15 @@ import jogamp.opengl.Debug; public class WGLUtil { /** - * Switch to use the <code>wgl</code> variants of {@link jogamp.opengl.windows.wgl.WGL} + * Switch to use the <code>wgl</code> variants of {@link jogamp.opengl.windows.wgl.WGL} * to replace the following 5 GDI based functions (see below). * <p> * Disabled per default. - * </p> + * </p> * <p> * You can enable it by defining the property <code>jogl.windows.useWGLVersionOf5WGLGDIFuncSet</code>. * </p> - * + * * @see jogamp.nativewindow.windows.GDI#ChoosePixelFormat(long, PIXELFORMATDESCRIPTOR) * @see jogamp.nativewindow.windows.GDI#DescribePixelFormat(long, int, int, PIXELFORMATDESCRIPTOR) * @see jogamp.nativewindow.windows.GDI#GetPixelFormat(long) @@ -49,8 +49,9 @@ public class WGLUtil { * @see jogamp.nativewindow.windows.GDI#SwapBuffers(long) */ 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."); @@ -62,34 +63,34 @@ public class WGLUtil { return WGL.wglChoosePixelFormat(hdc, pfd); } else { return GDI.ChoosePixelFormat(hdc, pfd); - } + } } public static int DescribePixelFormat(long hdc, int pfdid, int pfdSize, PIXELFORMATDESCRIPTOR pfd) { if(USE_WGLVersion_Of_5WGLGDIFuncSet) { return WGL.wglDescribePixelFormat(hdc, pfdid, pfdSize, pfd); } else { return GDI.DescribePixelFormat(hdc, pfdid, pfdSize, pfd); - } + } } public static int GetPixelFormat(long hdc) { if(USE_WGLVersion_Of_5WGLGDIFuncSet) { return WGL.wglGetPixelFormat(hdc); } else { return GDI.GetPixelFormat(hdc); - } + } } - public static boolean SetPixelFormat(long hdc, int pfdid, PIXELFORMATDESCRIPTOR pfd) { + public static boolean SetPixelFormat(long hdc, int pfdid, PIXELFORMATDESCRIPTOR pfd) { if(USE_WGLVersion_Of_5WGLGDIFuncSet) { return WGL.wglSetPixelFormat(hdc, pfdid, pfd); } else { return GDI.SetPixelFormat(hdc, pfdid, pfd); - } + } } - public static boolean SwapBuffers(long hdc) { + public static boolean SwapBuffers(long hdc) { if(USE_WGLVersion_Of_5WGLGDIFuncSet) { return WGL.wglSwapBuffers(hdc); } else { return GDI.SwapBuffers(hdc); - } + } } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLContext.java deleted file mode 100644 index 68b26b30d..000000000 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLContext.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.windows.wgl; - -import javax.media.opengl.*; - -public class WindowsBitmapWGLContext extends WindowsWGLContext { - public WindowsBitmapWGLContext(WindowsBitmapWGLDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } - - public int getOffscreenContextPixelDataType() { - return GL.GL_UNSIGNED_BYTE; - } - - public int getOffscreenContextReadBuffer() { - // On Windows these contexts are always single-buffered - return GL.GL_FRONT; - } - - public boolean offscreenImageNeedsVerticalFlip() { - // We can take care of this in the DIB creation (see below) - return false; - } -} diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLDrawable.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLDrawable.java index 574226570..f658a3598 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsBitmapWGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,41 +29,62 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.windows.wgl; -import com.jogamp.common.nio.PointerBuffer; +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.SurfaceChangeable; +import javax.media.nativewindow.MutableSurface; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; import jogamp.nativewindow.windows.BITMAPINFO; import jogamp.nativewindow.windows.BITMAPINFOHEADER; import jogamp.nativewindow.windows.GDI; -import javax.media.opengl.GLCapabilitiesImmutable; +import jogamp.opengl.GLGraphicsConfigurationUtil; + +import com.jogamp.common.nio.PointerBuffer; public class WindowsBitmapWGLDrawable extends WindowsWGLDrawable { private long origbitmap; private long hbitmap; - protected WindowsBitmapWGLDrawable(GLDrawableFactory factory, NativeSurface target) { - super(factory, target, false); + private WindowsBitmapWGLDrawable(GLDrawableFactory factory, NativeSurface comp) { + super(factory, comp, false); } - protected void destroyImpl() { - setRealized(false); + protected static WindowsBitmapWGLDrawable create(GLDrawableFactory factory, NativeSurface comp) { + final WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration)comp.getGraphicsConfiguration(); + final AbstractGraphicsDevice aDevice = config.getScreen().getDevice(); + if( !GLProfile.isAvailable(aDevice, GLProfile.GL2) ) { + throw new GLException("GLProfile GL2 n/a on "+aDevice+" but required for Windows BITMAP"); + } + final GLProfile glp = GLProfile.get(GLProfile.GL2); + final GLCapabilitiesImmutable capsChosen0 = (GLCapabilitiesImmutable)config.getChosenCapabilities(); + // RGB555 and also alpha channel is experienced to fail on some Windows machines + final GLCapabilitiesImmutable capsChosen1 = GLGraphicsConfigurationUtil.clipRGBAGLCapabilities(capsChosen0, false /* allowRGB555 */, false /* allowAlpha */); + final GLCapabilitiesImmutable capsChosen2 = GLGraphicsConfigurationUtil.fixGLProfile(capsChosen1, glp); + if( capsChosen0 != capsChosen2 ) { + config.setChosenCapabilities(capsChosen2); + if(DEBUG) { + System.err.println("WindowsBitmapWGLDrawable: "+capsChosen0+" -> "+capsChosen2); + } + } + return new WindowsBitmapWGLDrawable(factory, comp); } - + + @Override protected void setRealizedImpl() { if(realized) { createBitmap(); @@ -72,35 +93,44 @@ public class WindowsBitmapWGLDrawable extends WindowsWGLDrawable { } } + @Override public GLContext createContext(GLContext shareWith) { - return new WindowsBitmapWGLContext(this, shareWith); + return new WindowsWGLContext(this, shareWith); + } + + @Override + public boolean isGLOriented() { + return false; } private void createBitmap() { int werr; - NativeSurface ns = getNativeSurface(); + final NativeSurface ns = getNativeSurface(); if(DEBUG) { - System.err.println("WindowsBitmapWGLDrawable (1): "+ns); + System.err.println(getThreadName()+": WindowsBitmapWGLDrawable (1): "+ns); } - WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration)ns.getGraphicsConfiguration(); - GLCapabilitiesImmutable capabilities = (GLCapabilitiesImmutable)config.getRequestedCapabilities(); - int width = getWidth(); - int height = getHeight(); + final WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration)ns.getGraphicsConfiguration(); + final GLCapabilitiesImmutable capsChosen = (GLCapabilitiesImmutable)config.getChosenCapabilities(); + final int width = getWidth(); + final int height = getHeight(); // // 1. Create DIB Section // - BITMAPINFO info = BITMAPINFO.create(); - BITMAPINFOHEADER header = info.getBmiHeader(); - int bitsPerPixel = (capabilities.getRedBits() + - capabilities.getGreenBits() + - capabilities.getBlueBits() + - capabilities.getAlphaBits()); + final BITMAPINFO info = BITMAPINFO.create(); + final BITMAPINFOHEADER header = info.getBmiHeader(); + final int bitsPerPixelIn = capsChosen.getRedBits() + + capsChosen.getGreenBits() + + capsChosen.getBlueBits(); + final int bitsPerPixel; + // Note: For BITMAP 32 bpp, the high-byte is _not_ used and hence maximum color is RGB888! + // Note: For BITAMP a biBitCount value other than 24 (RGB888) usually does not work! + bitsPerPixel = 24; // RGB888 only! header.setBiSize(BITMAPINFOHEADER.size()); header.setBiWidth(width); - // NOTE: negating the height causes the DIB to be in top-down row - // order rather than bottom-up; ends up being correct during pixel - // readback + // NOTE: Positive height causes the DIB's origin at bottom-left (OpenGL), + // a negative height causes the DIB's origin at top-left (Java AWT, Windows, ..). + // We use !OpenGL origin to remove the need for vertical flip, see 'isGLOriented()' above. header.setBiHeight(-1 * height); header.setBiPlanes((short) 1); header.setBiBitCount((short) bitsPerPixel); @@ -109,21 +139,21 @@ public class WindowsBitmapWGLDrawable extends WindowsWGLDrawable { header.setBiClrUsed(0); header.setBiClrImportant(0); header.setBiCompression(GDI.BI_RGB); - int byteNum = width * height * ( bitsPerPixel >> 3 ) ; + final int byteNum = width * height * ( bitsPerPixel >> 3 ) ; header.setBiSizeImage(byteNum); - PointerBuffer pb = PointerBuffer.allocateDirect(1); + final PointerBuffer pb = PointerBuffer.allocateDirect(1); hbitmap = GDI.CreateDIBSection(0, info, GDI.DIB_RGB_COLORS, pb, 0, 0); werr = GDI.GetLastError(); if(DEBUG) { long p = ( pb.capacity() > 0 ) ? pb.get(0) : 0; System.err.println("WindowsBitmapWGLDrawable: pb sz/ptr "+pb.capacity() + ", "+toHexString(p)); System.err.println("WindowsBitmapWGLDrawable: " + width+"x"+height + - ", bpp " + bitsPerPixel + + ", bpp " + bitsPerPixelIn + " -> " + bitsPerPixel + ", bytes " + byteNum + ", header sz " + BITMAPINFOHEADER.size() + ", DIB ptr num " + pb.capacity()+ - ", "+capabilities+ + ", "+capsChosen+ ", werr "+werr); } if (hbitmap == 0) { @@ -140,9 +170,9 @@ public class WindowsBitmapWGLDrawable extends WindowsWGLDrawable { hbitmap = 0; throw new GLException("Error creating device context for offscreen OpenGL context, werr "+werr); } - ((SurfaceChangeable)ns).setSurfaceHandle(hdc); + ((MutableSurface)ns).setSurfaceHandle(hdc); if(DEBUG) { - System.err.println("WindowsBitmapWGLDrawable (2): "+ns); + System.err.println(getThreadName()+": WindowsBitmapWGLDrawable (2): "+ns); } if ((origbitmap = GDI.SelectObject(hdc, hbitmap)) == 0) { @@ -152,10 +182,10 @@ public class WindowsBitmapWGLDrawable extends WindowsWGLDrawable { hdc = 0; throw new GLException("Error selecting bitmap into new device context"); } - + config.updateGraphicsConfiguration(getFactory(), ns, null); } - + protected void destroyBitmap() { NativeSurface ns = getNativeSurface(); if (ns.getSurfaceHandle() != 0) { @@ -165,7 +195,7 @@ public class WindowsBitmapWGLDrawable extends WindowsWGLDrawable { GDI.DeleteDC(ns.getSurfaceHandle()); origbitmap = 0; hbitmap = 0; - ((SurfaceChangeable)ns).setSurfaceHandle(0); + ((MutableSurface)ns).setSurfaceHandle(0); } } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsDummyWGLDrawable.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsDummyWGLDrawable.java deleted file mode 100644 index 3ba5508ab..000000000 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsDummyWGLDrawable.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.windows.wgl; - -import javax.media.opengl.GLContext; -import javax.media.opengl.GLDrawableFactory; -import javax.media.opengl.GLProfile; - -import javax.media.nativewindow.AbstractGraphicsScreen; -import jogamp.nativewindow.windows.GDI; -import jogamp.nativewindow.windows.GDIUtil; - -import javax.media.nativewindow.NativeSurface; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLException; -import jogamp.nativewindow.windows.GDISurface; - -public class WindowsDummyWGLDrawable extends WindowsWGLDrawable { - private long hwnd; - private boolean handleHwndLifecycle; - - private WindowsDummyWGLDrawable(GLDrawableFactory factory, GDISurface ns, boolean handleHwndLifecycle) { - super(factory, ns, true); - this.handleHwndLifecycle = handleHwndLifecycle; - - if(NativeSurface.LOCK_SURFACE_NOT_READY >= ns.lockSurface()) { - throw new GLException("WindowsDummyWGLDrawable: surface not ready (lockSurface)"); - } - try { - WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration)ns.getGraphicsConfiguration(); - config.updateGraphicsConfiguration(factory, ns, null); - if (DEBUG) { - System.err.println("WindowsDummyWGLDrawable: "+config); - } - } catch (Throwable t) { - destroyImpl(); - throw new GLException(t); - } finally { - unlockSurface(); - } - } - - public static WindowsDummyWGLDrawable create(GLDrawableFactory factory, GLProfile glp, AbstractGraphicsScreen absScreen, - long windowHandle, int width, int height, boolean handleWindowLifecycle) { - if(0 == windowHandle) { - throw new GLException("Error windowHandle 0, werr: "+GDI.GetLastError()); - } - GLCapabilities caps = new GLCapabilities(glp); - WindowsWGLGraphicsConfiguration cfg = WindowsWGLGraphicsConfigurationFactory.createDefaultGraphicsConfiguration(caps, absScreen); - GDISurface ns = new GDISurface(cfg, windowHandle); - ns.surfaceSizeChanged(width, height); - return new WindowsDummyWGLDrawable(factory, ns, handleWindowLifecycle); - } - - public GLContext createContext(GLContext shareWith) { - // FIXME: figure out how to hook back in the Java 2D / JOGL bridge - return new WindowsWGLContext(this, shareWith); - } - - protected void destroyImpl() { - if (handleHwndLifecycle && hwnd != 0) { - GDI.ShowWindow(hwnd, GDI.SW_HIDE); - GDIUtil.DestroyDummyWindow(hwnd); - hwnd = 0; - } - } -} diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLContext.java index 745782085..c46b3c9dd 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLContext.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLContext.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -50,14 +50,11 @@ import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import com.jogamp.nativewindow.WrappedSurface; - +import jogamp.nativewindow.WrappedSurface; import jogamp.nativewindow.windows.GDI; import jogamp.opengl.GLContextShareSet; - public class WindowsExternalWGLContext extends WindowsWGLContext { - private GLContext lastContext; private WindowsExternalWGLContext(Drawable drawable, long ctx, WindowsWGLGraphicsConfiguration cfg) { super(drawable, null); @@ -66,7 +63,9 @@ public class WindowsExternalWGLContext extends WindowsWGLContext { System.err.println(getThreadName() + ": Created external OpenGL context " + toHexString(ctx) + " for " + this); } GLContextShareSet.contextCreated(this); - setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_COMPAT); // use GL_VERSION + if( !setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, false /* withinGLVersionsMapping */) ) { // use GL_VERSION + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } getGLStateTracker().setEnabled(false); // external context usage can't track state in Java } @@ -103,32 +102,18 @@ public class WindowsExternalWGLContext extends WindowsWGLContext { System.err.println("WindowsExternalWGLContext valid hdc/pfd, retrieved cfg: " + cfg); } } - return new WindowsExternalWGLContext(new Drawable(factory, new WrappedSurface(cfg, hdc)), ctx, cfg); - } - - public int makeCurrent() throws GLException { - // Save last context if necessary to allow external GLContexts to - // talk to other GLContexts created by this library - GLContext cur = getCurrent(); - if (cur != null && cur != this) { - lastContext = cur; - setCurrent(null); - } - return super.makeCurrent(); - } - - public void release() throws GLException { - super.release(); - setCurrent(lastContext); - lastContext = null; + return new WindowsExternalWGLContext(new Drawable(factory, new WrappedSurface(cfg, hdc, 64, 64, true)), ctx, cfg); } + @Override protected void makeCurrentImpl() throws GLException { } + @Override protected void releaseImpl() throws GLException { } + @Override protected void destroyImpl() throws GLException { } @@ -138,14 +123,17 @@ public class WindowsExternalWGLContext extends WindowsWGLContext { super(factory, comp, true); } + @Override public GLContext createContext(GLContext shareWith) { throw new GLException("Should not call this"); } + @Override public int getWidth() { throw new GLException("Should not call this"); } + @Override public int getHeight() { throw new GLException("Should not call this"); } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLDrawable.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLDrawable.java index ad2e91bed..f8c237c9e 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsExternalWGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -49,9 +49,10 @@ import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import com.jogamp.nativewindow.WrappedSurface; - +import jogamp.nativewindow.WrappedSurface; import jogamp.nativewindow.windows.GDI; +import jogamp.nativewindow.windows.GDIUtil; + public class WindowsExternalWGLDrawable extends WindowsWGLDrawable { @@ -69,25 +70,28 @@ public class WindowsExternalWGLDrawable extends WindowsWGLDrawable { throw new GLException("Error: attempted to make an external GLContext without a valid pixelformat, werr " + GDI.GetLastError()); } - AbstractGraphicsScreen aScreen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_WINDOWS); - WindowsWGLGraphicsConfiguration cfg = WindowsWGLGraphicsConfiguration.createFromExternal(factory, hdc, pfdID, glp, aScreen, true); - return new WindowsExternalWGLDrawable(factory, new WrappedSurface(cfg, hdc)); + final AbstractGraphicsScreen aScreen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_WINDOWS); + final WindowsWGLGraphicsConfiguration cfg = WindowsWGLGraphicsConfiguration.createFromExternal(factory, hdc, pfdID, glp, aScreen, true); + return new WindowsExternalWGLDrawable(factory, new WrappedSurface(cfg, hdc, 64, 64, true)); } + @Override public GLContext createContext(GLContext shareWith) { return new WindowsWGLContext(this, shareWith); } - + public void setSize(int newWidth, int newHeight) { throw new GLException("Should not call this"); } + @Override public int getWidth() { throw new GLException("Should not call this"); - } + } + @Override public int getHeight() { throw new GLException("Should not call this"); - } + } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLContext.java deleted file mode 100644 index a06ab8f0e..000000000 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLContext.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.windows.wgl; - -import java.util.*; - -import javax.media.nativewindow.*; -import javax.media.opengl.*; -import jogamp.opengl.*; - -public class WindowsOnscreenWGLContext extends WindowsWGLContext { - public WindowsOnscreenWGLContext(WindowsOnscreenWGLDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } -} diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLDrawable.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLDrawable.java index 6ad330ccc..61fb787c6 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsOnscreenWGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,27 +29,29 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.windows.wgl; -import javax.media.nativewindow.*; -import javax.media.opengl.*; +import javax.media.nativewindow.NativeSurface; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; public class WindowsOnscreenWGLDrawable extends WindowsWGLDrawable { protected WindowsOnscreenWGLDrawable(GLDrawableFactory factory, NativeSurface component) { super(factory, component, false); } + @Override public GLContext createContext(GLContext shareWith) { - return new WindowsOnscreenWGLContext(this, shareWith); + return new WindowsWGLContext(this, shareWith); } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLContext.java deleted file mode 100644 index 0f610495d..000000000 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLContext.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.windows.wgl; - -import javax.media.opengl.*; - -import jogamp.opengl.GLContextImpl; - -public class WindowsPbufferWGLContext extends WindowsWGLContext { - // State for render-to-texture and render-to-texture-rectangle support - private boolean rtt; // render-to-texture? - private boolean hasRTT; // render-to-texture extension available? - private boolean rect; // render-to-texture-rectangle? - private int textureTarget; // e.g. GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_NV - private int texture; // actual texture object - - protected WindowsPbufferWGLContext(WindowsPbufferWGLDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } - - public void bindPbufferToTexture() { - if (!rtt) { - throw new GLException("Shouldn't try to bind a pbuffer to a texture if render-to-texture hasn't been " + - "specified in its GLCapabilities"); - } - GL gl = getGL(); - WGLExt wglExt = getWGLExt(); - gl.glBindTexture(textureTarget, texture); - if (rtt && hasRTT) { - if (!wglExt.wglBindTexImageARB(((WindowsPbufferWGLDrawable)drawable).getPbufferHandle(), WGLExt.WGL_FRONT_LEFT_ARB)) { - throw new GLException("Binding of pbuffer to texture failed: " + wglGetLastError()); - } - } - // FIXME: comment is wrong now - // Note that if the render-to-texture extension is not supported, - // we perform a glCopyTexImage2D in swapBuffers(). - } - - public void releasePbufferFromTexture() { - if (!rtt) { - throw new GLException("Shouldn't try to bind a pbuffer to a texture if render-to-texture hasn't been " + - "specified in its GLCapabilities"); - } - if (rtt && hasRTT) { - WGLExt wglExt = getWGLExt(); - if (!wglExt.wglReleaseTexImageARB(((WindowsPbufferWGLDrawable)drawable).getPbufferHandle(), WGLExt.WGL_FRONT_LEFT_ARB)) { - throw new GLException("Releasing of pbuffer from texture failed: " + wglGetLastError()); - } - } - } - - protected boolean createImpl(GLContextImpl shareWith) { - boolean res = super.createImpl(shareWith); - if(res) { - GLCapabilitiesImmutable capabilities = drawable.getChosenGLCapabilities(); - - // Initialize render-to-texture support if requested - GL gl = getGL(); - rtt = capabilities.getPbufferRenderToTexture(); - rect = gl.isGL2GL3() && capabilities.getPbufferRenderToTextureRectangle(); - - if (rtt) { - if (DEBUG) { - System.err.println("Initializing render-to-texture support"); - } - - if (!gl.isExtensionAvailable("WGL_ARB_render_texture")) { - System.err.println("WindowsPbufferWGLContext: WARNING: WGL_ARB_render_texture extension not " + - "supported; implementing render_to_texture support using slow texture readback"); - } else { - hasRTT = true; - - if (rect && !gl.isExtensionAvailable("GL_NV_texture_rectangle")) { - System.err.println("WindowsPbufferWGLContext: WARNING: GL_NV_texture_rectangle extension not " + - "supported; skipping requested render_to_texture_rectangle support for pbuffer"); - rect = false; - } - if (rect) { - if (DEBUG) { - System.err.println(" Using render-to-texture-rectangle"); - } - textureTarget = GL2.GL_TEXTURE_RECTANGLE_ARB; - } else { - if (DEBUG) { - System.err.println(" Using vanilla render-to-texture"); - } - textureTarget = GL.GL_TEXTURE_2D; - } - int[] tmp = new int[1]; - gl.glGenTextures(1, tmp, 0); - texture = tmp[0]; - gl.glBindTexture(textureTarget, texture); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); - gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); - gl.glCopyTexImage2D(textureTarget, 0, GL.GL_RGB, 0, 0, drawable.getWidth(), drawable.getHeight(), 0); - } - } - } - return res; - } - - public int getFloatingPointMode() { - return ((WindowsPbufferWGLDrawable)drawable).getFloatingPointMode(); - } - - private static String wglGetLastError() { - return WindowsWGLDrawableFactory.wglGetLastError(); - } -} diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLDrawable.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLDrawable.java index 0988f3eca..2e60c682b 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsPbufferWGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,48 +29,47 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.windows.wgl; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.SurfaceChangeable; -import javax.media.opengl.GL; +import javax.media.nativewindow.MutableSurface; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; -// import javax.media.opengl.GLPbuffer; import javax.media.opengl.GLProfile; +import com.jogamp.common.nio.Buffers; + import jogamp.nativewindow.windows.GDI; import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.windows.wgl.WindowsWGLDrawableFactory.SharedResource; -import javax.media.opengl.GLCapabilitiesImmutable; - public class WindowsPbufferWGLDrawable extends WindowsWGLDrawable { private WGLExt cachedWGLExt; // cached WGLExt instance from parent GLCanvas, // needed to destroy pbuffer private long buffer; // pbuffer handle - private int floatMode; - protected WindowsPbufferWGLDrawable(GLDrawableFactory factory, NativeSurface target) { super(factory, target, false); } - protected void destroyImpl() { - setRealized(false); - } - + @Override protected void setRealizedImpl() { if(realized) { createPbuffer(); @@ -79,8 +78,9 @@ public class WindowsPbufferWGLDrawable extends WindowsWGLDrawable { } } + @Override public GLContext createContext(GLContext shareWith) { - return new WindowsPbufferWGLContext(this, shareWith); + return new WindowsWGLContext(this, shareWith); } protected void destroyPbuffer() { @@ -95,7 +95,7 @@ public class WindowsPbufferWGLDrawable extends WindowsWGLDrawable { if (wglExt.wglReleasePbufferDCARB(buffer, ns.getSurfaceHandle()) == 0) { throw new GLException("Error releasing pbuffer device context: error code " + GDI.GetLastError()); } - ((SurfaceChangeable)ns).setSurfaceHandle(0); + ((MutableSurface)ns).setSurfaceHandle(0); } if (!wglExt.wglDestroyPbufferARB(buffer)) { throw new GLException("Error destroying pbuffer: error code " + GDI.GetLastError()); @@ -111,139 +111,106 @@ public class WindowsPbufferWGLDrawable extends WindowsWGLDrawable { return buffer; } - public int getFloatingPointMode() { - return floatMode; - } - private void createPbuffer() { WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration) getNativeSurface().getGraphicsConfiguration(); - SharedResource sharedResource = ((WindowsWGLDrawableFactory)factory).getOrCreateSharedResource(config.getScreen().getDevice()); + SharedResource sharedResource = ((WindowsWGLDrawableFactory)factory).getOrCreateSharedResourceImpl(config.getScreen().getDevice()); NativeSurface sharedSurface = sharedResource.getDrawable().getNativeSurface(); if (NativeSurface.LOCK_SURFACE_NOT_READY >= sharedSurface.lockSurface()) { throw new NativeWindowException("Could not lock (sharedSurface): "+this); } try { long sharedHdc = sharedSurface.getSurfaceHandle(); - WGLExt wglExt = sharedResource.getContext().getWGLExt(); - + WGLExt wglExt = ((WindowsWGLContext)sharedResource.getContext()).getWGLExt(); + if (DEBUG) { - System.out.println("Pbuffer config: " + config); + System.out.println(getThreadName()+": Pbuffer config: " + config); } - - int[] iattributes = new int [2*WindowsWGLGraphicsConfiguration.MAX_ATTRIBS]; - float[] fattributes = new float[1]; + + final int winattrPbuffer = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(false /* onscreen */, false /* fbo */, true /* pbuffer */, false /* bitmap */); + + final IntBuffer iattributes = Buffers.newDirectIntBuffer(2*WindowsWGLGraphicsConfiguration.MAX_ATTRIBS); + final FloatBuffer fattributes = Buffers.newDirectFloatBuffer(1); int[] floatModeTmp = new int[1]; int niattribs = 0; - int width, height; - - GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); - GLProfile glProfile = chosenCaps.getGLProfile(); - + + final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); + final GLProfile glProfile = chosenCaps.getGLProfile(); + final AbstractGraphicsDevice device = config.getScreen().getDevice(); + if (DEBUG) { - System.out.println("Pbuffer parentHdc = " + toHexString(sharedHdc)); - System.out.println("Pbuffer chosenCaps: " + chosenCaps); + System.out.println(getThreadName()+": Pbuffer parentHdc = " + toHexString(sharedHdc)); + System.out.println(getThreadName()+": Pbuffer chosenCaps: " + chosenCaps); } - + if(!WindowsWGLGraphicsConfiguration.GLCapabilities2AttribList(chosenCaps, iattributes, sharedResource, -1, floatModeTmp)){ throw new GLException("Pbuffer-related extensions not supported"); } - - floatMode = floatModeTmp[0]; - boolean rtt = chosenCaps.getPbufferRenderToTexture(); - boolean rect = chosenCaps.getPbufferRenderToTextureRectangle(); - boolean useFloat = chosenCaps.getPbufferFloatingPointBuffers(); - // boolean ati = false; - - /** - if (useFloat) { - ati = (floatMode == GLPbuffer.ATI_FLOAT); - } */ - - int[] pformats = new int[WindowsWGLGraphicsConfiguration.MAX_PFORMATS]; - int nformats; - int[] nformatsTmp = new int[1]; + + final IntBuffer pformats = Buffers.newDirectIntBuffer(WindowsWGLGraphicsConfiguration.MAX_PFORMATS); + final IntBuffer nformatsTmp = Buffers.newDirectIntBuffer(1); if (!wglExt.wglChoosePixelFormatARB(sharedHdc, - iattributes, 0, - fattributes, 0, - WindowsWGLGraphicsConfiguration.MAX_PFORMATS, - pformats, 0, - nformatsTmp, 0)) { + iattributes, fattributes, WindowsWGLGraphicsConfiguration.MAX_PFORMATS, + pformats, nformatsTmp)) { throw new GLException("pbuffer creation error: wglChoosePixelFormat() failed"); } - nformats = nformatsTmp[0]; + final int nformats = Math.min(nformatsTmp.get(0), WindowsWGLGraphicsConfiguration.MAX_PFORMATS); if (nformats <= 0) { throw new GLException("pbuffer creation error: Couldn't find a suitable pixel format"); } - + if (DEBUG) { System.err.println("" + nformats + " suitable pixel formats found"); for (int i = 0; i < nformats; i++) { - WGLGLCapabilities dbgCaps = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities(sharedResource, sharedHdc, pformats[i], glProfile, false, true); - System.err.println("pixel format " + pformats[i] + " (index " + i + "): " + dbgCaps); + WGLGLCapabilities dbgCaps = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities(sharedResource, device, glProfile, + sharedHdc, pformats.get(i), winattrPbuffer); + System.err.println("pixel format " + pformats.get(i) + " (index " + i + "): " + dbgCaps); } } - + int pfdid = 0; long tmpBuffer = 0; { int whichFormat; // Loop is a workaround for bugs in NVidia's recent drivers for (whichFormat = 0; whichFormat < nformats; whichFormat++) { - int format = pformats[whichFormat]; - + int format = pformats.get(whichFormat); + // Create the p-buffer. niattribs = 0; - - if (rtt) { - iattributes[niattribs++] = WGLExt.WGL_TEXTURE_FORMAT_ARB; - if (useFloat) { - iattributes[niattribs++] = WGLExt.WGL_TEXTURE_FLOAT_RGB_NV; - } else { - iattributes[niattribs++] = WGLExt.WGL_TEXTURE_RGBA_ARB; - } - - iattributes[niattribs++] = WGLExt.WGL_TEXTURE_TARGET_ARB; - iattributes[niattribs++] = rect ? WGLExt.WGL_TEXTURE_RECTANGLE_NV : WGLExt.WGL_TEXTURE_2D_ARB; - - iattributes[niattribs++] = WGLExt.WGL_MIPMAP_TEXTURE_ARB; - iattributes[niattribs++] = GL.GL_FALSE; - - iattributes[niattribs++] = WGLExt.WGL_PBUFFER_LARGEST_ARB; - iattributes[niattribs++] = GL.GL_FALSE; - } - - iattributes[niattribs++] = 0; - - tmpBuffer = wglExt.wglCreatePbufferARB(sharedHdc, format, getWidth(), getHeight(), iattributes, 0); + + iattributes.put(niattribs++, 0); + + tmpBuffer = wglExt.wglCreatePbufferARB(sharedHdc, format, getWidth(), getHeight(), iattributes); if (tmpBuffer != 0) { // Done break; } } - + if (0 == tmpBuffer) { throw new GLException("pbuffer creation error: wglCreatePbuffer() failed: tried " + nformats + " pixel formats, last error was: " + wglGetLastError()); } - pfdid = pformats[whichFormat]; + pfdid = pformats.get(whichFormat); } - + // Get the device context. long tmpHdc = wglExt.wglGetPbufferDCARB(tmpBuffer); if (tmpHdc == 0) { throw new GLException("pbuffer creation error: wglGetPbufferDC() failed"); } - + NativeSurface ns = getNativeSurface(); // Set up instance variables buffer = tmpBuffer; - ((SurfaceChangeable)ns).setSurfaceHandle(tmpHdc); - cachedWGLExt = wglExt; - + ((MutableSurface)ns).setSurfaceHandle(tmpHdc); + cachedWGLExt = wglExt; + // Re-query chosen pixel format { - WGLGLCapabilities newCaps = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities(sharedResource, sharedHdc, pfdid, glProfile, false, true); + WGLGLCapabilities newCaps = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities(sharedResource, device, glProfile, + sharedHdc, pfdid, winattrPbuffer); if(null == newCaps) { throw new GLException("pbuffer creation error: unable to re-query chosen PFD ID: " + pfdid + ", hdc " + GLDrawableImpl.toHexString(tmpHdc)); } @@ -252,14 +219,6 @@ public class WindowsPbufferWGLDrawable extends WindowsWGLDrawable { } config.setCapsPFD(newCaps); } - - // Determine the actual width and height we were able to create. - int[] tmp = new int[1]; - wglExt.wglQueryPbufferARB( buffer, WGLExt.WGL_PBUFFER_WIDTH_ARB, tmp, 0 ); - width = tmp[0]; - wglExt.wglQueryPbufferARB( buffer, WGLExt.WGL_PBUFFER_HEIGHT_ARB, tmp, 0 ); - height = tmp[0]; - ((SurfaceChangeable)ns).surfaceSizeChanged(width, height); } finally { sharedSurface.unlockSurface(); } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java index 217d88f3c..b214252c1 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,6 +41,7 @@ package jogamp.opengl.windows.wgl; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.HashMap; import java.util.Map; @@ -51,8 +52,12 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLCapabilitiesImmutable; +import com.jogamp.common.nio.Buffers; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; + import jogamp.nativewindow.windows.GDI; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableImpl; @@ -77,8 +82,8 @@ public class WindowsWGLContext extends GLContextImpl { functionNameMap.put("glFreeMemoryNV", "wglFreeMemoryNV"); extensionNameMap = new HashMap<String, String>(); - extensionNameMap.put("GL_ARB_pbuffer", "WGL_ARB_pbuffer"); - extensionNameMap.put("GL_ARB_pixel_format", "WGL_ARB_pixel_format"); + extensionNameMap.put(GLExtensions.ARB_pbuffer, WindowsWGLDrawableFactory.WGL_ARB_pbuffer); + extensionNameMap.put(GLExtensions.ARB_pixel_format, WindowsWGLDrawableFactory.WGL_ARB_pixel_format); } // FIXME: figure out how to hook back in the Java 2D / JOGL bridge @@ -88,7 +93,7 @@ public class WindowsWGLContext extends GLContextImpl { } @Override - protected void resetStates() { + protected void resetStates(boolean isInit) { wglGetExtensionsStringEXTInitialized=false; wglGetExtensionsStringEXTAvailable=false; wglGLReadDrawableAvailableSet=false; @@ -96,10 +101,11 @@ public class WindowsWGLContext extends GLContextImpl { // no inner state _wglExt=null; wglExtProcAddressTable=null; hasSwapIntervalSGI = 0; - hasSwapGroupNV = 0; - super.resetStates(); + hasSwapGroupNV = 0; + super.resetStates(isInit); } - + + @Override public Object getPlatformGLExtensions() { return getWGLExt(); } @@ -114,6 +120,7 @@ public class WindowsWGLContext extends GLContextImpl { return _wglExt; } + @Override public final boolean isGLReadDrawableAvailable() { if(!wglGLReadDrawableAvailableSet && null != getWGLExtProcAddressTable()) { WindowsWGLDrawableFactory factory = (WindowsWGLDrawableFactory)drawable.getFactoryImpl(); @@ -159,6 +166,7 @@ public class WindowsWGLContext extends GLContextImpl { return ok; } + @Override public final ProcAddressTable getPlatformExtProcAddressTable() { return getWGLExtProcAddressTable(); } @@ -167,15 +175,19 @@ public class WindowsWGLContext extends GLContextImpl { return wglExtProcAddressTable; } + @Override protected Map<String, String> getFunctionNameMap() { return functionNameMap; } + @Override protected Map<String, String> getExtensionNameMap() { return extensionNameMap; } + @Override protected void destroyContextARBImpl(long context) { WGL.wglMakeCurrent(0, 0); WGL.wglDeleteContext(context); } + @Override protected long createContextARBImpl(long share, boolean direct, int ctp, int major, int minor) { if( null == getWGLExtProcAddressTable()) { updateGLXProcAddressTable(); @@ -207,14 +219,13 @@ public class WindowsWGLContext extends GLContextImpl { }; if ( major > 3 || major == 3 && minor >= 2 ) { - // FIXME: Verify with a None drawable binding (default framebuffer) attribs[idx_profile+0] = WGLExt.WGL_CONTEXT_PROFILE_MASK_ARB; if( ctBwdCompat ) { attribs[idx_profile+1] = WGLExt.WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } else { attribs[idx_profile+1] = WGLExt.WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - } - } + } + } if ( major >= 3 ) { if( !ctBwdCompat && ctFwdCompat ) { @@ -226,7 +237,8 @@ public class WindowsWGLContext extends GLContextImpl { } try { - ctx = _wglExt.wglCreateContextAttribsARB(drawable.getHandle(), share, attribs, 0); + final IntBuffer attribsNIO = Buffers.newDirectIntBuffer(attribs); + ctx = _wglExt.wglCreateContextAttribsARB(drawable.getHandle(), share, attribsNIO); } catch (RuntimeException re) { if(DEBUG) { Throwable t = new Throwable("Info: WindowWGLContext.createContextARBImpl wglCreateContextAttribsARB failed with "+getGLVersion(major, minor, ctp, "@creation"), re); @@ -255,74 +267,92 @@ public class WindowsWGLContext extends GLContextImpl { * Creates and initializes an appropriate OpenGL context. Should only be * called by {@link #makeCurrentImpl()}. */ - protected boolean createImpl(GLContextImpl shareWith) { - AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); - AbstractGraphicsDevice device = config.getScreen().getDevice(); - WindowsWGLDrawableFactory factory = (WindowsWGLDrawableFactory)drawable.getFactoryImpl(); - WindowsWGLContext sharedContext = (WindowsWGLContext) factory.getOrCreateSharedContextImpl(device); - GLCapabilitiesImmutable glCaps = drawable.getChosenGLCapabilities(); + @Override + protected boolean createImpl(long shareWithHandle) { + final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); + final AbstractGraphicsDevice device = config.getScreen().getDevice(); + final WindowsWGLDrawableFactory factory = (WindowsWGLDrawableFactory)drawable.getFactoryImpl(); + final WindowsWGLContext sharedContext = (WindowsWGLContext) factory.getOrCreateSharedContext(device); + final GLCapabilitiesImmutable glCaps = drawable.getChosenGLCapabilities(); isGLReadDrawableAvailable(); // trigger setup wglGLReadDrawableAvailable - // Windows can set up sharing of display lists after creation time - long share = 0; - if (null != shareWith) { - share = shareWith.getHandle(); - if (share == 0) { - throw new GLException("GLContextShareSet returned an invalid OpenGL context"); - } + if (DEBUG) { + System.err.println(getThreadName() + ": createImpl: START "+glCaps+", share "+toHexString(shareWithHandle)); } boolean createContextARBTried = false; - // utilize the shared context's GLXExt in case it was using the ARB method and it already exists - if( null!=sharedContext && sharedContext.isCreatedWithARBMethod() ) { - contextHandle = createContextARB(share, true); + // utilize the shared context's GLXExt in case it was using the ARB method and it already exists ; exclude BITMAP + if( null != sharedContext && sharedContext.isCreatedWithARBMethod() && !glCaps.isBitmap() ) { + if ( sharedContext.getRendererQuirks().exist( GLRendererQuirks.NeedCurrCtx4ARBCreateContext ) ) { + if(GLContext.CONTEXT_NOT_CURRENT == sharedContext.makeCurrent()) { + throw new GLException("Could not make Shared Context current: "+sharedContext); + } + contextHandle = createContextARB(shareWithHandle, true); + sharedContext.release(); + if (!wglMakeContextCurrent(drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + throw new GLException("Cannot make previous verified context current: 0x" + toHexString(contextHandle) + ", werr: " + GDI.GetLastError()); + } + } else { + contextHandle = createContextARB(shareWithHandle, true); + } createContextARBTried = true; - if (DEBUG && 0!=contextHandle) { - System.err.println(getThreadName() + ": createImpl: OK (ARB, using sharedContext) share "+share); + if ( DEBUG && 0 != contextHandle ) { + System.err.println(getThreadName() + ": createImpl: OK (ARB, using sharedContext) share "+toHexString(shareWithHandle)); } } - long temp_ctx = 0; - if(0==contextHandle) { + final long temp_ctx; + if( 0 == contextHandle ) { // To use WGL_ARB_create_context, we have to make a temp context current, // so we are able to use GetProcAddress temp_ctx = WGL.wglCreateContext(drawable.getHandle()); - if (temp_ctx == 0) { + if ( 0 == temp_ctx ) { throw new GLException("Unable to create temp OpenGL context for device context " + toHexString(drawable.getHandle())); } - if (!WGL.wglMakeCurrent(drawable.getHandle(), temp_ctx)) { + if ( !WGL.wglMakeCurrent(drawable.getHandle(), temp_ctx) ) { throw new GLException("Error making temp context current: 0x" + toHexString(temp_ctx) + ", werr: "+GDI.GetLastError()); } - setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT); // use GL_VERSION + if( !setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, false /* withinGLVersionsMapping */) ) { // use GL_VERSION + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } WGL.wglMakeCurrent(0, 0); // release temp context - if( !createContextARBTried) { + if( !createContextARBTried ) { // is*Available calls are valid since setGLFunctionAvailability(..) was called - final boolean isProcCreateContextAttribsARBAvailable = isFunctionAvailable("wglCreateContextAttribsARB"); - final boolean isExtARBCreateContextAvailable = isExtensionAvailable("WGL_ARB_create_context"); + final boolean isProcCreateContextAttribsARBAvailable; + final boolean isExtARBCreateContextAvailable; + if( !glCaps.isBitmap() ) { // exclude ARB if BITMAP + isProcCreateContextAttribsARBAvailable = isFunctionAvailable("wglCreateContextAttribsARB"); + isExtARBCreateContextAvailable = isExtensionAvailable("WGL_ARB_create_context"); + } else { + isProcCreateContextAttribsARBAvailable = false; + isExtARBCreateContextAvailable = false; + } if ( isProcCreateContextAttribsARBAvailable && isExtARBCreateContextAvailable ) { // initial ARB context creation - contextHandle = createContextARB(share, true); + contextHandle = createContextARB(shareWithHandle, true); createContextARBTried=true; if (DEBUG) { - if(0!=contextHandle) { - System.err.println(getThreadName() + ": createContextImpl: OK (ARB, initial) share "+share); + if( 0 != contextHandle ) { + System.err.println(getThreadName() + ": createContextImpl: OK (ARB, initial) share "+toHexString(shareWithHandle)); } else { - System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - creation failed - share "+share); + System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - creation failed - share "+toHexString(shareWithHandle)); } } } else if (DEBUG) { - System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - extension not available - share "+share+ - ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable); + System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - extension not available - share "+toHexString(shareWithHandle)+ + ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable); } } + } else { + temp_ctx = 0; } - - if(0!=contextHandle) { - share = 0; // mark as shared thx to the ARB create method - if(0!=temp_ctx) { + + if( 0 != contextHandle ) { + shareWithHandle = 0; // mark as shared thx to the ARB create method + if( 0 != temp_ctx ) { WGL.wglMakeCurrent(0, 0); WGL.wglDeleteContext(temp_ctx); if (!wglMakeContextCurrent(drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { @@ -330,10 +360,10 @@ public class WindowsWGLContext extends GLContextImpl { } } } else { - if(glCaps.getGLProfile().isGL3()) { + if( glCaps.getGLProfile().isGL3() ) { WGL.wglMakeCurrent(0, 0); WGL.wglDeleteContext(temp_ctx); - throw new GLException("WindowsWGLContext.createContext ctx !ARB, context > GL2 requested "+getGLVersion()); + throw new GLException(getThreadName()+": WindowsWGLContex.createContextImpl ctx !ARB, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glCaps.getGLProfile()+", current: "+getGLVersion()); } if(DEBUG) { System.err.println("WindowsWGLContext.createContext failed, fall back to !ARB context "+getGLVersion()); @@ -341,41 +371,49 @@ public class WindowsWGLContext extends GLContextImpl { // continue with temp context for GL < 3.0 contextHandle = temp_ctx; - if (!wglMakeContextCurrent(drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + if ( !wglMakeContextCurrent(drawable.getHandle(), drawableRead.getHandle(), contextHandle) ) { WGL.wglMakeCurrent(0, 0); WGL.wglDeleteContext(contextHandle); throw new GLException("Error making old context current: 0x" + toHexString(contextHandle) + ", werr: " + GDI.GetLastError()); } - if(0!=share) { - // Only utilize the classic GDI 'wglShareLists' shared context method + if( 0 != shareWithHandle ) { + // Windows can set up sharing of display lists after creation time if using GDI + // Only utilize the classic GDI 'wglShareLists' shared context method // for traditional non ARB context. - if (!WGL.wglShareLists(share, contextHandle)) { - throw new GLException("wglShareLists(" + toHexString(share) + + if ( !WGL.wglShareLists(shareWithHandle, contextHandle) ) { + throw new GLException("wglShareLists(" + toHexString(shareWithHandle) + ", " + toHexString(contextHandle) + ") failed: werr " + GDI.GetLastError()); } } if (DEBUG) { - System.err.println(getThreadName() + ": createImpl: OK (old) share "+share); + System.err.println(getThreadName() + ": createImpl: OK (old) share "+toHexString(shareWithHandle)); } } return true; } - + + @Override protected void makeCurrentImpl() throws GLException { if (WGL.wglGetCurrentContext() != contextHandle) { if (!wglMakeContextCurrent(drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { - throw new GLException("Error making context current: 0x" + toHexString(contextHandle) + ", werr: " + GDI.GetLastError() + ", " + this); + throw new GLException("Error making context " + toHexString(contextHandle) + + " current on Thread " + getThreadName() + + ", drawableWrite " + toHexString(drawable.getHandle()) + + ", drawableRead "+ toHexString(drawableRead.getHandle()) + + ", werr: " + GDI.GetLastError() + ", " + this); } } } + @Override protected void releaseImpl() throws GLException { if (!wglMakeContextCurrent(0, 0, 0)) { throw new GLException("Error freeing OpenGL context, werr: " + GDI.GetLastError()); } } + @Override protected void destroyImpl() throws GLException { WGL.wglMakeCurrent(0, 0); if (!WGL.wglDeleteContext(contextHandle)) { @@ -383,12 +421,14 @@ public class WindowsWGLContext extends GLContextImpl { } } + @Override protected void copyImpl(GLContext source, int mask) throws GLException { if (!WGL.wglCopyContext(source.getHandle(), getHandle(), mask)) { throw new GLException("wglCopyContext failed"); } } + @Override protected final void updateGLXProcAddressTable() { final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); @@ -421,10 +461,11 @@ public class WindowsWGLContext extends GLContextImpl { } } } - + + @Override protected final StringBuilder getPlatformExtensionsStringImpl() { StringBuilder sb = new StringBuilder(); - + if (!wglGetExtensionsStringEXTInitialized) { wglGetExtensionsStringEXTAvailable = (WGL.wglGetProcAddress("wglGetExtensionsStringEXT") != 0); wglGetExtensionsStringEXTInitialized = true; @@ -434,7 +475,7 @@ public class WindowsWGLContext extends GLContextImpl { } return sb; } - + @Override protected boolean setSwapIntervalImpl(int interval) { WGLExt wglExt = getWGLExt(); @@ -450,7 +491,7 @@ public class WindowsWGLContext extends GLContextImpl { } return false; } - + private final int initSwapGroupImpl(WGLExt wglExt) { if(0==hasSwapGroupNV) { try { @@ -462,7 +503,7 @@ public class WindowsWGLContext extends GLContextImpl { } return hasSwapGroupNV; } - + @Override protected final boolean queryMaxSwapGroupsImpl(int[] maxGroups, int maxGroups_offset, int[] maxBarriers, int maxBarriers_offset) { @@ -471,16 +512,19 @@ public class WindowsWGLContext extends GLContextImpl { if (initSwapGroupImpl(wglExt)>0) { final NativeSurface ns = drawable.getNativeSurface(); try { - if( wglExt.wglQueryMaxSwapGroupsNV(ns.getDisplayHandle(), - maxGroups, maxGroups_offset, - maxBarriers, maxBarriers_offset) ) { + final IntBuffer maxGroupsNIO = Buffers.newDirectIntBuffer(maxGroups.length - maxGroups_offset); + final IntBuffer maxBarriersNIO = Buffers.newDirectIntBuffer(maxBarriers.length - maxBarriers_offset); + + if( wglExt.wglQueryMaxSwapGroupsNV(ns.getDisplayHandle(), maxGroupsNIO, maxBarriersNIO) ) { + maxGroupsNIO.get(maxGroups, maxGroups_offset, maxGroupsNIO.remaining()); + maxBarriersNIO.get(maxGroups, maxGroups_offset, maxBarriersNIO.remaining()); res = true; } } catch (Throwable t) { hasSwapGroupNV=-1; } } return res; } - + @Override protected final boolean joinSwapGroupImpl(int group) { boolean res = false; @@ -495,7 +539,7 @@ public class WindowsWGLContext extends GLContextImpl { } return res; } - + @Override protected final boolean bindSwapBarrierImpl(int group, int barrier) { boolean res = false; @@ -507,31 +551,17 @@ public class WindowsWGLContext extends GLContextImpl { } } catch (Throwable t) { hasSwapGroupNV=-1; } } - return res; - } - - public ByteBuffer glAllocateMemoryNV(int arg0, float arg1, float arg2, float arg3) { - return getWGLExt().wglAllocateMemoryNV(arg0, arg1, arg2, arg3); - } - - public int getOffscreenContextPixelDataType() { - throw new GLException("Should not call this"); - } - - public int getOffscreenContextReadBuffer() { - throw new GLException("Should not call this"); - } - - public boolean offscreenImageNeedsVerticalFlip() { - throw new GLException("Should not call this"); + return res; } - public void bindPbufferToTexture() { - throw new GLException("Should not call this"); + @Override + public final ByteBuffer glAllocateMemoryNV(int size, float readFrequency, float writeFrequency, float priority) { + return getWGLExt().wglAllocateMemoryNV(size, readFrequency, writeFrequency, priority); } - public void releasePbufferFromTexture() { - throw new GLException("Should not call this"); + @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 579b11940..66071cbe1 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -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; @@ -60,43 +66,45 @@ public abstract class WindowsWGLDrawable extends GLDrawableImpl { super(factory, comp, realized); } + @Override protected void setRealizedImpl() { - if(!realized) { - return; // nothing todo .. - } - - NativeSurface ns = getNativeSurface(); - WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration)ns.getGraphicsConfiguration(); - config.updateGraphicsConfiguration(getFactory(), ns, null); - if (DEBUG) { - System.err.println("WindowsWGLDrawable.setRealized(true): "+config); + if(realized) { + NativeSurface ns = getNativeSurface(); + WindowsWGLGraphicsConfiguration config = (WindowsWGLGraphicsConfiguration)ns.getGraphicsConfiguration(); + config.updateGraphicsConfiguration(getFactory(), ns, null); + if (DEBUG) { + System.err.println(getThreadName()+": WindowsWGLDrawable.setRealized(true): "+config); + } } } - protected final void swapBuffersImpl() { - // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() - final long t0; - if (PROFILING) { - t0 = System.currentTimeMillis(); - } else { - t0 = 0; - } + @Override + protected final void swapBuffersImpl(boolean doubleBuffered) { + if(doubleBuffered) { + final long t0; + if (PROFILING) { + t0 = System.currentTimeMillis(); + } else { + t0 = 0; + } - if (!WGLUtil.SwapBuffers(getHandle()) && (GDI.GetLastError() != GDI.ERROR_SUCCESS)) { - throw new GLException("Error swapping buffers"); - } + if (!WGLUtil.SwapBuffers(getHandle()) && (GDI.GetLastError() != GDI.ERROR_SUCCESS)) { + throw new GLException("Error swapping buffers"); + } - if (PROFILING) { - profilingSwapBuffersTime += System.currentTimeMillis() - t0; - if (++profilingSwapBuffersTicks == PROFILING_TICKS) { - System.err.println("SwapBuffers calls: " + profilingSwapBuffersTime + " ms / " + PROFILING_TICKS + " calls (" + - ((float) profilingSwapBuffersTime / (float) PROFILING_TICKS) + " ms/call)"); - profilingSwapBuffersTime = 0; - profilingSwapBuffersTicks = 0; - } + if (PROFILING) { + profilingSwapBuffersTime += System.currentTimeMillis() - t0; + if (++profilingSwapBuffersTicks == PROFILING_TICKS) { + System.err.println("SwapBuffers calls: " + profilingSwapBuffersTime + " ms / " + PROFILING_TICKS + " calls (" + + ((float) profilingSwapBuffersTime / (float) PROFILING_TICKS) + " ms/call)"); + profilingSwapBuffersTime = 0; + profilingSwapBuffersTicks = 0; + } + } } } + @Override public GLDynamicLookupHelper getGLDynamicLookupHelper() { return getFactoryImpl().getGLDynamicLookupHelper(0); } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java index 494bb307e..7fa8775cf 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,82 +42,90 @@ 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; +import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.NativeWindowFactory; -import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.ProxySurface; -import javax.media.opengl.GL; -import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import javax.media.opengl.GLProfile.ShutdownType; - -import com.jogamp.common.JogampRuntimeException; -import com.jogamp.common.nio.PointerBuffer; -import com.jogamp.common.os.Platform; -import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.common.util.VersionNumber; -import com.jogamp.nativewindow.WrappedSurface; -import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; +import jogamp.nativewindow.WrappedSurface; import jogamp.nativewindow.windows.GDI; -import jogamp.nativewindow.windows.GDIUtil; +import jogamp.nativewindow.windows.GDIDummyUpstreamSurfaceHook; import jogamp.nativewindow.windows.GDISurface; import jogamp.nativewindow.windows.RegisteredClassFactory; import jogamp.opengl.DesktopGLDynamicLookupHelper; +import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableFactoryImpl; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.GLDynamicLookupHelper; +import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.SharedResourceRunner; +import com.jogamp.common.nio.PointerBuffer; +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; + public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { private static DesktopGLDynamicLookupHelper windowsWGLDynamicLookupHelper = null; - + public WindowsWGLDrawableFactory() { 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>() { + @Override + 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); - } + } ); } } - + defaultDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); - + if(null!=windowsWGLDynamicLookupHelper) { // Register our GraphicsConfigurationFactory implementations // The act of constructing them causes them to be registered WindowsWGLGraphicsConfigurationFactory.registerFactory(); if(GLProfile.isAWTAvailable()) { try { - ReflectionUtil.callStaticMethod("jogamp.opengl.windows.wgl.awt.WindowsAWTWGLGraphicsConfigurationFactory", - "registerFactory", null, null, getClass().getClassLoader()); - } catch (JogampRuntimeException jre) { /* n/a .. */ } + ReflectionUtil.callStaticMethod("jogamp.opengl.windows.wgl.awt.WindowsAWTWGLGraphicsConfigurationFactory", + "registerFactory", null, null, getClass().getClassLoader()); + } catch (Exception jre) { /* n/a .. */ } } - + sharedMap = new HashMap<String, SharedResourceRunner.Resource>(); - + // Init shared resources off thread // Will be released via ShutdownHook sharedResourceRunner = new SharedResourceRunner(new SharedResourceImplementation()); @@ -125,7 +133,17 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } } - protected final void destroy(ShutdownType shutdownType) { + @Override + protected final boolean isComplete() { + return null != windowsWGLDynamicLookupHelper; + } + + + @Override + protected final void shutdownImpl() { + if( DEBUG ) { + System.err.println("WindowsWGLDrawableFactory.shutdown"); + } if(null != sharedResourceRunner) { sharedResourceRunner.stop(); sharedResourceRunner = null; @@ -137,15 +155,15 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { defaultDevice = null; /** * Pulling away the native library may cause havoc .. - * - if(ShutdownType.COMPLETE == shutdownType && null != windowsWGLDynamicLookupHelper) { - windowsWGLDynamicLookupHelper.destroy(); - windowsWGLDynamicLookupHelper = null; - } */ - + * + windowsWGLDynamicLookupHelper.destroy(); + */ + windowsWGLDynamicLookupHelper = null; + RegisteredClassFactory.shutdownSharedClasses(); } + @Override public GLDynamicLookupHelper getGLDynamicLookupHelper(int profile) { return windowsWGLDynamicLookupHelper; } @@ -155,16 +173,17 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { private HashMap<String /*connection*/, SharedResourceRunner.Resource> sharedMap; private long processAffinityChanges = 0; - private PointerBuffer procMask = PointerBuffer.allocateDirect(1); - private PointerBuffer sysMask = PointerBuffer.allocateDirect(1); + private final PointerBuffer procMask = PointerBuffer.allocateDirect(1); + private final PointerBuffer sysMask = PointerBuffer.allocateDirect(1); + @Override protected void enterThreadCriticalZone() { synchronized (sysMask) { if( 0 == processAffinityChanges) { long pid = GDI.GetCurrentProcess(); if ( GDI.GetProcessAffinityMask(pid, procMask, sysMask) ) { if(DEBUG) { - System.err.println("WindowsWGLDrawableFactory.enterThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + Thread.currentThread().getName()); + System.err.println("WindowsWGLDrawableFactory.enterThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + getThreadName()); // Thread.dumpStack(); } processAffinityChanges = pid; @@ -174,6 +193,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } } + @Override protected void leaveThreadCriticalZone() { synchronized (sysMask) { if( 0 != processAffinityChanges) { @@ -183,35 +203,25 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { " this PID 0x" + Long.toHexString(pid) ); } if(DEBUG) { - System.err.println("WindowsWGLDrawableFactory.leaveThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + Thread.currentThread().getName()); + System.err.println("WindowsWGLDrawableFactory.leaveThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + getThreadName()); } GDI.SetProcessAffinityMask(pid, sysMask.get(0)); } } } - /** - * http://msdn.microsoft.com/en-us/library/ms724832%28v=vs.85%29.aspx - * Windows XP 5.1 - */ - static final VersionNumber winXPVersionNumber = new VersionNumber ( 5, 1, 0); - static class SharedResource implements SharedResourceRunner.Resource { + private final boolean hasARBPixelFormat; + private final boolean hasARBMultisample; + private final boolean hasARBPBuffer; + private final boolean hasARBReadDrawable; private WindowsGraphicsDevice device; private AbstractGraphicsScreen screen; - private WindowsDummyWGLDrawable drawable; - private WindowsWGLContext context; - private boolean hasARBPixelFormat; - private boolean hasARBMultisample; - private boolean hasARBPBuffer; - private boolean hasARBReadDrawable; - private String vendor; - private boolean isVendorATI; - private boolean isVendorNVIDIA; - private boolean needsCurrenContext4ARBPFDQueries; - - SharedResource(WindowsGraphicsDevice dev, AbstractGraphicsScreen scrn, WindowsDummyWGLDrawable draw, WindowsWGLContext ctx, - boolean arbPixelFormat, boolean arbMultisample, boolean arbPBuffer, boolean arbReadDrawable, String glVendor) { + private GLDrawableImpl drawable; + private GLContextImpl context; + + SharedResource(WindowsGraphicsDevice dev, AbstractGraphicsScreen scrn, GLDrawableImpl draw, GLContextImpl ctx, + boolean arbPixelFormat, boolean arbMultisample, boolean arbPBuffer, boolean arbReadDrawable) { device = dev; screen = scrn; drawable = draw; @@ -220,90 +230,71 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { hasARBMultisample = arbMultisample; hasARBPBuffer = arbPBuffer; hasARBReadDrawable = arbReadDrawable; - vendor = glVendor; - if(null != vendor) { - isVendorNVIDIA = vendor.startsWith("NVIDIA") ; - isVendorATI = vendor.startsWith("ATI") ; - } - - if ( isVendorATI() ) { - final VersionNumber winVersion = Platform.getOSVersionNumber(); - final boolean isWinXPOrLess = winVersion.compareTo(winXPVersionNumber) <= 0; - if(DEBUG) { - System.err.println("needsCurrenContext4ARBPFDQueries: "+winVersion+" <= "+winXPVersionNumber+" = "+isWinXPOrLess+" - "+Platform.getOSVersion()); - } - needsCurrenContext4ARBPFDQueries = isWinXPOrLess; - } else { - if(DEBUG) { - System.err.println("needsCurrenContext4ARBPFDQueries: false"); - } - needsCurrenContext4ARBPFDQueries = false; - } } - + + @Override + public final boolean isValid() { + return null != context; + } + @Override final public AbstractGraphicsDevice getDevice() { return device; } + @Override final public AbstractGraphicsScreen getScreen() { return screen; } - final public WindowsWGLDrawable getDrawable() { return drawable; } - final public WindowsWGLContext getContext() { return context; } + @Override + final public GLDrawableImpl getDrawable() { return drawable; } + @Override + final public GLContextImpl getContext() { return context; } + @Override + public GLRendererQuirks getRendererQuirks() { + return null != context ? context.getRendererQuirks() : null; + } final boolean hasARBPixelFormat() { return hasARBPixelFormat; } final boolean hasARBMultisample() { return hasARBMultisample; } final boolean hasARBPBuffer() { return hasARBPBuffer; } final boolean hasReadDrawable() { return hasARBReadDrawable; } - - final String vendor() { return vendor; } - final boolean isVendorATI() { return isVendorATI; } - final boolean isVendorNVIDIA() { return isVendorNVIDIA; } - - /** - * Solves bug #480 - * - * TODO: Validate if bug is actually relates to the 'old' ATI Windows driver for old GPU's like X300 etc - * and unrelated to the actual Windows version ! - * - * @return true if GL_VENDOR is ATI _and_ platform is Windows version XP or less! - */ - final boolean needsCurrentContext4ARBPFDQueries() { return needsCurrenContext4ARBPFDQueries; } } class SharedResourceImplementation implements SharedResourceRunner.Implementation { + @Override public void clear() { - synchronized(sharedMap) { - sharedMap.clear(); - } + sharedMap.clear(); } + @Override public SharedResourceRunner.Resource mapPut(String connection, SharedResourceRunner.Resource resource) { - synchronized(sharedMap) { - return sharedMap.put(connection, resource); - } + return sharedMap.put(connection, resource); } + @Override public SharedResourceRunner.Resource mapGet(String connection) { - synchronized(sharedMap) { - return sharedMap.get(connection); - } + return sharedMap.get(connection); } + @Override public Collection<SharedResourceRunner.Resource> mapValues() { synchronized(sharedMap) { return sharedMap.values(); } } + @Override + public boolean isDeviceSupported(String connection) { + return true; + } + + @Override public SharedResourceRunner.Resource createSharedResource(String connection) { - WindowsGraphicsDevice sharedDevice = new WindowsGraphicsDevice(connection, AbstractGraphicsDevice.DEFAULT_UNIT); + final WindowsGraphicsDevice sharedDevice = new WindowsGraphicsDevice(connection, AbstractGraphicsDevice.DEFAULT_UNIT); sharedDevice.lock(); try { - AbstractGraphicsScreen absScreen = new DefaultGraphicsScreen(sharedDevice, 0); - GLProfile glp = GLProfile.get(sharedDevice, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false); + final AbstractGraphicsScreen absScreen = new DefaultGraphicsScreen(sharedDevice, 0); + final GLProfile glp = GLProfile.get(sharedDevice, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false); if (null == glp) { throw new GLException("Couldn't get default GLProfile for device: "+sharedDevice); } - final int f_dim = 64; - long hwnd = GDIUtil.CreateDummyWindow(0, 0, f_dim, f_dim); - WindowsDummyWGLDrawable sharedDrawable = WindowsDummyWGLDrawable.create(WindowsWGLDrawableFactory.this, glp, absScreen, hwnd, f_dim, f_dim, true); - if (null == sharedDrawable) { - throw new GLException("Couldn't create shared drawable for screen: "+absScreen+", "+glp); - } - WindowsWGLContext sharedContext = (WindowsWGLContext) sharedDrawable.createContext(null); + final GLCapabilitiesImmutable caps = new GLCapabilities(glp); + final GLDrawableImpl sharedDrawable = createOnscreenDrawableImpl(createDummySurfaceImpl(sharedDevice, false, caps, caps, null, 64, 64)); + sharedDrawable.setRealized(true); + + final GLContextImpl sharedContext = (GLContextImpl) sharedDrawable.createContext(null); if (null == sharedContext) { throw new GLException("Couldn't create shared context for drawable: "+sharedDrawable); } @@ -311,15 +302,13 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { boolean hasARBMultisample; boolean hasARBPBuffer; boolean hasARBReadDrawableAvailable; - String vendor; sharedContext.makeCurrent(); try { hasARBPixelFormat = sharedContext.isExtensionAvailable(WGL_ARB_pixel_format); hasARBMultisample = sharedContext.isExtensionAvailable(WGL_ARB_multisample); - hasARBPBuffer = sharedContext.isExtensionAvailable(GL_ARB_pbuffer); + hasARBPBuffer = sharedContext.isExtensionAvailable(GLExtensions.ARB_pbuffer); hasARBReadDrawableAvailable = sharedContext.isExtensionAvailable(WGL_ARB_make_current_read) && sharedContext.isFunctionAvailable(wglMakeContextCurrent); - vendor = sharedContext.getGL().glGetString(GL.GL_VENDOR); } finally { sharedContext.release(); } @@ -331,11 +320,10 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { System.err.println("multisample: " + hasARBMultisample); System.err.println("pbuffer: " + hasARBPBuffer); System.err.println("readDrawable: " + hasARBReadDrawableAvailable); - System.err.println("vendor: " + vendor); } - return new SharedResource(sharedDevice, absScreen, sharedDrawable, sharedContext, + return new SharedResource(sharedDevice, absScreen, sharedDrawable, sharedContext, hasARBPixelFormat, hasARBMultisample, - hasARBPBuffer, hasARBReadDrawableAvailable, vendor); + hasARBPBuffer, hasARBReadDrawableAvailable); } catch (Throwable t) { throw new GLException("WindowsWGLDrawableFactory - Could not initialize shared resources for "+connection, t); } finally { @@ -343,6 +331,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } } + @Override public void releaseSharedResource(SharedResourceRunner.Resource shared) { SharedResource sr = (SharedResource) shared; if (DEBUG) { @@ -359,7 +348,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } if (null != sr.drawable) { - sr.drawable.destroy(); + sr.drawable.setRealized(false); sr.drawable = null; } @@ -374,10 +363,12 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } } + @Override public final AbstractGraphicsDevice getDefaultDevice() { return defaultDevice; } + @Override public final boolean getIsDeviceCompatible(AbstractGraphicsDevice device) { if(null!=windowsWGLDynamicLookupHelper && device instanceof WindowsGraphicsDevice) { return true; @@ -385,64 +376,37 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { return false; } - final static String GL_ARB_pbuffer = "GL_ARB_pbuffer"; + final static String WGL_ARB_pbuffer = "WGL_ARB_pbuffer"; final static String WGL_ARB_pixel_format = "WGL_ARB_pixel_format"; final static String WGL_ARB_multisample = "WGL_ARB_multisample"; final static String WGL_NV_float_buffer = "WGL_NV_float_buffer"; final static String WGL_ARB_make_current_read = "WGL_ARB_make_current_read"; final static String wglMakeContextCurrent = "wglMakeContextCurrent"; + @Override protected final Thread getSharedResourceThread() { return sharedResourceRunner.start(); } - - protected final boolean createSharedResource(AbstractGraphicsDevice device) { - try { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); - if(null!=sr) { - return null != sr.getContext(); - } - } catch (GLException gle) { - if(DEBUG) { - System.err.println("Catched Exception while WindowsWGL Shared Resource initialization"); - gle.printStackTrace(); - } - } - return false; - } - - protected final GLContext getOrCreateSharedContextImpl(AbstractGraphicsDevice device) { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); - if(null!=sr) { - return sr.getContext(); - } - return null; - } - protected AbstractGraphicsDevice getOrCreateSharedDeviceImpl(AbstractGraphicsDevice device) { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); - if(null!=sr) { - return sr.getDevice(); - } - return null; + @Override + protected final SharedResource getOrCreateSharedResourceImpl(AbstractGraphicsDevice device) { + return (SharedResource) sharedResourceRunner.getOrCreateShared(device); } - protected WindowsWGLDrawable getOrCreateSharedDrawable(AbstractGraphicsDevice device) { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); + protected final WindowsWGLDrawable getOrCreateSharedDrawable(AbstractGraphicsDevice device) { + SharedResourceRunner.Resource sr = getOrCreateSharedResourceImpl(device); if(null!=sr) { return (WindowsWGLDrawable) sr.getDrawable(); } return null; } - SharedResource getOrCreateSharedResource(AbstractGraphicsDevice device) { - return (SharedResource) sharedResourceRunner.getOrCreateShared(device); - } - + @Override protected List<GLCapabilitiesImmutable> getAvailableCapabilitiesImpl(AbstractGraphicsDevice device) { return WindowsWGLGraphicsConfigurationFactory.getAvailableCapabilities(this, device); } + @Override protected final GLDrawableImpl createOnscreenDrawableImpl(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); @@ -450,6 +414,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { return new WindowsOnscreenWGLDrawable(this, target); } + @Override protected final GLDrawableImpl createOffscreenDrawableImpl(final NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); @@ -457,7 +422,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { AbstractGraphicsConfiguration config = target.getGraphicsConfiguration(); GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); if(!chosenCaps.isPBuffer()) { - return new WindowsBitmapWGLDrawable(this, target); + return WindowsBitmapWGLDrawable.create(this, target); } // PBuffer GLDrawable Creation @@ -468,7 +433,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { * Similar to ATI Bug https://bugzilla.mozilla.org/show_bug.cgi?id=486277, * we need to have a context current on the same Display to create a PBuffer. */ - final SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared(device); + final SharedResource sr = getOrCreateSharedResourceImpl(device); if(null!=sr) { GLContext lastContext = GLContext.getCurrent(); if (lastContext != null) { @@ -494,46 +459,77 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { * and -1 if undefined yet, ie no shared device exist at this point. */ public final int isReadDrawableAvailable(AbstractGraphicsDevice device) { - SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared((null!=device)?device:defaultDevice); + SharedResource sr = getOrCreateSharedResourceImpl( ( null != device ) ? device : defaultDevice ); if(null!=sr) { return sr.hasReadDrawable() ? 1 : 0 ; } return -1; // undefined } - public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { - SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared((null!=device)?device:defaultDevice); + @Override + public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { + SharedResource sr = getOrCreateSharedResourceImpl( ( null != device ) ? device : defaultDevice ); if(null!=sr) { return sr.hasARBPBuffer(); } return false; } - protected final NativeSurface createOffscreenSurfaceImpl(AbstractGraphicsDevice device, GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, int width, int height) { - AbstractGraphicsScreen screen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_WINDOWS); - WrappedSurface ns = new WrappedSurface(WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( - capsChosen, capsRequested, chooser, screen) ); - ns.surfaceSizeChanged(width, height); - return ns; + @Override + protected final ProxySurface createMutableSurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstreamHook) { + final WindowsGraphicsDevice device; + if(createNewDevice || !(deviceReq instanceof WindowsGraphicsDevice)) { + device = new WindowsGraphicsDevice(deviceReq.getConnection(), deviceReq.getUnitID()); + } else { + device = (WindowsGraphicsDevice)deviceReq; + } + final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); + final WindowsWGLGraphicsConfiguration config = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen); + if(null == config) { + throw new GLException("Choosing GraphicsConfiguration failed w/ "+capsChosen+" on "+screen); + } + return new WrappedSurface(config, 0, upstreamHook, createNewDevice); + } + + @Override + public final ProxySurface createDummySurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable chosenCaps, GLCapabilitiesImmutable requestedCaps, GLCapabilitiesChooser chooser, int width, int height) { + final WindowsGraphicsDevice device; + if( createNewDevice || !(deviceReq instanceof WindowsGraphicsDevice) ) { + device = new WindowsGraphicsDevice(deviceReq.getConnection(), deviceReq.getUnitID()); + } else { + device = (WindowsGraphicsDevice)deviceReq; + } + final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); + chosenCaps = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps); + final WindowsWGLGraphicsConfiguration config = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(chosenCaps, requestedCaps, chooser, screen); + if(null == config) { + throw new GLException("Choosing GraphicsConfiguration failed w/ "+chosenCaps+" on "+screen); + } + return new GDISurface(config, 0, new GDIDummyUpstreamSurfaceHook(width, height), createNewDevice); } - - protected final ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice adevice, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { - // FIXME device/windowHandle -> screen ?! - WindowsGraphicsDevice device = (WindowsGraphicsDevice) adevice; - AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); - WindowsWGLGraphicsConfiguration cfg = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen); - GDISurface ns = new GDISurface(cfg, windowHandle); - return ns; + + @Override + protected final ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice deviceReq, int screenIdx, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream) { + final WindowsGraphicsDevice device = new WindowsGraphicsDevice(deviceReq.getConnection(), deviceReq.getUnitID()); + final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, screenIdx); + final WindowsWGLGraphicsConfiguration cfg = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen); + return new GDISurface(cfg, windowHandle, upstream, true); } - + + @Override protected final GLContext createExternalGLContextImpl() { return WindowsExternalWGLContext.create(this, null); } + @Override public final boolean canCreateExternalGLDrawable(AbstractGraphicsDevice device) { return true; } + @Override protected final GLDrawable createExternalGLDrawableImpl() { return WindowsExternalWGLDrawable.create(this, null); } @@ -553,25 +549,18 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { return detail; } - public final boolean canCreateContextOnJava2DSurface(AbstractGraphicsDevice device) { - return false; - } - - public final GLContext createContextOnJava2DSurface(Object graphics, GLContext shareWith) - throws GLException { - throw new GLException("Unimplemented on this platform"); - } - //------------------------------------------------------ // Gamma-related functionality // private static final int GAMMA_RAMP_LENGTH = 256; + @Override protected final int getGammaRampLength() { return GAMMA_RAMP_LENGTH; } + @Override protected final boolean setGammaRamp(float[] ramp) { short[] rampData = new short[3 * GAMMA_RAMP_LENGTH]; for (int i = 0; i < GAMMA_RAMP_LENGTH; i++) { @@ -587,6 +576,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { return res; } + @Override protected final Buffer getGammaRamp() { ShortBuffer rampData = ShortBuffer.wrap(new short[3 * GAMMA_RAMP_LENGTH]); long screenDC = GDI.GetDC(0); @@ -598,6 +588,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { return rampData; } + @Override protected final void resetGammaRamp(Buffer originalGammaRamp) { if (originalGammaRamp == null) { // getGammaRamp failed earlier diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java index a553bd4c2..6098cde7f 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,31 +20,31 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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"); - libsList.add(libsGL); + libsList.add(libsGL); return libsList; } - + @Override public final List<String> getToolGetProcAddressFuncNameList() { List<String> res = new ArrayList<String>(); diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfiguration.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfiguration.java index 5682b35e8..5dd9f88b2 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfiguration.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfiguration.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -33,6 +33,8 @@ package jogamp.opengl.windows.wgl; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.List; @@ -44,26 +46,27 @@ import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; -import javax.media.opengl.GLPbuffer; import javax.media.opengl.GLProfile; +import com.jogamp.common.nio.Buffers; import com.jogamp.nativewindow.MutableGraphicsConfiguration; import jogamp.nativewindow.windows.DWM_BLURBEHIND; import jogamp.nativewindow.windows.GDI; +import jogamp.nativewindow.windows.GDIUtil; import jogamp.nativewindow.windows.MARGINS; import jogamp.nativewindow.windows.PIXELFORMATDESCRIPTOR; import jogamp.opengl.GLGraphicsConfigurationUtil; -public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguration implements Cloneable { +public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguration implements Cloneable { protected static final int MAX_PFORMATS = 256; protected static final int MAX_ATTRIBS = 256; - private GLCapabilitiesChooser chooser; + private final GLCapabilitiesChooser chooser; private boolean isDetermined = false; private boolean isExternal = false; - WindowsWGLGraphicsConfiguration(AbstractGraphicsScreen screen, + WindowsWGLGraphicsConfiguration(AbstractGraphicsScreen screen, GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { super(screen, capsChosen, capsRequested); @@ -96,15 +99,15 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio } WindowsWGLDrawableFactory factory = (WindowsWGLDrawableFactory) _factory; AbstractGraphicsDevice device = screen.getDevice(); - WindowsWGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResource(device); + WindowsWGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResourceImpl(device); boolean hasARB = null != sharedResource && sharedResource.hasARBPixelFormat(); WGLGLCapabilities caps = null; if(hasARB) { - caps = wglARBPFID2GLCapabilities(sharedResource, hdc, pfdID, glp, onscreen, true /* pbuffer */); + caps = wglARBPFID2GLCapabilities(sharedResource, device, glp, hdc, pfdID, GLGraphicsConfigurationUtil.ALL_BITS); } else { - caps = PFD2GLCapabilities(glp, hdc, pfdID, onscreen); + caps = PFD2GLCapabilities(device, glp, hdc, pfdID, GLGraphicsConfigurationUtil.ALL_BITS); } if(null==caps) { throw new GLException("Couldn't choose Capabilities by: HDC 0x"+Long.toHexString(hdc)+ @@ -116,6 +119,7 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio return cfg; } + @Override public Object clone() { return super.clone(); } @@ -158,19 +162,19 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio if (0 == hdc) { throw new GLException("Error: HDC is null"); } - + if (!WGLUtil.SetPixelFormat(hdc, caps.getPFDID(), caps.getPFD())) { - throw new GLException("Unable to set pixel format " + caps + + throw new GLException("Unable to set pixel format " + caps.getPFDID() + " of " + caps + " for device context " + toHexString(hdc) + ": error code " + GDI.GetLastError()); } - if(!caps.isBackgroundOpaque()) { + if( !caps.isBackgroundOpaque() ) { final long hwnd = GDI.WindowFromDC(hdc); DWM_BLURBEHIND bb = DWM_BLURBEHIND.create(); - bb.setDwFlags(GDI.DWM_BB_ENABLE); - bb.setFEnable(1); + bb.setDwFlags(GDI.DWM_BB_ENABLE| GDI.DWM_BB_TRANSITIONONMAXIMIZED); + bb.setFEnable( 1 ); boolean ok = GDI.DwmEnableBlurBehindWindow(hwnd, bb); - if(ok) { + if( ok ) { MARGINS m = MARGINS.create(); m.setCxLeftWidth(-1); m.setCxRightWidth(-1); @@ -179,15 +183,17 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio ok = GDI.DwmExtendFrameIntoClientArea(hwnd, m); } if(DEBUG) { - System.err.println("translucency enabled on wnd: 0x"+Long.toHexString(hwnd)+" - ok: "+ok); + final boolean isUndecorated = GDIUtil.IsUndecorated(hwnd); + final boolean isChild = GDIUtil.IsChild(hwnd); + System.err.println("translucency enabled on wnd: 0x"+Long.toHexString(hwnd)+" - isUndecorated "+isUndecorated+", isChild "+isChild+", ok: "+ok); } } if (DEBUG) { - System.err.println("setPixelFormat (ARB): hdc "+toHexString(hdc) +", "+caps); + System.err.println("setPixelFormat: hdc "+toHexString(hdc) +", "+caps); } setCapsPFD(caps); } - + /** * Only sets this configuration's capabilities and marks it as determined, * the actual pixelformat is not set. @@ -204,130 +210,120 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio * External configuration's HDC pixelformat shall not be modified */ public final boolean isExternal() { return isExternal; } - + final void markExternal() { this.isExternal=true; } - + /** * Determined configuration states set target capabilties via {@link #setCapsPFD(WGLGLCapabilities)}, * but does not imply a set pixelformat. - * - * @see #setPixelFormat(long, WGLGLCapabilities) + * + * @see #setPixelFormat(long, WGLGLCapabilities) * @see #setCapsPFD(WGLGLCapabilities) */ public final boolean isDetermined() { return isDetermined; } - + public final PIXELFORMATDESCRIPTOR getPixelFormat() { return isDetermined ? ((WGLGLCapabilities)capabilitiesChosen).getPFD() : null; } public final int getPixelFormatID() { return isDetermined ? ((WGLGLCapabilities)capabilitiesChosen).getPFDID() : 0; } public final boolean isChoosenByARB() { return isDetermined ? ((WGLGLCapabilities)capabilitiesChosen).isSetByARB() : false; } - static int fillAttribsForGeneralWGLARBQuery(WindowsWGLDrawableFactory.SharedResource sharedResource, int[] iattributes) { + static int fillAttribsForGeneralWGLARBQuery(WindowsWGLDrawableFactory.SharedResource sharedResource, IntBuffer iattributes) { int niattribs = 0; - iattributes[niattribs++] = WGLExt.WGL_DRAW_TO_WINDOW_ARB; + iattributes.put(niattribs++, WGLExt.WGL_DRAW_TO_WINDOW_ARB); if(sharedResource.hasARBPBuffer()) { - iattributes[niattribs++] = WGLExt.WGL_DRAW_TO_PBUFFER_ARB; - } - iattributes[niattribs++] = WGLExt.WGL_DRAW_TO_BITMAP_ARB; - iattributes[niattribs++] = WGLExt.WGL_ACCELERATION_ARB; - iattributes[niattribs++] = WGLExt.WGL_SUPPORT_OPENGL_ARB; - iattributes[niattribs++] = WGLExt.WGL_DEPTH_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_STENCIL_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_DOUBLE_BUFFER_ARB; - iattributes[niattribs++] = WGLExt.WGL_STEREO_ARB; - iattributes[niattribs++] = WGLExt.WGL_PIXEL_TYPE_ARB; - iattributes[niattribs++] = WGLExt.WGL_RED_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_GREEN_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_BLUE_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_ALPHA_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_ACCUM_RED_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_ACCUM_GREEN_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_ACCUM_BLUE_BITS_ARB; - iattributes[niattribs++] = WGLExt.WGL_ACCUM_ALPHA_BITS_ARB; + iattributes.put(niattribs++, WGLExt.WGL_DRAW_TO_PBUFFER_ARB); + } + iattributes.put(niattribs++, WGLExt.WGL_DRAW_TO_BITMAP_ARB); + iattributes.put(niattribs++, WGLExt.WGL_ACCELERATION_ARB); + iattributes.put(niattribs++, WGLExt.WGL_SUPPORT_OPENGL_ARB); + iattributes.put(niattribs++, WGLExt.WGL_DEPTH_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_STENCIL_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_DOUBLE_BUFFER_ARB); + iattributes.put(niattribs++, WGLExt.WGL_STEREO_ARB); + iattributes.put(niattribs++, WGLExt.WGL_PIXEL_TYPE_ARB); + iattributes.put(niattribs++, WGLExt.WGL_RED_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_GREEN_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_BLUE_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_ALPHA_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_RED_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_GREEN_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_BLUE_BITS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_ALPHA_BITS_ARB); if(sharedResource.hasARBMultisample()) { - iattributes[niattribs++] = WGLExt.WGL_SAMPLE_BUFFERS_ARB; - iattributes[niattribs++] = WGLExt.WGL_SAMPLES_ARB; - } - - if(sharedResource.hasARBPBuffer()) { - WindowsWGLContext sharedCtx = sharedResource.getContext(); - if(null != sharedCtx && sharedCtx.isExtensionAvailable(WindowsWGLDrawableFactory.WGL_NV_float_buffer)) { - // pbo float buffer - iattributes[niattribs++] = WGLExt.WGL_FLOAT_COMPONENTS_NV; // nvidia - } + iattributes.put(niattribs++, WGLExt.WGL_SAMPLE_BUFFERS_ARB); + iattributes.put(niattribs++, WGLExt.WGL_SAMPLES_ARB); } - return niattribs; } - + static boolean wglARBPFIDValid(WindowsWGLContext sharedCtx, long hdc, int pfdID) { - int[] in = new int[1]; - int[] out = new int[1]; - in[0] = WGLExt.WGL_COLOR_BITS_ARB; - if (!sharedCtx.getWGLExt().wglGetPixelFormatAttribivARB(hdc, pfdID, 0, 1, in, 0, out, 0)) { + final IntBuffer out = Buffers.newDirectIntBuffer(1); + final IntBuffer in = Buffers.newDirectIntBuffer(1); + in.put(0, WGLExt.WGL_COLOR_BITS_ARB); + if (!sharedCtx.getWGLExt().wglGetPixelFormatAttribivARB(hdc, pfdID, 0, 1, in, out)) { // Some GPU's falsely fails with a zero error code (success) return GDI.GetLastError() == GDI.ERROR_SUCCESS ; } return true; } - static int[] wglAllARBPFIDs(WindowsWGLContext sharedCtx, long hdc) { - int[] iattributes = new int[1]; - int[] iresults = new int[1]; + static int wglARBPFDIDCount(WindowsWGLContext sharedCtx, long hdc) { + final IntBuffer iresults = Buffers.newDirectIntBuffer(1); + final IntBuffer iattributes = Buffers.newDirectIntBuffer(1); + iattributes.put(0, WGLExt.WGL_NUMBER_PIXEL_FORMATS_ARB); WGLExt wglExt = sharedCtx.getWGLExt(); - iattributes[0] = WGLExt.WGL_NUMBER_PIXEL_FORMATS_ARB; - if (!wglExt.wglGetPixelFormatAttribivARB(hdc, 0, 0, 1, iattributes, 0, iresults, 0)) { + // pfdID shall be ignored here (spec), however, pass a valid pdf index '1' below (possible driver bug) + if (!wglExt.wglGetPixelFormatAttribivARB(hdc, 1 /* pfdID */, 0, 1, iattributes, iresults)) { if(DEBUG) { System.err.println("GetPixelFormatAttribivARB: Failed - HDC 0x" + Long.toHexString(hdc) + + ", value "+iresults.get(0)+ ", LastError: " + GDI.GetLastError()); Thread.dumpStack(); } - return null; + return 0; } - int numFormats = iresults[0]; - if(0 == numFormats) { + final int pfdIDCount = iresults.get(0); + if(0 == pfdIDCount) { if(DEBUG) { System.err.println("GetPixelFormatAttribivARB: No formats - HDC 0x" + Long.toHexString(hdc) + ", LastError: " + GDI.GetLastError()); Thread.dumpStack(); } - return null; } - int[] pfdIDs = new int[numFormats]; - for (int i = 0; i < numFormats; i++) { + return pfdIDCount; + } + + static int[] wglAllARBPFDIDs(int pfdIDCount) { + int[] pfdIDs = new int[pfdIDCount]; + for (int i = 0; i < pfdIDCount; i++) { pfdIDs[i] = 1 + i; } return pfdIDs; } static WGLGLCapabilities wglARBPFID2GLCapabilities(WindowsWGLDrawableFactory.SharedResource sharedResource, - long hdc, int pfdID, - GLProfile glp, boolean onscreen, boolean usePBuffer) { + AbstractGraphicsDevice device, GLProfile glp, + long hdc, int pfdID, int winattrbits) { if (!sharedResource.hasARBPixelFormat()) { return null; } - int[] iattributes = new int [2*MAX_ATTRIBS]; - int[] iresults = new int [2*MAX_ATTRIBS]; - - int niattribs = fillAttribsForGeneralWGLARBQuery(sharedResource, iattributes); + final IntBuffer iattributes = Buffers.newDirectIntBuffer(2*MAX_ATTRIBS); + final IntBuffer iresults = Buffers.newDirectIntBuffer(2*MAX_ATTRIBS); + final int niattribs = fillAttribsForGeneralWGLARBQuery(sharedResource, iattributes); - if (!sharedResource.getContext().getWGLExt().wglGetPixelFormatAttribivARB(hdc, pfdID, 0, niattribs, iattributes, 0, iresults, 0)) { - throw new GLException("wglARBPFID2GLCapabilities: Error getting pixel format attributes for pixel format " + pfdID + + if (!((WindowsWGLContext)sharedResource.getContext()).getWGLExt().wglGetPixelFormatAttribivARB(hdc, pfdID, 0, niattribs, iattributes, iresults)) { + throw new GLException("wglARBPFID2GLCapabilities: Error getting pixel format attributes for pixel format " + pfdID + " of device context " + toHexString(hdc) + ", werr " + GDI.GetLastError()); } - ArrayList<WGLGLCapabilities> bucket = new ArrayList<WGLGLCapabilities>(1); - final int winattrbits = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); - if(AttribList2GLCapabilities(bucket, glp, hdc, pfdID, iattributes, niattribs, iresults, winattrbits)) { - return bucket.get(0); - } - return null; + return AttribList2GLCapabilities(device, glp, hdc, pfdID, iattributes, niattribs, iresults, winattrbits); } - static int[] wglChoosePixelFormatARB(long hdc, WindowsWGLDrawableFactory.SharedResource sharedResource, + static int[] wglChoosePixelFormatARB(WindowsWGLDrawableFactory.SharedResource sharedResource, AbstractGraphicsDevice device, GLCapabilitiesImmutable capabilities, - int[] iattributes, int accelerationMode, float[] fattributes) + long hdc, IntBuffer iattributes, int accelerationMode, FloatBuffer fattributes) { if ( !WindowsWGLGraphicsConfiguration.GLCapabilities2AttribList(capabilities, @@ -340,77 +336,83 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio return null; } - int[] pformatsTmp = new int[WindowsWGLGraphicsConfiguration.MAX_PFORMATS]; - int[] numFormatsTmp = new int[1]; - if ( !sharedResource.getContext().getWGLExt().wglChoosePixelFormatARB(hdc, iattributes, 0, - fattributes, 0, - WindowsWGLGraphicsConfiguration.MAX_PFORMATS, - pformatsTmp, 0, numFormatsTmp, 0)) - { + final WGLExt wglExt = ((WindowsWGLContext)sharedResource.getContext()).getWGLExt(); + final IntBuffer pformatsTmp = Buffers.newDirectIntBuffer(WindowsWGLGraphicsConfiguration.MAX_PFORMATS); + final IntBuffer numFormatsTmp = Buffers.newDirectIntBuffer(1); + + if ( !wglExt.wglChoosePixelFormatARB(hdc, iattributes, fattributes, + WindowsWGLGraphicsConfiguration.MAX_PFORMATS, + pformatsTmp, numFormatsTmp) ) { if (DEBUG) { System.err.println("wglChoosePixelFormatARB: wglChoosePixelFormatARB failed: " + GDI.GetLastError()); Thread.dumpStack(); } return null; } - int numFormats = numFormatsTmp[0]; - int[] pformats = null; + final int numFormats = Math.min(numFormatsTmp.get(0), WindowsWGLGraphicsConfiguration.MAX_PFORMATS); + final int[] pformats; if( 0 < numFormats ) { pformats = new int[numFormats]; - System.arraycopy(pformatsTmp, 0, pformats, 0, numFormats); + pformatsTmp.get(pformats, 0, numFormats); + } else { + pformats = null; } if (DEBUG) { System.err.println("wglChoosePixelFormatARB: NumFormats (wglChoosePixelFormatARB) accelMode 0x" + Integer.toHexString(accelerationMode) + ": " + numFormats); for (int i = 0; i < numFormats; i++) { WGLGLCapabilities dbgCaps0 = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities( - sharedResource, hdc, pformats[i], - capabilities.getGLProfile(), capabilities.isOnscreen(), capabilities.isPBuffer()); + sharedResource, device, capabilities.getGLProfile(), hdc, pformats[i], GLGraphicsConfigurationUtil.ALL_BITS); System.err.println("pixel format " + pformats[i] + " (index " + i + "): " + dbgCaps0); } } return pformats; } - static List /*<GLCapabilitiesImmutable>*/ wglARBPFIDs2GLCapabilities(WindowsWGLDrawableFactory.SharedResource sharedResource, - long hdc, int[] pfdIDs, GLProfile glp, boolean onscreen, boolean usePBuffer) { - final int winattrbits = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); - return wglARBPFIDs2GLCapabilitiesImpl(sharedResource, hdc, pfdIDs, glp, winattrbits); - } - - static List /*<GLCapabilitiesImmutable>*/ wglARBPFIDs2AllGLCapabilities(WindowsWGLDrawableFactory.SharedResource sharedResource, - long hdc, int[] pfdIDs, GLProfile glp) { - return wglARBPFIDs2GLCapabilitiesImpl(sharedResource, hdc, pfdIDs, glp, GLGraphicsConfigurationUtil.ALL_BITS); - } - - private static List /*<GLCapabilitiesImmutable>*/ wglARBPFIDs2GLCapabilitiesImpl(WindowsWGLDrawableFactory.SharedResource sharedResource, - long hdc, int[] pfdIDs, GLProfile glp, int winattrbits) { + static List <GLCapabilitiesImmutable> wglARBPFIDs2GLCapabilities(WindowsWGLDrawableFactory.SharedResource sharedResource, + AbstractGraphicsDevice device, GLProfile glp, long hdc, int[] pfdIDs, int winattrbits, boolean onlyFirstValid) { if (!sharedResource.hasARBPixelFormat()) { return null; } final int numFormats = pfdIDs.length; - int[] iattributes = new int [2*MAX_ATTRIBS]; - int[] iresults = new int [2*MAX_ATTRIBS]; - int niattribs = fillAttribsForGeneralWGLARBQuery(sharedResource, iattributes); + final IntBuffer iattributes = Buffers.newDirectIntBuffer(2*MAX_ATTRIBS); + final IntBuffer iresults = Buffers.newDirectIntBuffer(2*MAX_ATTRIBS); + final int niattribs = fillAttribsForGeneralWGLARBQuery(sharedResource, iattributes); ArrayList<GLCapabilitiesImmutable> bucket = new ArrayList<GLCapabilitiesImmutable>(); for(int i = 0; i<numFormats; i++) { if ( pfdIDs[i] >= 1 && - sharedResource.getContext().getWGLExt().wglGetPixelFormatAttribivARB(hdc, pfdIDs[i], 0, niattribs, iattributes, 0, iresults, 0) ) { - AttribList2GLCapabilities(bucket, glp, hdc, pfdIDs[i], iattributes, niattribs, iresults, winattrbits); + ((WindowsWGLContext)sharedResource.getContext()).getWGLExt().wglGetPixelFormatAttribivARB(hdc, pfdIDs[i], 0, niattribs, iattributes, iresults) ) { + final GLCapabilitiesImmutable caps = AttribList2GLCapabilities(device, glp, hdc, pfdIDs[i], iattributes, niattribs, iresults, winattrbits); + if(null != caps) { + bucket.add(caps); + if(DEBUG) { + final int j = bucket.size() - 1; + System.err.println("wglARBPFIDs2GLCapabilities: bucket["+i+" -> "+j+"]: "+caps); + } + if( onlyFirstValid ) { + break; + } + } else if(DEBUG) { + GLCapabilitiesImmutable skipped = AttribList2GLCapabilities(device, glp, hdc, pfdIDs[i], iattributes, niattribs, iresults, GLGraphicsConfigurationUtil.ALL_BITS); + System.err.println("wglARBPFIDs2GLCapabilities: bucket["+i+" -> skip]: pfdID "+pfdIDs[i]+", "+skipped+", winattr "+GLGraphicsConfigurationUtil.winAttributeBits2String(null, winattrbits).toString()); + } } else if (DEBUG) { - System.err.println("wglARBPFIDs2GLCapabilities: Cannot get pixel format attributes for pixel format " + - i + "/" + numFormats + ": " + pfdIDs[i] + ", " + - GLGraphicsConfigurationUtil.winAttributeBits2String(null, winattrbits).toString()); + if( 1 > pfdIDs[i] ) { + System.err.println("wglARBPFIDs2GLCapabilities: Invalid pfdID " + i + "/" + numFormats + ": " + pfdIDs[i]); + } else { + System.err.println("wglARBPFIDs2GLCapabilities: Cannot get pixel format attributes for pixel format " + + i + "/" + numFormats + ": " + pfdIDs[i] + ", hdc " + toHexString(hdc)); + } } } return bucket; } static boolean GLCapabilities2AttribList(GLCapabilitiesImmutable caps, - int[] iattributes, + IntBuffer iattributes, WindowsWGLDrawableFactory.SharedResource sharedResource, int accelerationValue, int[] floatMode) throws GLException { @@ -418,212 +420,145 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio return false; } - boolean onscreen = caps.isOnscreen(); - boolean pbuffer = caps.isPBuffer(); - int niattribs = 0; - iattributes[niattribs++] = WGLExt.WGL_SUPPORT_OPENGL_ARB; - iattributes[niattribs++] = GL.GL_TRUE; + iattributes.put(niattribs++, WGLExt.WGL_SUPPORT_OPENGL_ARB); + iattributes.put(niattribs++, GL.GL_TRUE); if(accelerationValue>0) { - iattributes[niattribs++] = WGLExt.WGL_ACCELERATION_ARB; - iattributes[niattribs++] = accelerationValue; - } - if (onscreen) { - iattributes[niattribs++] = WGLExt.WGL_DRAW_TO_WINDOW_ARB; - iattributes[niattribs++] = GL.GL_TRUE; - } else if (pbuffer && sharedResource.hasARBPBuffer()) { - iattributes[niattribs++] = WGLExt.WGL_DRAW_TO_PBUFFER_ARB; - iattributes[niattribs++] = GL.GL_TRUE; + iattributes.put(niattribs++, WGLExt.WGL_ACCELERATION_ARB); + iattributes.put(niattribs++, accelerationValue); + } + + final boolean usePBuffer = caps.isPBuffer() && sharedResource.hasARBPBuffer() ; + + final int surfaceType; + if( caps.isOnscreen() ) { + surfaceType = WGLExt.WGL_DRAW_TO_WINDOW_ARB; + } else if( caps.isFBO() ) { + surfaceType = WGLExt.WGL_DRAW_TO_WINDOW_ARB; // native replacement! + } else if( usePBuffer ) { + surfaceType = WGLExt.WGL_DRAW_TO_PBUFFER_ARB; + } else if( caps.isBitmap() ) { + surfaceType = WGLExt.WGL_DRAW_TO_BITMAP_ARB; } else { - iattributes[niattribs++] = WGLExt.WGL_DRAW_TO_BITMAP_ARB; - iattributes[niattribs++] = GL.GL_TRUE; + throw new GLException("no surface type set in caps: "+caps); } + iattributes.put(niattribs++, surfaceType); + iattributes.put(niattribs++, GL.GL_TRUE); - iattributes[niattribs++] = WGLExt.WGL_DOUBLE_BUFFER_ARB; + iattributes.put(niattribs++, WGLExt.WGL_DOUBLE_BUFFER_ARB); if (caps.getDoubleBuffered()) { - iattributes[niattribs++] = GL.GL_TRUE; + iattributes.put(niattribs++, GL.GL_TRUE); } else { - iattributes[niattribs++] = GL.GL_FALSE; + iattributes.put(niattribs++, GL.GL_FALSE); } - iattributes[niattribs++] = WGLExt.WGL_STEREO_ARB; + iattributes.put(niattribs++, WGLExt.WGL_STEREO_ARB); if (caps.getStereo()) { - iattributes[niattribs++] = GL.GL_TRUE; + iattributes.put(niattribs++, GL.GL_TRUE); } else { - iattributes[niattribs++] = GL.GL_FALSE; - } - - iattributes[niattribs++] = WGLExt.WGL_RED_BITS_ARB; - iattributes[niattribs++] = caps.getRedBits(); - iattributes[niattribs++] = WGLExt.WGL_GREEN_BITS_ARB; - iattributes[niattribs++] = caps.getGreenBits(); - iattributes[niattribs++] = WGLExt.WGL_BLUE_BITS_ARB; - iattributes[niattribs++] = caps.getBlueBits(); + iattributes.put(niattribs++, GL.GL_FALSE); + } + + iattributes.put(niattribs++, WGLExt.WGL_RED_BITS_ARB); + iattributes.put(niattribs++, caps.getRedBits()); + iattributes.put(niattribs++, WGLExt.WGL_GREEN_BITS_ARB); + iattributes.put(niattribs++, caps.getGreenBits()); + iattributes.put(niattribs++, WGLExt.WGL_BLUE_BITS_ARB); + iattributes.put(niattribs++, caps.getBlueBits()); if(caps.getAlphaBits()>0) { - iattributes[niattribs++] = WGLExt.WGL_ALPHA_BITS_ARB; - iattributes[niattribs++] = caps.getAlphaBits(); + iattributes.put(niattribs++, WGLExt.WGL_ALPHA_BITS_ARB); + iattributes.put(niattribs++, caps.getAlphaBits()); } if(caps.getStencilBits()>0) { - iattributes[niattribs++] = WGLExt.WGL_STENCIL_BITS_ARB; - iattributes[niattribs++] = caps.getStencilBits(); + iattributes.put(niattribs++, WGLExt.WGL_STENCIL_BITS_ARB); + iattributes.put(niattribs++, caps.getStencilBits()); } - iattributes[niattribs++] = WGLExt.WGL_DEPTH_BITS_ARB; - iattributes[niattribs++] = caps.getDepthBits(); + iattributes.put(niattribs++, WGLExt.WGL_DEPTH_BITS_ARB); + iattributes.put(niattribs++, caps.getDepthBits()); if (caps.getAccumRedBits() > 0 || caps.getAccumGreenBits() > 0 || caps.getAccumBlueBits() > 0 || caps.getAccumAlphaBits() > 0) { - iattributes[niattribs++] = WGLExt.WGL_ACCUM_BITS_ARB; - iattributes[niattribs++] = (caps.getAccumRedBits() + - caps.getAccumGreenBits() + - caps.getAccumBlueBits() + - caps.getAccumAlphaBits()); - iattributes[niattribs++] = WGLExt.WGL_ACCUM_RED_BITS_ARB; - iattributes[niattribs++] = caps.getAccumRedBits(); - iattributes[niattribs++] = WGLExt.WGL_ACCUM_GREEN_BITS_ARB; - iattributes[niattribs++] = caps.getAccumGreenBits(); - iattributes[niattribs++] = WGLExt.WGL_ACCUM_BLUE_BITS_ARB; - iattributes[niattribs++] = caps.getAccumBlueBits(); - iattributes[niattribs++] = WGLExt.WGL_ACCUM_ALPHA_BITS_ARB; - iattributes[niattribs++] = caps.getAccumAlphaBits(); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_BITS_ARB); + iattributes.put(niattribs++, ( caps.getAccumRedBits() + + caps.getAccumGreenBits() + + caps.getAccumBlueBits() + + caps.getAccumAlphaBits() ) ); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_RED_BITS_ARB); + iattributes.put(niattribs++, caps.getAccumRedBits()); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_GREEN_BITS_ARB); + iattributes.put(niattribs++, caps.getAccumGreenBits()); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_BLUE_BITS_ARB); + iattributes.put(niattribs++, caps.getAccumBlueBits()); + iattributes.put(niattribs++, WGLExt.WGL_ACCUM_ALPHA_BITS_ARB); + iattributes.put(niattribs++, caps.getAccumAlphaBits()); } if (caps.getSampleBuffers() && sharedResource.hasARBMultisample()) { - iattributes[niattribs++] = WGLExt.WGL_SAMPLE_BUFFERS_ARB; - iattributes[niattribs++] = GL.GL_TRUE; - iattributes[niattribs++] = WGLExt.WGL_SAMPLES_ARB; - iattributes[niattribs++] = caps.getNumSamples(); - } - - boolean rtt = caps.getPbufferRenderToTexture(); - boolean rect = caps.getPbufferRenderToTextureRectangle(); - boolean useFloat = caps.getPbufferFloatingPointBuffers(); - boolean ati = false; - boolean nvidia = false; - if (pbuffer && sharedResource.hasARBPBuffer()) { - // Check some invariants and set up some state - if (rect && !rtt) { - throw new GLException("Render-to-texture-rectangle requires render-to-texture to be specified"); - } - - WindowsWGLContext sharedCtx = sharedResource.getContext(); - if (rect) { - if (!sharedCtx.isExtensionAvailable("GL_NV_texture_rectangle")) { - throw new GLException("Render-to-texture-rectangle requires GL_NV_texture_rectangle extension"); - } - } - - if (useFloat) { - // Prefer NVidia extension over ATI - nvidia = sharedCtx.isExtensionAvailable(WindowsWGLDrawableFactory.WGL_NV_float_buffer); - if(nvidia) { - floatMode[0] = GLPbuffer.NV_FLOAT; - } else { - ati = sharedCtx.isExtensionAvailable("WGL_ATI_pixel_format_float"); - if(ati) { - floatMode[0] = GLPbuffer.ATI_FLOAT; - } else { - throw new GLException("Floating-point pbuffers not supported by this hardware"); - } - } - - if (DEBUG) { - System.err.println("Using " + (ati ? "ATI" : ( nvidia ? "NVidia" : "NONE" ) ) + " floating-point extension"); - } - } - - // See whether we need to change the pixel type to support ATI's - // floating-point pbuffers - if (useFloat && ati) { - if (rtt) { - throw new GLException("Render-to-floating-point-texture not supported on ATI hardware"); - } else { - iattributes[niattribs++] = WGLExt.WGL_PIXEL_TYPE_ARB; - iattributes[niattribs++] = WGLExt.WGL_TYPE_RGBA_FLOAT_ARB; - } - } else { - if (!rtt) { - // Currently we don't support non-truecolor visuals in the - // GLCapabilities, so we don't offer the option of making - // color-index pbuffers. - iattributes[niattribs++] = WGLExt.WGL_PIXEL_TYPE_ARB; - iattributes[niattribs++] = WGLExt.WGL_TYPE_RGBA_ARB; - } - } - - if (useFloat && nvidia) { - iattributes[niattribs++] = WGLExt.WGL_FLOAT_COMPONENTS_NV; - iattributes[niattribs++] = GL.GL_TRUE; - } - - if (rtt) { - if (useFloat) { - assert(!ati); - assert(nvidia); - if (!rect) { - throw new GLException("Render-to-floating-point-texture only supported on NVidia hardware with render-to-texture-rectangle"); - } - iattributes[niattribs++] = WGLExt.WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV; - iattributes[niattribs++] = GL.GL_TRUE; - } else { - iattributes[niattribs++] = rect ? WGLExt.WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV : WGLExt.WGL_BIND_TO_TEXTURE_RGB_ARB; - iattributes[niattribs++] = GL.GL_TRUE; - } - } - } else { - iattributes[niattribs++] = WGLExt.WGL_PIXEL_TYPE_ARB; - iattributes[niattribs++] = WGLExt.WGL_TYPE_RGBA_ARB; + iattributes.put(niattribs++, WGLExt.WGL_SAMPLE_BUFFERS_ARB); + iattributes.put(niattribs++, GL.GL_TRUE); + iattributes.put(niattribs++, WGLExt.WGL_SAMPLES_ARB); + iattributes.put(niattribs++, caps.getNumSamples()); } - iattributes[niattribs++] = 0; + + iattributes.put(niattribs++, WGLExt.WGL_PIXEL_TYPE_ARB); + iattributes.put(niattribs++, WGLExt.WGL_TYPE_RGBA_ARB); + iattributes.put(niattribs++, 0); return true; } - static int AttribList2DrawableTypeBits(final int[] iattribs, final int niattribs, final int[] iresults) { + static int AttribList2DrawableTypeBits(final IntBuffer iattribs, + final int niattribs, final IntBuffer iresults) { int val = 0; for (int i = 0; i < niattribs; i++) { - int attr = iattribs[i]; + final int attr = iattribs.get(i); switch (attr) { case WGLExt.WGL_DRAW_TO_WINDOW_ARB: - if(iresults[i] == GL.GL_TRUE) val |= GLGraphicsConfigurationUtil.WINDOW_BIT; + if(iresults.get(i) == GL.GL_TRUE) { + val |= GLGraphicsConfigurationUtil.WINDOW_BIT | + GLGraphicsConfigurationUtil.FBO_BIT; + } break; case WGLExt.WGL_DRAW_TO_BITMAP_ARB: - if(iresults[i] == GL.GL_TRUE) val |= GLGraphicsConfigurationUtil.BITMAP_BIT; + if(iresults.get(i) == GL.GL_TRUE) { + val |= GLGraphicsConfigurationUtil.BITMAP_BIT; + } break; case WGLExt.WGL_DRAW_TO_PBUFFER_ARB: - if(iresults[i] == GL.GL_TRUE) val |= GLGraphicsConfigurationUtil.PBUFFER_BIT; + if(iresults.get(i) == GL.GL_TRUE) { + val |= GLGraphicsConfigurationUtil.PBUFFER_BIT; + } break; } } return val; } - static boolean AttribList2GLCapabilities( ArrayList<? extends GLCapabilitiesImmutable> capsBucket, - final GLProfile glp, final long hdc, final int pfdID, final int[] iattribs, - final int niattribs, - final int[] iresults, final int winattrmask) { + static WGLGLCapabilities AttribList2GLCapabilities(final AbstractGraphicsDevice device, + final GLProfile glp, final long hdc, final int pfdID, + final IntBuffer iattribs, final int niattribs, IntBuffer iresults, final int winattrmask) { final int allDrawableTypeBits = AttribList2DrawableTypeBits(iattribs, niattribs, iresults); int drawableTypeBits = winattrmask & allDrawableTypeBits; if( 0 == drawableTypeBits ) { - return false; + return null; } PIXELFORMATDESCRIPTOR pfd = createPixelFormatDescriptor(); if (WGLUtil.DescribePixelFormat(hdc, pfdID, PIXELFORMATDESCRIPTOR.size(), pfd) == 0) { // remove displayable bits, since pfdID is non displayable - drawableTypeBits = drawableTypeBits & ~(GLGraphicsConfigurationUtil.WINDOW_BIT | GLGraphicsConfigurationUtil.BITMAP_BIT); + drawableTypeBits = drawableTypeBits & ~(GLGraphicsConfigurationUtil.WINDOW_BIT | GLGraphicsConfigurationUtil.BITMAP_BIT | GLGraphicsConfigurationUtil.FBO_BIT ); if( 0 == drawableTypeBits ) { - return false; + return null; } // non displayable requested (pbuffer) } - WGLGLCapabilities res = new WGLGLCapabilities(pfd, pfdID, glp); + final WGLGLCapabilities res = new WGLGLCapabilities(pfd, pfdID, glp); res.setValuesByARB(iattribs, niattribs, iresults); - - return GLGraphicsConfigurationUtil.addGLCapabilitiesPermutations(capsBucket, res, drawableTypeBits ); + return (WGLGLCapabilities) GLGraphicsConfigurationUtil.fixWinAttribBitsAndHwAccel(device, drawableTypeBits, res); } // @@ -649,7 +584,8 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio int dwFlags = pfd.getDwFlags(); if( 0 != (GDI.PFD_DRAW_TO_WINDOW & dwFlags ) ) { - val |= GLGraphicsConfigurationUtil.WINDOW_BIT; + val |= GLGraphicsConfigurationUtil.WINDOW_BIT | + GLGraphicsConfigurationUtil.FBO_BIT; } if( 0 != (GDI.PFD_DRAW_TO_BITMAP & dwFlags ) ) { val |= GLGraphicsConfigurationUtil.BITMAP_BIT; @@ -657,107 +593,138 @@ public class WindowsWGLGraphicsConfiguration extends MutableGraphicsConfiguratio return val; } - static WGLGLCapabilities PFD2GLCapabilities(GLProfile glp, long hdc, int pfdID, boolean onscreen) { - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, false); - ArrayList<WGLGLCapabilities> capsBucket = new ArrayList<WGLGLCapabilities>(1); - if( PFD2GLCapabilities(capsBucket, glp, hdc, pfdID, winattrmask) ) { - return capsBucket.get(0); - } - return null; - } - - static boolean PFD2GLCapabilities(ArrayList<? extends GLCapabilitiesImmutable> capsBucket, final GLProfile glp, final long hdc, final int pfdID, final int winattrmask) { + static WGLGLCapabilities PFD2GLCapabilities(AbstractGraphicsDevice device, final GLProfile glp, final long hdc, final int pfdID, final int winattrmask) { PIXELFORMATDESCRIPTOR pfd = createPixelFormatDescriptor(hdc, pfdID); if(null == pfd) { - return false; + return null; } - if ((pfd.getDwFlags() & GDI.PFD_SUPPORT_OPENGL) == 0) { - return false; + if ( (pfd.getDwFlags() & GDI.PFD_SUPPORT_OPENGL) == 0) { + return null; } final int allDrawableTypeBits = PFD2DrawableTypeBits(pfd); final int drawableTypeBits = winattrmask & allDrawableTypeBits; if( 0 == drawableTypeBits ) { - return false; + if(DEBUG) { + System.err.println("Drop [drawableType mismatch]: " + WGLGLCapabilities.PFD2String(pfd, pfdID)); + } + return null; + } + if( GLGraphicsConfigurationUtil.BITMAP_BIT == drawableTypeBits ) { + // BITMAP exclusive PFD SafeGuard: Only accept BITMAP compatible color formats! + final int pfdColorBits = pfd.getCColorBits(); + if ( pfdColorBits != 24 || 0 < pfd.getCAlphaBits() ) { // Allowed: RGB888 && !alpha + if(DEBUG) { + System.err.println("Drop [color bits excl BITMAP]: " + WGLGLCapabilities.PFD2String(pfd, pfdID)); + } + return null; + } } - WGLGLCapabilities res = new WGLGLCapabilities(pfd, pfdID, glp); + final WGLGLCapabilities res = new WGLGLCapabilities(pfd, pfdID, glp); res.setValuesByGDI(); + return (WGLGLCapabilities) GLGraphicsConfigurationUtil.fixWinAttribBitsAndHwAccel(device, drawableTypeBits, res); + } - return GLGraphicsConfigurationUtil.addGLCapabilitiesPermutations(capsBucket, res, drawableTypeBits ); - } + static WGLGLCapabilities PFD2GLCapabilitiesNoCheck(AbstractGraphicsDevice device, final GLProfile glp, final long hdc, final int pfdID) { + PIXELFORMATDESCRIPTOR pfd = createPixelFormatDescriptor(hdc, pfdID); + return PFD2GLCapabilitiesNoCheck(device, glp, pfd, pfdID); + } - static PIXELFORMATDESCRIPTOR GLCapabilities2PFD(GLCapabilitiesImmutable caps, PIXELFORMATDESCRIPTOR pfd) { - int colorDepth = (caps.getRedBits() + - caps.getGreenBits() + - caps.getBlueBits()); - if (colorDepth < 15) { - throw new GLException("Bit depths < 15 (i.e., non-true-color) not supported"); - } - int pfdFlags = (GDI.PFD_SUPPORT_OPENGL | - GDI.PFD_GENERIC_ACCELERATED); - if (caps.getDoubleBuffered()) { - pfdFlags |= GDI.PFD_DOUBLEBUFFER; - } - if (caps.isOnscreen()) { - pfdFlags |= GDI.PFD_DRAW_TO_WINDOW; - } else { - pfdFlags |= GDI.PFD_DRAW_TO_BITMAP; - } - if (caps.getStereo()) { - pfdFlags |= GDI.PFD_STEREO; - } - pfd.setDwFlags(pfdFlags); - pfd.setIPixelType((byte) GDI.PFD_TYPE_RGBA); - pfd.setCColorBits((byte) colorDepth); - pfd.setCRedBits ((byte) caps.getRedBits()); - pfd.setCGreenBits((byte) caps.getGreenBits()); - pfd.setCBlueBits ((byte) caps.getBlueBits()); - pfd.setCAlphaBits((byte) caps.getAlphaBits()); - int accumDepth = (caps.getAccumRedBits() + - caps.getAccumGreenBits() + - caps.getAccumBlueBits()); - pfd.setCAccumBits ((byte) accumDepth); - pfd.setCAccumRedBits ((byte) caps.getAccumRedBits()); - pfd.setCAccumGreenBits((byte) caps.getAccumGreenBits()); - pfd.setCAccumBlueBits ((byte) caps.getAccumBlueBits()); - pfd.setCAccumAlphaBits((byte) caps.getAccumAlphaBits()); - pfd.setCDepthBits((byte) caps.getDepthBits()); - pfd.setCStencilBits((byte) caps.getStencilBits()); - pfd.setILayerType((byte) GDI.PFD_MAIN_PLANE); - - // n/a with non ARB/GDI method: - // multisample - // opaque - // pbuffer - return pfd; - } - - static PIXELFORMATDESCRIPTOR createPixelFormatDescriptor(long hdc, int pfdID) { - PIXELFORMATDESCRIPTOR pfd = PIXELFORMATDESCRIPTOR.create(); - pfd.setNSize((short) PIXELFORMATDESCRIPTOR.size()); - pfd.setNVersion((short) 1); - if(0 != hdc && 1 <= pfdID) { - if (WGLUtil.DescribePixelFormat(hdc, pfdID, PIXELFORMATDESCRIPTOR.size(), pfd) == 0) { - // Accelerated pixel formats that are non displayable - if(DEBUG) { - System.err.println("Info: Non displayable pixel format " + pfdID + " of device context: error code " + GDI.GetLastError()); - } + static WGLGLCapabilities PFD2GLCapabilitiesNoCheck(AbstractGraphicsDevice device, GLProfile glp, PIXELFORMATDESCRIPTOR pfd, int pfdID) { + if(null == pfd) { return null; } - } - return pfd; - } - - static PIXELFORMATDESCRIPTOR createPixelFormatDescriptor() { - return createPixelFormatDescriptor(0, 0); - } - - public String toString() { - return "WindowsWGLGraphicsConfiguration["+getScreen()+", pfdID " + getPixelFormatID() + ", ARB-Choosen " + isChoosenByARB() + - ",\n\trequested " + getRequestedCapabilities() + - ",\n\tchosen " + getChosenCapabilities() + - "]"; - } + final WGLGLCapabilities res = new WGLGLCapabilities(pfd, pfdID, glp); + res.setValuesByGDI(); + + return (WGLGLCapabilities) GLGraphicsConfigurationUtil.fixWinAttribBitsAndHwAccel(device, PFD2DrawableTypeBits(pfd), res); + } + + static PIXELFORMATDESCRIPTOR GLCapabilities2PFD(GLCapabilitiesImmutable caps, PIXELFORMATDESCRIPTOR pfd) { + int colorDepth = (caps.getRedBits() + + caps.getGreenBits() + + caps.getBlueBits()); + if (colorDepth < 15) { + throw new GLException("Bit depths < 15 (i.e., non-true-color) not supported"); + } + int pfdFlags = ( GDI.PFD_SUPPORT_OPENGL | GDI.PFD_GENERIC_ACCELERATED ); + + if( caps.isOnscreen() ) { + pfdFlags |= GDI.PFD_DRAW_TO_WINDOW; + } else if( caps.isFBO() ) { + pfdFlags |= GDI.PFD_DRAW_TO_WINDOW; // native replacement! + } else if( caps.isPBuffer() ) { + pfdFlags |= GDI.PFD_DRAW_TO_BITMAP; // pbuffer n/a, use bitmap + } else if( caps.isBitmap() ) { + pfdFlags |= GDI.PFD_DRAW_TO_BITMAP; + } else { + throw new GLException("no surface type set in caps: "+caps); + } + + if ( caps.getDoubleBuffered() ) { + if( caps.isBitmap() || caps.isPBuffer() ) { + pfdFlags |= GDI.PFD_DOUBLEBUFFER_DONTCARE; // bitmaps probably don't have dbl buffering + } else { + pfdFlags |= GDI.PFD_DOUBLEBUFFER; + } + } + + if (caps.getStereo()) { + pfdFlags |= GDI.PFD_STEREO; + } + pfd.setDwFlags(pfdFlags); + pfd.setIPixelType((byte) GDI.PFD_TYPE_RGBA); + pfd.setCColorBits((byte) colorDepth); + pfd.setCRedBits ((byte) caps.getRedBits()); + pfd.setCGreenBits((byte) caps.getGreenBits()); + pfd.setCBlueBits ((byte) caps.getBlueBits()); + pfd.setCAlphaBits((byte) caps.getAlphaBits()); + int accumDepth = (caps.getAccumRedBits() + + caps.getAccumGreenBits() + + caps.getAccumBlueBits()); + pfd.setCAccumBits ((byte) accumDepth); + pfd.setCAccumRedBits ((byte) caps.getAccumRedBits()); + pfd.setCAccumGreenBits((byte) caps.getAccumGreenBits()); + pfd.setCAccumBlueBits ((byte) caps.getAccumBlueBits()); + pfd.setCAccumAlphaBits((byte) caps.getAccumAlphaBits()); + pfd.setCDepthBits((byte) caps.getDepthBits()); + pfd.setCStencilBits((byte) caps.getStencilBits()); + pfd.setILayerType((byte) GDI.PFD_MAIN_PLANE); + + // n/a with non ARB/GDI method: + // multisample + // opaque + // pbuffer + return pfd; + } + + static PIXELFORMATDESCRIPTOR createPixelFormatDescriptor(long hdc, int pfdID) { + PIXELFORMATDESCRIPTOR pfd = PIXELFORMATDESCRIPTOR.create(); + pfd.setNSize((short) PIXELFORMATDESCRIPTOR.size()); + pfd.setNVersion((short) 1); + if(0 != hdc && 1 <= pfdID) { + if (WGLUtil.DescribePixelFormat(hdc, pfdID, PIXELFORMATDESCRIPTOR.size(), pfd) == 0) { + // Accelerated pixel formats that are non displayable + if(DEBUG) { + System.err.println("Info: Non displayable pixel format " + pfdID + " of device context: error code " + GDI.GetLastError()); + } + return null; + } + } + return pfd; + } + + static PIXELFORMATDESCRIPTOR createPixelFormatDescriptor() { + return createPixelFormatDescriptor(0, 0); + } + + @Override + public String toString() { + return "WindowsWGLGraphicsConfiguration["+getScreen()+", pfdID " + getPixelFormatID() + ", ARB-Choosen " + isChoosenByARB() + + ",\n\trequested " + getRequestedCapabilities() + + ",\n\tchosen " + getChosenCapabilities() + + "]"; + } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfigurationFactory.java index 2c8c03185..969e45ed8 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -50,11 +50,17 @@ import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.GLRendererQuirks; + import jogamp.nativewindow.windows.GDI; import jogamp.nativewindow.windows.PIXELFORMATDESCRIPTOR; +import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.GLGraphicsConfigurationFactory; import jogamp.opengl.GLGraphicsConfigurationUtil; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -68,13 +74,14 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat static VisualIDHolder.VIDComparator PfdIDComparator = new VisualIDHolder.VIDComparator(VisualIDHolder.VIDType.WIN32_PFD); static void registerFactory() { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.windows.WindowsGraphicsDevice.class, new WindowsWGLGraphicsConfigurationFactory()); + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.windows.WindowsGraphicsDevice.class, GLCapabilitiesImmutable.class, new WindowsWGLGraphicsConfigurationFactory()); } private WindowsWGLGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( - CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { + CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { if (! (capsChosen instanceof GLCapabilitiesImmutable) ) { throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilities objects - chosen"); @@ -84,7 +91,11 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilities objects - requested"); } - return chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested, chooser, absScreen); + if (chooser != null && !(chooser instanceof GLCapabilitiesChooser)) { + throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilitiesChooser objects"); + } + + return chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested, (GLCapabilitiesChooser)chooser, absScreen); } static WindowsWGLGraphicsConfiguration createDefaultGraphicsConfiguration(GLCapabilitiesImmutable caps, @@ -94,34 +105,34 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat static WindowsWGLGraphicsConfiguration chooseGraphicsConfigurationStatic(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, - CapabilitiesChooser chooser, + GLCapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { if(null==absScreen) { absScreen = DefaultGraphicsScreen.createDefault(NativeWindowFactory.TYPE_WINDOWS); } - AbstractGraphicsDevice absDevice = absScreen.getDevice(); - - capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( - capsChosen, GLDrawableFactory.getDesktopFactory().canCreateGLPbuffer(absDevice) ); - - return new WindowsWGLGraphicsConfiguration( absScreen, capsChosen, capsReq, (GLCapabilitiesChooser)chooser ); + final AbstractGraphicsDevice absDevice = absScreen.getDevice(); + capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, GLDrawableFactory.getDesktopFactory(), absDevice); + return new WindowsWGLGraphicsConfiguration( absScreen, capsChosen, capsReq, chooser ); } protected static List<GLCapabilitiesImmutable> getAvailableCapabilities(WindowsWGLDrawableFactory factory, AbstractGraphicsDevice device) { - WindowsWGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResource(device); + final WindowsWGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResourceImpl(device); if(null == sharedResource) { throw new GLException("Shared resource for device n/a: "+device); } - WindowsWGLDrawable sharedDrawable = sharedResource.getDrawable(); - GLCapabilitiesImmutable capsChosen = sharedDrawable.getChosenGLCapabilities(); - WindowsWGLContext sharedContext = sharedResource.getContext(); - List/*<GLCapabilitiesImmutable>*/ availableCaps = null; - - if ( sharedResource.needsCurrentContext4ARBPFDQueries() ) { + final GLDrawableImpl sharedDrawable = sharedResource.getDrawable(); + final GLProfile glp = GLProfile.getDefault(device); + + List<GLCapabilitiesImmutable> availableCaps = null; + + final GLContext sharedContext; + if ( factory.hasRendererQuirk(device, GLRendererQuirks.NeedCurrCtx4ARBPixFmtQueries) ) { + sharedContext = sharedResource.getContext(); if(GLContext.CONTEXT_NOT_CURRENT == sharedContext.makeCurrent()) { throw new GLException("Could not make Shared Context current: "+device); } } else { + sharedContext = null; sharedDrawable.lockSurface(); } try { @@ -130,17 +141,21 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat throw new GLException("Error: HDC is null"); } if (sharedResource.hasARBPixelFormat()) { - availableCaps = getAvailableGLCapabilitiesARB(hdc, sharedResource, capsChosen.getGLProfile()); + availableCaps = WindowsWGLGraphicsConfigurationFactory.getAvailableGLCapabilitiesARB(sharedResource, sharedResource.getDevice(), glp, hdc); } - if( null == availableCaps || availableCaps.isEmpty() ) { - availableCaps = getAvailableGLCapabilitiesGDI(hdc, capsChosen.getGLProfile()); + final boolean hasARBCaps = null != availableCaps && !availableCaps.isEmpty() ; + final List<GLCapabilitiesImmutable> availableCapsGDI = getAvailableGLCapabilitiesGDI(device, glp, hdc, hasARBCaps); + if( !hasARBCaps ) { + availableCaps = availableCapsGDI; + } else { + availableCaps.addAll(availableCapsGDI); } } finally { - if ( sharedResource.needsCurrentContext4ARBPFDQueries() ) { - sharedContext.release(); + if ( null != sharedContext ) { + sharedContext.release(); } else { sharedDrawable.unlockSurface(); - } + } } if( null != availableCaps && availableCaps.size() > 1 ) { @@ -149,17 +164,23 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat return availableCaps; } - static List/*<GLCapabilitiesImmutable>*/ getAvailableGLCapabilitiesARB(long hdc, WindowsWGLDrawableFactory.SharedResource sharedResource, GLProfile glProfile) { - int[] pformats = WindowsWGLGraphicsConfiguration.wglAllARBPFIDs(sharedResource.getContext(), hdc); - return WindowsWGLGraphicsConfiguration.wglARBPFIDs2AllGLCapabilities(sharedResource, hdc, pformats, glProfile); + private static List<GLCapabilitiesImmutable> getAvailableGLCapabilitiesARB(WindowsWGLDrawableFactory.SharedResource sharedResource, AbstractGraphicsDevice device, GLProfile glProfile, long hdc) { + final int pfdIDCount = WindowsWGLGraphicsConfiguration.wglARBPFDIDCount((WindowsWGLContext)sharedResource.getContext(), hdc); + final int[] pformats = WindowsWGLGraphicsConfiguration.wglAllARBPFDIDs(pfdIDCount); + return WindowsWGLGraphicsConfiguration.wglARBPFIDs2GLCapabilities(sharedResource, device, glProfile, hdc, pformats, + GLGraphicsConfigurationUtil.ALL_BITS & ~GLGraphicsConfigurationUtil.BITMAP_BIT, false); // w/o BITMAP } - static List/*<GLCapabilitiesImmutable>*/ getAvailableGLCapabilitiesGDI(long hdc, GLProfile glProfile) { + private static List<GLCapabilitiesImmutable> getAvailableGLCapabilitiesGDI(AbstractGraphicsDevice device, GLProfile glProfile, long hdc, boolean bitmapOnly) { int[] pformats = WindowsWGLGraphicsConfiguration.wglAllGDIPFIDs(hdc); int numFormats = pformats.length; - ArrayList<GLCapabilitiesImmutable> bucket = new ArrayList<GLCapabilitiesImmutable>(numFormats); + List<GLCapabilitiesImmutable> bucket = new ArrayList<GLCapabilitiesImmutable>(numFormats); for (int i = 0; i < numFormats; i++) { - WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(bucket, glProfile, hdc, pformats[i], GLGraphicsConfigurationUtil.ALL_BITS); + final GLCapabilitiesImmutable caps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(device, glProfile, hdc, pformats[i], + bitmapOnly ? GLGraphicsConfigurationUtil.BITMAP_BIT : GLGraphicsConfigurationUtil.ALL_BITS ); + if(null != caps) { + bucket.add(caps); + } } return bucket; } @@ -206,7 +227,7 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat " for device context " + toHexString(hdc) + ": error code " + GDI.GetLastError()); } - set = true; + set = true; } if (DEBUG) { System.err.println("setPixelFormat (post): hdc "+toHexString(hdc) +", "+pfdID+" -> "+config.getPixelFormatID()+", set: "+set); @@ -264,17 +285,24 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat System.err.println("user chosen caps " + config.getChosenCapabilities()); } AbstractGraphicsDevice device = config.getScreen().getDevice(); - WindowsWGLDrawableFactory.SharedResource sharedResource = ((WindowsWGLDrawableFactory)factory).getOrCreateSharedResource(device); - WindowsWGLContext sharedContext = null; - if (null != sharedResource && sharedResource.needsCurrentContext4ARBPFDQueries()) { + WindowsWGLDrawableFactory.SharedResource sharedResource = ((WindowsWGLDrawableFactory)factory).getOrCreateSharedResourceImpl(device); + final GLContext sharedContext; + if ( factory.hasRendererQuirk(device, GLRendererQuirks.NeedCurrCtx4ARBPixFmtQueries) ) { sharedContext = sharedResource.getContext(); if(GLContext.CONTEXT_NOT_CURRENT == sharedContext.makeCurrent()) { throw new GLException("Could not make Shared Context current: "+device); } + } else { + sharedContext = null; } try { - if( !updateGraphicsConfigurationARB(hdc, extHDC, config, chooser, (WindowsWGLDrawableFactory)factory, pfdIDs) ) { - updateGraphicsConfigurationGDI(hdc, extHDC, config, chooser, pfdIDs); + final GLCapabilitiesImmutable capsChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + boolean done = false; + if( capsChosen.getHardwareAccelerated() && !capsChosen.isBitmap() ) { + done = updateGraphicsConfigurationARB((WindowsWGLDrawableFactory)factory, config, chooser, hdc, extHDC, pfdIDs); + } + if( !done ) { + updateGraphicsConfigurationGDI(config, chooser, hdc, extHDC, pfdIDs); } } finally { if (null != sharedContext) { @@ -283,10 +311,10 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat } } - private static boolean updateGraphicsConfigurationARB(long hdc, boolean extHDC, WindowsWGLGraphicsConfiguration config, - CapabilitiesChooser chooser, WindowsWGLDrawableFactory factory, int[] pformats) { - AbstractGraphicsDevice device = config.getScreen().getDevice(); - WindowsWGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResource(device); + private static boolean updateGraphicsConfigurationARB(WindowsWGLDrawableFactory factory, WindowsWGLGraphicsConfiguration config, CapabilitiesChooser chooser, + long hdc, boolean extHDC, int[] pformats) { + final AbstractGraphicsDevice device = config.getScreen().getDevice(); + final WindowsWGLDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResourceImpl(device); if (null == sharedResource) { if (DEBUG) { @@ -301,14 +329,25 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat return false; } - GLCapabilitiesImmutable capsChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - boolean isOpaque = capsChosen.isBackgroundOpaque() && GDI.DwmIsCompositionEnabled(); - boolean onscreen = capsChosen.isOnscreen(); - boolean usePBuffer = capsChosen.isPBuffer(); - GLProfile glProfile = capsChosen.getGLProfile(); - + final GLCapabilitiesImmutable capsChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final boolean isOpaque = capsChosen.isBackgroundOpaque() && GDI.DwmIsCompositionEnabled(); + final int winattrbits = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(capsChosen) & ~GLGraphicsConfigurationUtil.BITMAP_BIT; // w/o BITMAP + final GLProfile glProfile = capsChosen.getGLProfile(); + + final int pfdIDCount = WindowsWGLGraphicsConfiguration.wglARBPFDIDCount((WindowsWGLContext)sharedResource.getContext(), hdc); + if(DEBUG) { - System.err.println("translucency requested: "+(!capsChosen.isBackgroundOpaque())+", compositioning enabled: "+GDI.DwmIsCompositionEnabled()+" -> translucency "+(!isOpaque)); + System.err.println("updateGraphicsConfigurationARB: hdc "+toHexString(hdc)+", pfdIDCount(hdc) "+pfdIDCount+", capsChosen "+capsChosen+", "+GLGraphicsConfigurationUtil.winAttributeBits2String(null, winattrbits).toString()); + System.err.println("\tisOpaque "+isOpaque+" (translucency requested: "+(!capsChosen.isBackgroundOpaque())+", compositioning enabled: "+GDI.DwmIsCompositionEnabled()+")"); + final int pformatsNum = null != pformats ? pformats.length : -1; + System.err.println("\textHDC "+extHDC+", chooser "+(null!=chooser)+", pformatsNum "+pformatsNum); + } + + if(0 >= pfdIDCount) { + if (DEBUG) { + System.err.println("updateGraphicsConfigurationARB: failed due to 0 pfdIDs for hdc "+toHexString(hdc)+" - hdc incompatible w/ ARB ext."); + } + return false; } WGLGLCapabilities pixelFormatCaps = null; // chosen or preset PFD ID's caps @@ -324,29 +363,28 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat + ", pixelformat " + presetPFDID); } pixelFormatSet = true; - pixelFormatCaps = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities(sharedResource, hdc, presetPFDID, glProfile, onscreen, usePBuffer); + pixelFormatCaps = WindowsWGLGraphicsConfiguration.wglARBPFID2GLCapabilities(sharedResource, device, glProfile, hdc, presetPFDID, winattrbits); pixelFormatCaps = (WGLGLCapabilities) GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(pixelFormatCaps, isOpaque); } else { int recommendedIndex = -1; // recommended index - if(null == pformats) { // No given PFD IDs // // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice - int[] iattributes = new int[2 * WindowsWGLGraphicsConfiguration.MAX_ATTRIBS]; - float[] fattributes = new float[1]; + final IntBuffer iattributes = Buffers.newDirectIntBuffer(2*WindowsWGLGraphicsConfiguration.MAX_ATTRIBS); + final FloatBuffer fattributes = Buffers.newDirectFloatBuffer(1); int accelerationMode = WGLExt.WGL_FULL_ACCELERATION_ARB; - pformats = WindowsWGLGraphicsConfiguration.wglChoosePixelFormatARB(hdc, sharedResource, capsChosen, - iattributes, accelerationMode, fattributes); + pformats = WindowsWGLGraphicsConfiguration.wglChoosePixelFormatARB(sharedResource, device, capsChosen, + hdc, iattributes, accelerationMode, fattributes); if (null == pformats) { accelerationMode = WGLExt.WGL_GENERIC_ACCELERATION_ARB; - pformats = WindowsWGLGraphicsConfiguration.wglChoosePixelFormatARB(hdc, sharedResource, capsChosen, - iattributes, accelerationMode, fattributes); + pformats = WindowsWGLGraphicsConfiguration.wglChoosePixelFormatARB(sharedResource, device, capsChosen, + hdc, iattributes, accelerationMode, fattributes); } if (null == pformats) { accelerationMode = -1; // use what we are offered .. - pformats = WindowsWGLGraphicsConfiguration.wglChoosePixelFormatARB(hdc, sharedResource, capsChosen, - iattributes, accelerationMode, fattributes); + pformats = WindowsWGLGraphicsConfiguration.wglChoosePixelFormatARB(sharedResource, device, capsChosen, + hdc, iattributes, accelerationMode, fattributes); } if (null != pformats) { recommendedIndex = 0; @@ -355,7 +393,7 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat System.err.println("updateGraphicsConfigurationARB: wglChoosePixelFormatARB failed with: "+capsChosen); } // 2nd choice: get all GLCapabilities available, no preferred recommendedIndex available - pformats = WindowsWGLGraphicsConfiguration.wglAllARBPFIDs(sharedResource.getContext(), hdc); + pformats = WindowsWGLGraphicsConfiguration.wglAllARBPFDIDs(pfdIDCount); if (DEBUG) { final int len = ( null != pformats ) ? pformats.length : 0; System.err.println("updateGraphicsConfigurationARB: NumFormats (wglAllARBPFIDs) " + len); @@ -369,14 +407,14 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat return false; } } + final boolean skipCapsChooser = 0 <= recommendedIndex && null == chooser && capsChosen.isBackgroundOpaque(); // fast path: skip choosing if using recommended idx and null chooser is used and if not translucent + + final List<GLCapabilitiesImmutable> availableCaps = + WindowsWGLGraphicsConfiguration.wglARBPFIDs2GLCapabilities(sharedResource, device, glProfile, hdc, pformats, winattrbits, skipCapsChooser /* onlyFirstValid */); - List /*<WGLGLCapabilities>*/ availableCaps = - WindowsWGLGraphicsConfiguration.wglARBPFIDs2GLCapabilities(sharedResource, hdc, pformats, - glProfile, onscreen, usePBuffer); if( null == availableCaps || 0 == availableCaps.size() ) { if (DEBUG) { - System.err.println("updateGraphicsConfigurationARB: wglARBPFIDs2GLCapabilities failed with " + pformats.length + - " pfd ids, onscreen " + onscreen + ", pbuffer " + usePBuffer); + System.err.println("updateGraphicsConfigurationARB: wglARBPFIDs2GLCapabilities failed with " + pformats.length + " pfd ids"); Thread.dumpStack(); } return false; @@ -384,14 +422,19 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat if (DEBUG) { System.err.println("updateGraphicsConfigurationARB: " + pformats.length + - " pfd ids, onscreen " + onscreen + ", pbuffer " + usePBuffer + ", " + availableCaps.size() + " glcaps"); + " pfd ids, skipCapsChooser " + skipCapsChooser + ", " + GLGraphicsConfigurationUtil.winAttributeBits2String(null, winattrbits).toString() + ", " + availableCaps.size() + " glcaps"); if(0 <= recommendedIndex) { System.err.println("updateGraphicsConfigurationARB: Used wglChoosePixelFormatARB to recommend pixel format " + pformats[recommendedIndex] + ", idx " + recommendedIndex +", "+availableCaps.get(recommendedIndex)); } } - int chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); + final int chosenIndex; + if( skipCapsChooser ) { + chosenIndex = recommendedIndex; + } else { + chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); + } if ( 0 > chosenIndex ) { if (DEBUG) { Thread.dumpStack(); @@ -402,12 +445,12 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat if( null == pixelFormatCaps) { throw new GLException("Null Capabilities with "+ " chosen pfdID: native recommended "+ (recommendedIndex+1) + - " chosen idx "+chosenIndex); + " chosen idx "+chosenIndex+", skipCapsChooser "+skipCapsChooser); } pixelFormatCaps = (WGLGLCapabilities) GLGraphicsConfigurationUtil.fixOpaqueGLCapabilities(pixelFormatCaps, isOpaque); if (DEBUG) { System.err.println("chosen pfdID (ARB): native recommended "+ (recommendedIndex+1) + - " chosen "+pixelFormatCaps); + " chosen "+pixelFormatCaps+", skipCapsChooser "+skipCapsChooser); } } @@ -419,61 +462,121 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat return true; } - private static boolean updateGraphicsConfigurationGDI(long hdc, boolean extHDC, WindowsWGLGraphicsConfiguration config, - CapabilitiesChooser chooser, int[] pformats) { - GLCapabilitiesImmutable capsChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - if(capsChosen.isPBuffer()) { + private static boolean updateGraphicsConfigurationGDI(WindowsWGLGraphicsConfiguration config, CapabilitiesChooser chooser, long hdc, + boolean extHDC, int[] pformats) { + final GLCapabilitiesImmutable capsChosen = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + if( !capsChosen.isOnscreen() && capsChosen.isPBuffer() ) { if (DEBUG) { System.err.println("updateGraphicsConfigurationGDI: no pbuffer supported on GDI: " + capsChosen); } return false; } - boolean onscreen = capsChosen.isOnscreen(); - GLProfile glProfile = capsChosen.getGLProfile(); + // final boolean onscreen = capsChosen.isOnscreen(); + // final boolean useFBO = capsChosen.isFBO(); + final GLProfile glProfile = capsChosen.getGLProfile(); + final int winattrmask = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(capsChosen); + + if(DEBUG) { + System.err.println("updateGraphicsConfigurationGDI: hdc "+toHexString(hdc)+", capsChosen "+capsChosen+", "+GLGraphicsConfigurationUtil.winAttributeBits2String(null, winattrmask).toString()); + final int pformatsNum = null != pformats ? pformats.length : -1; + System.err.println("\textHDC "+extHDC+", chooser "+(null!=chooser)+", pformatsNum "+pformatsNum); + } - ArrayList<WGLGLCapabilities> availableCaps = new ArrayList<WGLGLCapabilities>(); - int pfdID; // chosen or preset PFD ID + final AbstractGraphicsDevice device = config.getScreen().getDevice(); WGLGLCapabilities pixelFormatCaps = null; // chosen or preset PFD ID's caps boolean pixelFormatSet = false; // indicates a preset PFD ID [caps] - - if ( !extHDC && 1 <= ( pfdID = WGLUtil.GetPixelFormat(hdc) ) ) { + final int presetPFDID = extHDC ? -1 : WGLUtil.GetPixelFormat(hdc) ; + if ( 1 <= presetPFDID ) { // Pixelformat already set by either // - a previous preselectGraphicsConfiguration() call on the same HDC, // - the graphics driver, copying the HDC's pixelformat to the new one, // - or the Java2D/OpenGL pipeline's configuration if (DEBUG) { System.err.println("updateGraphicsConfigurationGDI: NOTE: pixel format already chosen for HDC: " + toHexString(hdc) - + ", pixelformat " + pfdID); + + ", pixelformat " + presetPFDID); } pixelFormatSet = true; - pixelFormatCaps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(glProfile, hdc, pfdID, onscreen); + pixelFormatCaps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(device, glProfile, hdc, presetPFDID, winattrmask); + if(null == pixelFormatCaps) { + throw new GLException("Could not map PFD2GLCaps w/ already chosen pfdID "+presetPFDID); + } } else { - if(null == pformats) { + final boolean givenPFormats = null != pformats; + if( !givenPFormats ) { pformats = WindowsWGLGraphicsConfiguration.wglAllGDIPFIDs(hdc); } - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, false); - - for (int i = 0; i < pformats.length; i++) { - WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(availableCaps, glProfile, hdc, pformats[i], winattrmask); - } // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice + final List<GLCapabilitiesImmutable> availableCaps = new ArrayList<GLCapabilitiesImmutable>(); PIXELFORMATDESCRIPTOR pfd = WindowsWGLGraphicsConfiguration.createPixelFormatDescriptor(); pfd = WindowsWGLGraphicsConfiguration.GLCapabilities2PFD(capsChosen, pfd); - pfdID = WGLUtil.ChoosePixelFormat(hdc, pfd); + int chosenPFDID = WGLUtil.ChoosePixelFormat(hdc, pfd); int recommendedIndex = -1 ; - if( 1 <= pfdID ) { - // seek index .. - for (recommendedIndex = availableCaps.size() - 1 ; - 0 <= recommendedIndex && pfdID != ((WGLGLCapabilities) availableCaps.get(recommendedIndex)).getPFDID(); - recommendedIndex--) - { /* nop */ } + final boolean skipCapsChooser; + if( 1 <= chosenPFDID ) { + final boolean _skipCapsChooser = null == chooser && capsChosen.isBackgroundOpaque(); // fast path: skip choosing if using recommended idx and null chooser is used and if not translucent + // seek index .. in all formats _or_ in given formats! + int chosenIdx; + for (chosenIdx = pformats.length - 1 ; 0 <= chosenIdx && chosenPFDID != pformats[chosenIdx]; chosenIdx--) { /* nop */ } + if( 0 <= chosenIdx ) { + if( _skipCapsChooser ) { + final WGLGLCapabilities caps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(device, glProfile, hdc, chosenPFDID, winattrmask); + availableCaps.add(caps); + recommendedIndex = 0; + skipCapsChooser = true; + } else { + skipCapsChooser = false; + } + if( DEBUG ) { + System.err.println("Chosen PFDID "+chosenPFDID+" (idx "+chosenIdx+") -> recommendedIndex "+recommendedIndex+", skipCapsChooser "+skipCapsChooser); + } + } else { + if(DEBUG) { + final GLCapabilitiesImmutable reqPFDCaps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilitiesNoCheck(device, glProfile, pfd, chosenPFDID); + final GLCapabilitiesImmutable chosenCaps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(device, glProfile, hdc, chosenPFDID, winattrmask); + System.err.println("Chosen PFDID "+chosenPFDID+" (idx "+chosenIdx+"), but not found in available caps (use given pfdIDs "+givenPFormats+", reqPFDCaps "+reqPFDCaps+", chosenCaps: "+chosenCaps); + } + chosenPFDID = 0; // not found in pformats -> clear + skipCapsChooser = false; + } + } else { + skipCapsChooser = false; } - // 2nd choice: if no preferred recommendedIndex available if (DEBUG) { - System.err.println("updateGraphicsConfigurationGDI: ChoosePixelFormat(HDC " + toHexString(hdc) + ") = " + pfdID + ", idx " + recommendedIndex + " (LastError: " + GDI.GetLastError() + ")"); + System.err.println("updateGraphicsConfigurationGDI: ChoosePixelFormat(HDC " + toHexString(hdc) + ") = pfdID " + chosenPFDID + ", skipCapsChooser "+skipCapsChooser+", idx " + recommendedIndex + " (LastError: " + GDI.GetLastError() + ")"); + } + + if( !skipCapsChooser ) { + for (int i = 0; i < pformats.length; i++) { + final int pfdid = pformats[i]; + final WGLGLCapabilities caps = WindowsWGLGraphicsConfiguration.PFD2GLCapabilities(device, glProfile, hdc, pfdid, winattrmask); + if(null != caps) { + availableCaps.add(caps); + if(DEBUG) { + final int j = availableCaps.size() - 1; + System.err.println("updateGraphicsConfigurationGDI: availableCaps["+i+" -> "+j+"]: "+caps); + } + } else if(DEBUG) { + GLCapabilitiesImmutable skipped = WindowsWGLGraphicsConfiguration.PFD2GLCapabilitiesNoCheck(device, glProfile, hdc, pformats[i]); + System.err.println("updateGraphicsConfigurationGDI: availableCaps["+i+" -> skip]: pfdID "+pformats[i]+", "+skipped); + } + } + // seek recommendedIndex in all _or_ given formats! + if( 1 <= chosenPFDID && 0 > recommendedIndex) { + for (recommendedIndex = availableCaps.size() - 1 ; + 0 <= recommendedIndex && chosenPFDID != ((WGLGLCapabilities) availableCaps.get(recommendedIndex)).getPFDID(); + recommendedIndex--) + { /* nop */ } + } + } + + // 2nd choice: if no preferred recommendedIndex available + final int chosenIndex; + if( skipCapsChooser ) { + chosenIndex = recommendedIndex; + } else { + chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); } - int chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); if ( 0 > chosenIndex ) { if (DEBUG) { System.err.println("updateGraphicsConfigurationGDI: failed, return false"); @@ -481,10 +584,10 @@ public class WindowsWGLGraphicsConfigurationFactory extends GLGraphicsConfigurat } return false; } - pixelFormatCaps = availableCaps.get(chosenIndex); + pixelFormatCaps = (WGLGLCapabilities) availableCaps.get(chosenIndex); if (DEBUG) { - System.err.println("chosen pfdID (GDI): native recommended "+ (recommendedIndex+1) + - ", caps " + pixelFormatCaps); + System.err.println("chosen pfdID (GDI): recommendedIndex "+recommendedIndex+" -> chosenIndex "+ chosenIndex + ", skipCapsChooser "+skipCapsChooser+", caps " + pixelFormatCaps + + " (" + WGLGLCapabilities.PFD2String(pixelFormatCaps.getPFD(), pixelFormatCaps.getPFDID()) +")"); } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/awt/WindowsAWTWGLGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/awt/WindowsAWTWGLGraphicsConfigurationFactory.java index bd64b58a4..96bd0bdd0 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/awt/WindowsAWTWGLGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/awt/WindowsAWTWGLGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -63,14 +63,15 @@ import javax.media.opengl.GLDrawableFactory; public class WindowsAWTWGLGraphicsConfigurationFactory extends GLGraphicsConfigurationFactory { public static void registerFactory() { - GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.awt.AWTGraphicsDevice.class, new WindowsAWTWGLGraphicsConfigurationFactory()); + GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.awt.AWTGraphicsDevice.class, GLCapabilitiesImmutable.class, new WindowsAWTWGLGraphicsConfigurationFactory()); } - private WindowsAWTWGLGraphicsConfigurationFactory() { + private WindowsAWTWGLGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, - CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { + CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { GraphicsDevice device = null; if (absScreen != null && !(absScreen instanceof AWTGraphicsScreen)) { @@ -105,16 +106,16 @@ public class WindowsAWTWGLGraphicsConfigurationFactory extends GLGraphicsConfigu WindowsGraphicsDevice winDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); DefaultGraphicsScreen winScreen = new DefaultGraphicsScreen(winDevice, awtScreen.getIndex()); - GraphicsConfigurationFactory configFactory = GraphicsConfigurationFactory.getFactory(winDevice); + GraphicsConfigurationFactory configFactory = GraphicsConfigurationFactory.getFactory(winDevice, capsChosen); WindowsWGLGraphicsConfiguration winConfig = (WindowsWGLGraphicsConfiguration) configFactory.chooseGraphicsConfiguration(capsChosen, capsRequested, - chooser, winScreen); + chooser, winScreen, nativeVisualID); if (winConfig == null) { throw new GLException("Unable to choose a GraphicsConfiguration: "+capsChosen+",\n\t"+chooser+"\n\t"+winScreen); } - GLDrawableFactory drawableFactory = GLDrawableFactory.getFactory(((GLCapabilitiesImmutable)capsChosen).getGLProfile()); + GLDrawableFactory drawableFactory = GLDrawableFactory.getFactory(((GLCapabilitiesImmutable)capsChosen).getGLProfile()); GraphicsConfiguration chosenGC = null; if ( drawableFactory instanceof WindowsWGLDrawableFactory ) { @@ -138,7 +139,7 @@ public class WindowsAWTWGLGraphicsConfigurationFactory extends GLGraphicsConfigu } // go on .. } - + if( null == chosenGC ) { // 2nd Choice: Choose and match the GL Visual with AWT: // - collect all AWT PFDs @@ -147,7 +148,7 @@ public class WindowsAWTWGLGraphicsConfigurationFactory extends GLGraphicsConfigu // The resulting GraphicsConfiguration has to be 'forced' on the AWT native peer, // ie. returned by GLCanvas's getGraphicsConfiguration() befor call by super.addNotify(). // - + // collect all available PFD IDs GraphicsConfiguration[] configs = device.getConfigurations(); int[] pfdIDs = new int[configs.length]; @@ -171,11 +172,11 @@ public class WindowsAWTWGLGraphicsConfigurationFactory extends GLGraphicsConfigu System.err.println("WindowsAWTWGLGraphicsConfigurationFactory: Found matching AWT PFD ID "+winConfig.getPixelFormatID()+" -> "+winConfig); } } - } + } } else { chosenGC = device.getDefaultConfiguration(); } - + if ( null == chosenGC ) { throw new GLException("Unable to determine GraphicsConfiguration: "+winConfig); } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/GLXUtil.java b/src/jogl/classes/jogamp/opengl/x11/glx/GLXUtil.java index 7cc2d0f2e..12e3db3bd 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/GLXUtil.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/GLXUtil.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -33,54 +33,91 @@ package jogamp.opengl.x11.glx; +import java.nio.IntBuffer; + import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.VersionNumber; import com.jogamp.nativewindow.x11.X11GraphicsDevice; public class GLXUtil { public static final boolean DEBUG = Debug.debug("GLXUtil"); - + public static synchronized boolean isGLXAvailableOnServer(X11GraphicsDevice x11Device) { if(null == x11Device) { throw new IllegalArgumentException("null X11GraphicsDevice"); } if(0 == x11Device.getHandle()) { throw new IllegalArgumentException("null X11GraphicsDevice display handle"); - } + } boolean glXAvailable = false; + x11Device.lock(); try { - glXAvailable = GLX.glXQueryExtension(x11Device.getHandle(), null, 0, null, 0); - } catch (Throwable t) { /* n/a */ } - return glXAvailable; + glXAvailable = GLX.glXQueryExtension(x11Device.getHandle(), null, null); + } catch (Throwable t) { /* n/a */ + } finally { + x11Device.unlock(); + } + return glXAvailable; } - - public static VersionNumber getGLXServerVersionNumber(long display) { - int[] major = new int[1]; - int[] minor = new int[1]; - - if (!GLX.glXQueryVersion(display, major, 0, minor, 0)) { - throw new GLException("glXQueryVersion failed"); + + public static String getGLXClientString(X11GraphicsDevice x11Device, int name) { + x11Device.lock(); + try { + return GLX.glXGetClientString(x11Device.getHandle(), name); + } finally { + x11Device.unlock(); + } + } + public static String queryGLXServerString(X11GraphicsDevice x11Device, int screen_idx, int name) { + x11Device.lock(); + try { + return GLX.glXQueryServerString(x11Device.getHandle(), screen_idx, name); + } finally { + x11Device.unlock(); + } + } + public static String queryGLXExtensionsString(X11GraphicsDevice x11Device, int screen_idx) { + x11Device.lock(); + try { + return GLX.glXQueryExtensionsString(x11Device.getHandle(), screen_idx); + } finally { + x11Device.unlock(); } + } - // Work around bugs in ATI's Linux drivers where they report they - // only implement GLX version 1.2 on the server side - if (major[0] == 1 && minor[0] == 2) { - String str = GLX.glXGetClientString(display, GLX.GLX_VERSION); - try { - // e.g. "1.3" - major[0] = Integer.valueOf(str.substring(0, 1)).intValue(); - minor[0] = Integer.valueOf(str.substring(2, 3)).intValue(); - } catch (Exception e) { - major[0] = 1; - minor[0] = 2; - } - } - return new VersionNumber(major[0], minor[0], 0); + public static VersionNumber getGLXServerVersionNumber(X11GraphicsDevice x11Device) { + final IntBuffer major = Buffers.newDirectIntBuffer(1); + final IntBuffer minor = Buffers.newDirectIntBuffer(1); + + x11Device.lock(); + try { + if (!GLX.glXQueryVersion(x11Device.getHandle(), major, minor)) { + throw new GLException("glXQueryVersion failed"); + } + + // Work around bugs in ATI's Linux drivers where they report they + // only implement GLX version 1.2 on the server side + if (major.get(0) == 1 && minor.get(0) == 2) { + String str = GLX.glXGetClientString(x11Device.getHandle(), GLX.GLX_VERSION); + try { + // e.g. "1.3" + major.put(0, Integer.valueOf(str.substring(0, 1)).intValue()); + minor.put(0, Integer.valueOf(str.substring(2, 3)).intValue()); + } catch (Exception e) { + major.put(0, 1); + minor.put(0, 2); + } + } + } finally { + x11Device.unlock(); + } + return new VersionNumber(major.get(0), minor.get(0), 0); } - + public static boolean isMultisampleAvailable(String extensions) { if (extensions != null) { return (extensions.indexOf("GLX_ARB_multisample") >= 0); @@ -105,8 +142,8 @@ public class GLXUtil { public static VersionNumber getClientVersionNumber() { return clientVersionNumber; } - - public static synchronized void initGLXClientDataSingleton(X11GraphicsDevice x11Device) { + + public static synchronized void initGLXClientDataSingleton(X11GraphicsDevice x11Device) { if(null != clientVendorName) { return; // already initialized } @@ -115,10 +152,10 @@ public class GLXUtil { } if(0 == x11Device.getHandle()) { throw new IllegalArgumentException("null X11GraphicsDevice display handle"); - } + } clientMultisampleAvailable = isMultisampleAvailable(GLX.glXGetClientString(x11Device.getHandle(), GLX.GLX_EXTENSIONS)); clientVendorName = GLX.glXGetClientString(x11Device.getHandle(), GLX.GLX_VENDOR); - + int[] major = new int[1]; int[] minor = new int[1]; final String str = GLX.glXGetClientString(x11Device.getHandle(), GLX.GLX_VERSION); diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11DummyGLXDrawable.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11DummyGLXDrawable.java deleted file mode 100644 index a1039e552..000000000 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11DummyGLXDrawable.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 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 met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package jogamp.opengl.x11.glx; - -import javax.media.opengl.*; - -import com.jogamp.nativewindow.WrappedSurface; -import com.jogamp.nativewindow.x11.X11GraphicsDevice; -import com.jogamp.nativewindow.x11.X11GraphicsScreen; - -import jogamp.nativewindow.*; -import jogamp.nativewindow.x11.*; - -public class X11DummyGLXDrawable extends X11OnscreenGLXDrawable { - private static final int f_dim = 64; - private long dummyWindow = 0; - - /** - * Due to the ATI Bug https://bugzilla.mozilla.org/show_bug.cgi?id=486277, - * we cannot switch the Display as we please, - * hence we reuse the target's screen configuration. - */ - public X11DummyGLXDrawable(X11GraphicsScreen screen, GLDrawableFactory factory, GLCapabilitiesImmutable caps) { - super(factory, - new WrappedSurface(X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( - caps, caps, null, screen))); - this.realized = true; - - WrappedSurface ns = (WrappedSurface) getNativeSurface(); - X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)ns.getGraphicsConfiguration(); - - X11GraphicsDevice device = (X11GraphicsDevice) screen.getDevice(); - long dpy = device.getHandle(); - int scrn = screen.getIndex(); - int visualID = config.getXVisualID(); - - dummyWindow = X11Lib.CreateDummyWindow(dpy, scrn, visualID, f_dim, f_dim); - ns.setSurfaceHandle( dummyWindow ); - ns.surfaceSizeChanged(f_dim, f_dim); - - updateHandle(); - } - - public static X11DummyGLXDrawable create(X11GraphicsScreen screen, GLDrawableFactory factory, GLProfile glp) { - GLCapabilities caps = new GLCapabilities(glp); - return new X11DummyGLXDrawable(screen, factory, caps); - } - - public void setSize(int width, int height) { - } - - public int getWidth() { - return 1; - } - - public int getHeight() { - return 1; - } - - protected void destroyImpl() { - if(0!=dummyWindow) { - destroyHandle(); - X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)getNativeSurface().getGraphicsConfiguration(); - X11Lib.DestroyDummyWindow(config.getScreen().getDevice().getHandle(), dummyWindow); - } - } -} diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXContext.java index 700b25662..ff9363ca0 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXContext.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXContext.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,38 +29,42 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.x11.glx; +import java.nio.IntBuffer; + import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.VisualIDHolder; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import jogamp.opengl.GLContextImpl; +import jogamp.nativewindow.WrappedSurface; import jogamp.opengl.GLContextShareSet; -import com.jogamp.nativewindow.WrappedSurface; +import com.jogamp.common.nio.Buffers; import com.jogamp.nativewindow.x11.X11GraphicsScreen; public class X11ExternalGLXContext extends X11GLXContext { - private GLContext lastContext; private X11ExternalGLXContext(Drawable drawable, long ctx) { super(drawable, null); this.contextHandle = ctx; GLContextShareSet.contextCreated(this); - setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_COMPAT); + if( !setGLFunctionAvailability(false, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, false /* withinGLVersionsMapping */) ) { // use GL_VERSION + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } getGLStateTracker().setEnabled(false); // external context usage can't track state in Java } @@ -77,58 +81,51 @@ public class X11ExternalGLXContext extends X11GLXContext { if (drawable == 0) { throw new GLException("Error: attempted to make an external GLDrawable without a drawable/context current"); } - int[] val = new int[1]; - GLX.glXQueryContext(display, ctx, GLX.GLX_SCREEN, val, 0); - X11GraphicsScreen x11Screen = (X11GraphicsScreen) X11GraphicsScreen.createScreenDevice(display, val[0], false); + IntBuffer val = Buffers.newDirectIntBuffer(1); + + int w, h; + GLX.glXQueryDrawable(display, drawable, GLX.GLX_WIDTH, val); + w=val.get(0); + GLX.glXQueryDrawable(display, drawable, GLX.GLX_HEIGHT, val); + h=val.get(0); + + GLX.glXQueryContext(display, ctx, GLX.GLX_SCREEN, val); + X11GraphicsScreen x11Screen = (X11GraphicsScreen) X11GraphicsScreen.createScreenDevice(display, val.get(0), false); - GLX.glXQueryContext(display, ctx, GLX.GLX_FBCONFIG_ID, val, 0); + GLX.glXQueryContext(display, ctx, GLX.GLX_FBCONFIG_ID, val); X11GLXGraphicsConfiguration cfg = null; // sometimes glXQueryContext on an external context gives us a framebuffer config ID // of 0, which doesn't work in a subsequent call to glXChooseFBConfig; if this happens, // create and use a default config (this has been observed when running on CentOS 5.5 inside // of VMWare Server 2.0 with the Mesa 6.5.1 drivers) - if( X11GLXGraphicsConfiguration.GLXFBConfigIDValid(display, x11Screen.getIndex(), val[0]) ) { + if( VisualIDHolder.VID_UNDEFINED == val.get(0) || !X11GLXGraphicsConfiguration.GLXFBConfigIDValid(display, x11Screen.getIndex(), val.get(0)) ) { GLCapabilities glcapsDefault = new GLCapabilities(GLProfile.getDefault()); - cfg = X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(glcapsDefault, glcapsDefault, null, x11Screen); + cfg = X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(glcapsDefault, glcapsDefault, null, x11Screen, VisualIDHolder.VID_UNDEFINED); if(DEBUG) { - System.err.println("X11ExternalGLXContext invalid FBCONFIG_ID "+val[0]+", using default cfg: " + cfg); + System.err.println("X11ExternalGLXContext invalid FBCONFIG_ID "+val.get(0)+", using default cfg: " + cfg); } } else { - cfg = X11GLXGraphicsConfiguration.create(glp, x11Screen, val[0]); + cfg = X11GLXGraphicsConfiguration.create(glp, x11Screen, val.get(0)); } - WrappedSurface ns = new WrappedSurface(cfg); - ns.setSurfaceHandle(drawable); + final WrappedSurface ns = new WrappedSurface(cfg, drawable, w, h, true); return new X11ExternalGLXContext(new Drawable(factory, ns), ctx); } - protected boolean createImpl(GLContextImpl shareWith) { + @Override + protected boolean createImpl(final long shareWithHandle) { return true; } - public int makeCurrent() throws GLException { - // Save last context if necessary to allow external GLContexts to - // talk to other GLContexts created by this library - GLContext cur = getCurrent(); - if (cur != null && cur != this) { - lastContext = cur; - setCurrent(null); - } - return super.makeCurrent(); - } - - public void release() throws GLException { - super.release(); - setCurrent(lastContext); - lastContext = null; - } - + @Override protected void makeCurrentImpl() throws GLException { } + @Override protected void releaseImpl() throws GLException { } + @Override protected void destroyImpl() throws GLException { } @@ -138,14 +135,17 @@ public class X11ExternalGLXContext extends X11GLXContext { super(factory, comp, true); } + @Override public GLContext createContext(GLContext shareWith) { throw new GLException("Should not call this"); } + @Override public int getWidth() { throw new GLException("Should not call this"); } + @Override public int getHeight() { throw new GLException("Should not call this"); } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXDrawable.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXDrawable.java index 4d0276163..650fd31d3 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXDrawable.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11ExternalGLXDrawable.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,25 +28,28 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.x11.glx; +import java.nio.IntBuffer; + import javax.media.nativewindow.NativeSurface; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import jogamp.nativewindow.WrappedSurface; -import com.jogamp.nativewindow.WrappedSurface; +import com.jogamp.common.nio.Buffers; import com.jogamp.nativewindow.x11.X11GraphicsScreen; @@ -69,31 +72,30 @@ public class X11ExternalGLXDrawable extends X11GLXDrawable { if (drawable == 0) { throw new GLException("Error: attempted to make an external GLDrawable without a drawable current"); } - int[] val = new int[1]; - GLX.glXQueryContext(display, context, GLX.GLX_SCREEN, val, 0); - X11GraphicsScreen x11Screen = (X11GraphicsScreen) X11GraphicsScreen.createScreenDevice(display, val[0], false); + IntBuffer val = Buffers.newDirectIntBuffer(1); + + GLX.glXQueryContext(display, context, GLX.GLX_SCREEN, val); + X11GraphicsScreen x11Screen = (X11GraphicsScreen) X11GraphicsScreen.createScreenDevice(display, val.get(0), false); - GLX.glXQueryContext(display, context, GLX.GLX_FBCONFIG_ID, val, 0); - X11GLXGraphicsConfiguration cfg = X11GLXGraphicsConfiguration.create(glp, x11Screen, val[0]); + GLX.glXQueryContext(display, context, GLX.GLX_FBCONFIG_ID, val); + X11GLXGraphicsConfiguration cfg = X11GLXGraphicsConfiguration.create(glp, x11Screen, val.get(0)); int w, h; - GLX.glXQueryDrawable(display, drawable, GLX.GLX_WIDTH, val, 0); - w=val[0]; - GLX.glXQueryDrawable(display, drawable, GLX.GLX_HEIGHT, val, 0); - h=val[0]; + GLX.glXQueryDrawable(display, drawable, GLX.GLX_WIDTH, val); + w=val.get(0); + GLX.glXQueryDrawable(display, drawable, GLX.GLX_HEIGHT, val); + h=val.get(0); - GLX.glXQueryContext(display, context, GLX.GLX_RENDER_TYPE, val, 0); - if ((val[0] & GLX.GLX_RGBA_TYPE) == 0) { + GLX.glXQueryContext(display, context, GLX.GLX_RENDER_TYPE, val); + if ((val.get(0) & GLX.GLX_RGBA_TYPE) == 0) { if (DEBUG) { - System.err.println("X11ExternalGLXDrawable: WARNING: forcing GLX_RGBA_TYPE for newly created contexts (current 0x"+Integer.toHexString(val[0])+")"); + System.err.println("X11ExternalGLXDrawable: WARNING: forcing GLX_RGBA_TYPE for newly created contexts (current 0x"+Integer.toHexString(val.get(0))+")"); } } - WrappedSurface ns = new WrappedSurface(cfg); - ns.setSurfaceHandle(drawable); - ns.surfaceSizeChanged(w, h); - return new X11ExternalGLXDrawable(factory, ns); + return new X11ExternalGLXDrawable(factory, new WrappedSurface(cfg, drawable, w, h, true)); } + @Override public GLContext createContext(GLContext shareWith) { return new Context(this, shareWith); } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLCapabilities.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLCapabilities.java index 96d4c7713..e0b69ffd4 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLCapabilities.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLCapabilities.java @@ -55,10 +55,12 @@ public class X11GLCapabilities extends GLCapabilities { this.fbcfgid = VisualIDHolder.VID_UNDEFINED; } + @Override public Object cloneMutable() { return clone(); } + @Override public Object clone() { try { return super.clone(); @@ -86,9 +88,10 @@ public class X11GLCapabilities extends GLCapabilities { return getFBConfigID(); default: throw new NativeWindowException("Invalid type <"+type+">"); - } + } } - + + @Override public StringBuilder toString(StringBuilder sink) { if(null == sink) { sink = new StringBuilder(); diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java index 2fd8cbcd9..94620c4fc 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -57,25 +57,32 @@ import jogamp.nativewindow.x11.X11Lib; import jogamp.nativewindow.x11.X11Util; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLXExtensions; import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.VersionNumber; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; +import com.jogamp.opengl.GLExtensions; -public abstract class X11GLXContext extends GLContextImpl { +public class X11GLXContext extends GLContextImpl { private static final Map<String, String> functionNameMap; private static final Map<String, String> extensionNameMap; private GLXExt _glXExt; // Table that holds the addresses of the native C-language entry points for // GLX extension functions. private GLXExtProcAddressTable glXExtProcAddressTable; - private int hasSwapIntervalSGI = 0; + /** 1 MESA, 2 SGI, 0 undefined, -1 none */ + private int hasSwapInterval = 0; private int hasSwapGroupNV = 0; // This indicates whether the context we have created is indirect // and therefore requires the toolkit to be locked around all GL // calls rather than just all GLX calls protected boolean isDirect; + protected volatile VersionNumber glXServerVersion; + protected volatile boolean isGLXVersionGreaterEqualOneThree; static { functionNameMap = new HashMap<String, String>(); @@ -83,25 +90,28 @@ public abstract class X11GLXContext extends GLContextImpl { functionNameMap.put("glFreeMemoryNV", "glXFreeMemoryNV"); extensionNameMap = new HashMap<String, String>(); - extensionNameMap.put("GL_ARB_pbuffer", "GLX_SGIX_pbuffer"); - extensionNameMap.put("GL_ARB_pixel_format", "GLX_SGIX_pbuffer"); // good enough + extensionNameMap.put(GLExtensions.ARB_pbuffer, X11GLXDrawableFactory.GLX_SGIX_pbuffer); + extensionNameMap.put(GLExtensions.ARB_pixel_format, X11GLXDrawableFactory.GLX_SGIX_pbuffer); // good enough } X11GLXContext(GLDrawableImpl drawable, GLContext shareWith) { super(drawable, shareWith); } - + @Override - protected void resetStates() { + protected void resetStates(boolean isInit) { // no inner state _glXExt=null; glXExtProcAddressTable = null; - hasSwapIntervalSGI = 0; + hasSwapInterval = 0; hasSwapGroupNV = 0; isDirect = false; - super.resetStates(); + glXServerVersion = null; + isGLXVersionGreaterEqualOneThree = false; + super.resetStates(isInit); } + @Override public final ProcAddressTable getPlatformExtProcAddressTable() { return getGLXExtProcAddressTable(); } @@ -110,6 +120,7 @@ public abstract class X11GLXContext extends GLContextImpl { return glXExtProcAddressTable; } + @Override public Object getPlatformGLExtensions() { return getGLXExt(); } @@ -121,14 +132,29 @@ public abstract class X11GLXContext extends GLContextImpl { return _glXExt; } + @Override protected Map<String, String> getFunctionNameMap() { return functionNameMap; } + @Override protected Map<String, String> getExtensionNameMap() { return extensionNameMap; } - protected final boolean isGLXVersionGreaterEqualOneThree() { - return ((X11GLXDrawableFactory)drawable.getFactoryImpl()).isGLXVersionGreaterEqualOneThree(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice()); + protected final boolean isGLXVersionGreaterEqualOneThree() { // fast-path: use cached boolean + if(null != glXServerVersion) { + return isGLXVersionGreaterEqualOneThree; + } + glXServerVersion = ((X11GLXDrawableFactory)drawable.getFactoryImpl()).getGLXVersionNumber(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice()); + isGLXVersionGreaterEqualOneThree = null != glXServerVersion ? glXServerVersion.compareTo(X11GLXDrawableFactory.versionOneThree) >= 0 : false; + return isGLXVersionGreaterEqualOneThree; } - + protected final void forceGLXVersionOneOne() { + glXServerVersion = X11GLXDrawableFactory.versionOneOne; + isGLXVersionGreaterEqualOneThree = false; + if(DEBUG) { + System.err.println("X11GLXContext.forceGLXVersionNumber: "+glXServerVersion); + } + } + + @Override public final boolean isGLReadDrawableAvailable() { return isGLXVersionGreaterEqualOneThree(); } @@ -138,15 +164,17 @@ public abstract class X11GLXContext extends GLContextImpl { try { if ( isGLXVersionGreaterEqualOneThree() ) { + // System.err.println(getThreadName() +": X11GLXContext.makeCurrent: obj " + toHexString(hashCode()) + " / ctx "+toHexString(contextHandle)+": ctx "+toHexString(ctx)+", [write "+toHexString(writeDrawable)+", read "+toHexString(readDrawable)+"] - switch"); res = GLX.glXMakeContextCurrent(dpy, writeDrawable, readDrawable, ctx); } else if ( writeDrawable == readDrawable ) { + // System.err.println(getThreadName() +": X11GLXContext.makeCurrent: obj " + toHexString(hashCode()) + " / ctx "+toHexString(contextHandle)+": ctx "+toHexString(ctx)+", [write "+toHexString(writeDrawable)+"] - switch"); res = GLX.glXMakeCurrent(dpy, writeDrawable, ctx); } else { // should not happen due to 'isGLReadDrawableAvailable()' query in GLContextImpl throw new InternalError("Given readDrawable but no driver support"); } } catch (RuntimeException re) { - if(TRACE_SWITCH) { + if( DEBUG_TRACE_SWITCH ) { System.err.println(getThreadName()+": Warning: X11GLXContext.glXMakeContextCurrent failed: "+re+", with "+ "dpy "+toHexString(dpy)+ ", write "+toHexString(writeDrawable)+ @@ -158,14 +186,13 @@ public abstract class X11GLXContext extends GLContextImpl { return res; } + @Override protected void destroyContextARBImpl(long ctx) { - X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)drawable.getNativeSurface().getGraphicsConfiguration(); - long display = config.getScreen().getDevice().getHandle(); + final long display = drawable.getNativeSurface().getDisplayHandle(); glXMakeContextCurrent(display, 0, 0, 0); GLX.glXDestroyContext(display, ctx); } - private static final int ctx_arb_attribs_idx_major = 0; private static final int ctx_arb_attribs_idx_minor = 2; private static final int ctx_arb_attribs_idx_flags = 6; @@ -178,7 +205,8 @@ public abstract class X11GLXContext extends GLContextImpl { /* 8 */ 0, 0, /* 10 */ 0 }; - + + @Override protected long createContextARBImpl(long share, boolean direct, int ctp, int major, int minor) { updateGLXProcAddressTable(); GLXExt _glXExt = getGLXExt(); @@ -197,15 +225,15 @@ public abstract class X11GLXContext extends GLContextImpl { IntBuffer attribs = Buffers.newDirectIntBuffer(ctx_arb_attribs_rom); attribs.put(ctx_arb_attribs_idx_major + 1, major); attribs.put(ctx_arb_attribs_idx_minor + 1, minor); - + if ( major > 3 || major == 3 && minor >= 2 ) { attribs.put(ctx_arb_attribs_idx_profile + 0, GLX.GLX_CONTEXT_PROFILE_MASK_ARB); if( ctBwdCompat ) { attribs.put(ctx_arb_attribs_idx_profile + 1, GLX.GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); } else { attribs.put(ctx_arb_attribs_idx_profile + 1, GLX.GLX_CONTEXT_CORE_PROFILE_BIT_ARB); - } - } + } + } if ( major >= 3 ) { int flags = attribs.get(ctx_arb_attribs_idx_flags + 1); @@ -220,20 +248,21 @@ public abstract class X11GLXContext extends GLContextImpl { X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)drawable.getNativeSurface().getGraphicsConfiguration(); AbstractGraphicsDevice device = config.getScreen().getDevice(); - long display = device.getHandle(); + final long display = device.getHandle(); try { // critical path, a remote display might not support this command, // hence we need to catch the X11 Error within this block. + X11Util.setX11ErrorHandler(true, DEBUG ? false : true); // make sure X11 error handler is set X11Lib.XSync(display, false); ctx = _glXExt.glXCreateContextAttribsARB(display, config.getFBConfig(), share, direct, attribs); - X11Lib.XSync(display, false); } catch (RuntimeException re) { if(DEBUG) { Throwable t = new Throwable(getThreadName()+": Info: X11GLXContext.createContextARBImpl glXCreateContextAttribsARB failed with "+getGLVersion(major, minor, ctp, "@creation"), re); t.printStackTrace(); } } + if(0!=ctx) { if (!glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), ctx)) { if(DEBUG) { @@ -253,54 +282,43 @@ public abstract class X11GLXContext extends GLContextImpl { return ctx; } - protected boolean createImpl(GLContextImpl shareWith) { - // covers the whole context creation loop incl createContextARBImpl and destroyContextARBImpl - X11Util.setX11ErrorHandler(true, DEBUG ? false : true); - try { - return createImplRaw(shareWith); - } finally { - X11Util.setX11ErrorHandler(false, false); - } - } - - private boolean createImplRaw(GLContextImpl shareWith) { + @Override + protected boolean createImpl(final long shareWithHandle) { boolean direct = true; // try direct always isDirect = false; // fall back - X11GLXDrawableFactory factory = (X11GLXDrawableFactory)drawable.getFactoryImpl(); - X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)drawable.getNativeSurface().getGraphicsConfiguration(); - AbstractGraphicsDevice device = config.getScreen().getDevice(); - X11GLXContext sharedContext = (X11GLXContext) factory.getOrCreateSharedContextImpl(device); + final X11GLXDrawableFactory factory = (X11GLXDrawableFactory)drawable.getFactoryImpl(); + final X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)drawable.getNativeSurface().getGraphicsConfiguration(); + final AbstractGraphicsDevice device = config.getScreen().getDevice(); + final X11GLXContext sharedContext = (X11GLXContext) factory.getOrCreateSharedContext(device); long display = device.getHandle(); - long share = 0; - if (shareWith != null) { - share = shareWith.getHandle(); - if (share == 0) { - throw new GLException("GLContextShareSet returned an invalid OpenGL context"); - } - direct = GLX.glXIsDirect(display, share); + if ( 0 != shareWithHandle ) { + direct = GLX.glXIsDirect(display, shareWithHandle); } - GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - GLProfile glp = glCaps.getGLProfile(); + final GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final GLProfile glp = glCaps.getGLProfile(); - if(config.getFBConfigID()<0) { - // not able to use FBConfig + if( !config.hasFBConfig() ) { + // not able to use FBConfig -> GLX 1.1 + forceGLXVersionOneOne(); if(glp.isGL3()) { throw new GLException(getThreadName()+": Unable to create OpenGL >= 3.1 context"); } - contextHandle = GLX.glXCreateContext(display, config.getXVisualInfo(), share, direct); - if (contextHandle == 0) { + contextHandle = GLX.glXCreateContext(display, config.getXVisualInfo(), shareWithHandle, direct); + if ( 0 == contextHandle ) { throw new GLException(getThreadName()+": Unable to create context(0)"); } - if (!glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + if ( !glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle) ) { throw new GLException(getThreadName()+": Error making temp context(0) current: display "+toHexString(display)+", context "+toHexString(contextHandle)+", drawable "+drawable); } - setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT); // use GL_VERSION + if( !setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, false /* withinGLVersionsMapping */) ) { // use GL_VERSION + throw new InternalError("setGLFunctionAvailability !strictMatch failed"); + } isDirect = GLX.glXIsDirect(display, contextHandle); if (DEBUG) { - System.err.println(getThreadName() + ": createContextImpl: OK (old-1) share "+share+", direct "+isDirect+"/"+direct); + System.err.println(getThreadName() + ": createContextImpl: OK (old-1) share "+toHexString(shareWithHandle)+", direct "+isDirect+"/"+direct); } return true; } @@ -308,63 +326,64 @@ public abstract class X11GLXContext extends GLContextImpl { boolean createContextARBTried = false; // utilize the shared context's GLXExt in case it was using the ARB method and it already exists - if(null!=sharedContext && sharedContext.isCreatedWithARBMethod()) { - contextHandle = createContextARB(share, direct); + if( null != sharedContext && sharedContext.isCreatedWithARBMethod() ) { + contextHandle = createContextARB(shareWithHandle, direct); createContextARBTried = true; - if (DEBUG && 0!=contextHandle) { - System.err.println(getThreadName() + ": createContextImpl: OK (ARB, using sharedContext) share "+share); + if ( DEBUG && 0 != contextHandle ) { + System.err.println(getThreadName() + ": createContextImpl: OK (ARB, using sharedContext) share "+toHexString(shareWithHandle)); } } - long temp_ctx = 0; - if(0==contextHandle) { + final long temp_ctx; + if( 0 == contextHandle ) { // To use GLX_ARB_create_context, we have to make a temp context current, // so we are able to use GetProcAddress - temp_ctx = GLX.glXCreateNewContext(display, config.getFBConfig(), GLX.GLX_RGBA_TYPE, share, direct); - if (temp_ctx == 0) { + temp_ctx = GLX.glXCreateNewContext(display, config.getFBConfig(), GLX.GLX_RGBA_TYPE, shareWithHandle, direct); + if ( 0 == temp_ctx ) { throw new GLException(getThreadName()+": Unable to create temp OpenGL context(1)"); } - if (!glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), temp_ctx)) { + if ( !glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), temp_ctx) ) { throw new GLException(getThreadName()+": Error making temp context(1) current: display "+toHexString(display)+", context "+toHexString(temp_ctx)+", drawable "+drawable); } - setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT); // use GL_VERSION + setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, false /* withinGLVersionsMapping */); // use GL_VERSION glXMakeContextCurrent(display, 0, 0, 0); // release temp context - if( !createContextARBTried ) { // is*Available calls are valid since setGLFunctionAvailability(..) was called final boolean isProcCreateContextAttribsARBAvailable = isFunctionAvailable("glXCreateContextAttribsARB"); final boolean isExtARBCreateContextAvailable = isExtensionAvailable("GLX_ARB_create_context"); if ( isProcCreateContextAttribsARBAvailable && isExtARBCreateContextAvailable ) { // initial ARB context creation - contextHandle = createContextARB(share, direct); + contextHandle = createContextARB(shareWithHandle, direct); createContextARBTried=true; if (DEBUG) { - if(0!=contextHandle) { - System.err.println(getThreadName() + ": createContextImpl: OK (ARB, initial) share "+share); + if( 0 != contextHandle ) { + System.err.println(getThreadName() + ": createContextImpl: OK (ARB, initial) share "+toHexString(shareWithHandle)); } else { - System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - creation failed - share "+share); + System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - creation failed - share "+toHexString(shareWithHandle)); } } } else if (DEBUG) { - System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - extension not available - share "+share+ + System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - extension not available - share "+toHexString(shareWithHandle)+ ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable); } } + } else { + temp_ctx = 0; } - if(0!=contextHandle) { - if(0!=temp_ctx) { + if( 0 != contextHandle ) { + if( 0 != temp_ctx ) { glXMakeContextCurrent(display, 0, 0, 0); GLX.glXDestroyContext(display, temp_ctx); - if (!glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + if ( !glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle) ) { throw new GLException(getThreadName()+": Cannot make previous verified context current"); } } } else { - if(glp.isGL3()) { + if( glp.isGL3() ) { glXMakeContextCurrent(display, 0, 0, 0); GLX.glXDestroyContext(display, temp_ctx); - throw new GLException(getThreadName()+": X11GLXContext.createContextImpl ctx !ARB, context > GL2 requested - requested: "+glp+", current: "+getGLVersion()+", "); + throw new GLException(getThreadName()+": X11GLXContext.createContextImpl ctx !ARB, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glp+", current: "+getGLVersion()); } if(DEBUG) { System.err.println(getThreadName()+": X11GLXContext.createContextImpl failed, fall back to !ARB context "+getGLVersion()); @@ -372,53 +391,53 @@ public abstract class X11GLXContext extends GLContextImpl { // continue with temp context for GL <= 3.0 contextHandle = temp_ctx; - if (!glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + if ( !glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle) ) { glXMakeContextCurrent(display, 0, 0, 0); GLX.glXDestroyContext(display, temp_ctx); throw new GLException(getThreadName()+": Error making context(1) current: display "+toHexString(display)+", context "+toHexString(contextHandle)+", drawable "+drawable); } if (DEBUG) { - System.err.println(getThreadName() + ": createContextImpl: OK (old-2) share "+share); + System.err.println(getThreadName() + ": createContextImpl: OK (old-2) share "+toHexString(shareWithHandle)); } } isDirect = GLX.glXIsDirect(display, contextHandle); if (DEBUG) { System.err.println(getThreadName() + ": createContextImpl: OK direct "+isDirect+"/"+direct); } + return true; } + @Override protected void makeCurrentImpl() throws GLException { long dpy = drawable.getNativeSurface().getDisplayHandle(); if (GLX.glXGetCurrentContext() != contextHandle) { - X11Util.setX11ErrorHandler(true, DEBUG ? false : true); - try { - if (!glXMakeContextCurrent(dpy, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { - throw new GLException(getThreadName()+": Error making context current: "+this); - } - } finally { - X11Util.setX11ErrorHandler(false, false); + if (!glXMakeContextCurrent(dpy, drawable.getHandle(), drawableRead.getHandle(), contextHandle)) { + throw new GLException("Error making context " + toHexString(contextHandle) + + " current on Thread " + getThreadName() + + " with display " + toHexString(dpy) + + ", drawableWrite " + toHexString(drawable.getHandle()) + + ", drawableRead "+ toHexString(drawableRead.getHandle()) + + " - " + this); } } } + @Override protected void releaseImpl() throws GLException { long display = drawable.getNativeSurface().getDisplayHandle(); - X11Util.setX11ErrorHandler(true, DEBUG ? false : true); - try { - if (!glXMakeContextCurrent(display, 0, 0, 0)) { - throw new GLException(getThreadName()+": Error freeing OpenGL context"); - } - } finally { - X11Util.setX11ErrorHandler(false, false); + if (!glXMakeContextCurrent(display, 0, 0, 0)) { + throw new GLException(getThreadName()+": Error freeing OpenGL context"); } } + @Override protected void destroyImpl() throws GLException { - GLX.glXDestroyContext(drawable.getNativeSurface().getDisplayHandle(), contextHandle); + destroyContextARBImpl(contextHandle); } + @Override protected void copyImpl(GLContext source, int mask) throws GLException { long dst = getHandle(); long src = source.getHandle(); @@ -430,6 +449,7 @@ public abstract class X11GLXContext extends GLContextImpl { // Should check for X errors and raise GLException } + @Override protected final void updateGLXProcAddressTable() { final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); @@ -458,65 +478,78 @@ public abstract class X11GLXContext extends GLContextImpl { } } + @Override protected final StringBuilder getPlatformExtensionsStringImpl() { - StringBuilder sb = new StringBuilder(); - if (DEBUG) { - System.err.println("GLX Version client version "+ GLXUtil.getClientVersionNumber()+ - ", server: "+ - ((X11GLXDrawableFactory)drawable.getFactoryImpl()).getGLXVersionNumber(drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice())); - } final NativeSurface ns = drawable.getNativeSurface(); - if(((X11GLXDrawableFactory)drawable.getFactoryImpl()).isGLXVersionGreaterEqualOneOne(ns.getGraphicsConfiguration().getScreen().getDevice())) { - { - final String ret = GLX.glXGetClientString(ns.getDisplayHandle(), GLX.GLX_EXTENSIONS); - if (DEBUG) { - System.err.println("GLX extensions (glXGetClientString): " + ret); - } - sb.append(ret).append(" "); + final X11GraphicsDevice x11Device = (X11GraphicsDevice) ns.getGraphicsConfiguration().getScreen().getDevice(); + StringBuilder sb = new StringBuilder(); + x11Device.lock(); + try{ + if (DEBUG) { + System.err.println("GLX Version client version "+ GLXUtil.getClientVersionNumber()+ + ", server: "+ GLXUtil.getGLXServerVersionNumber(x11Device)); } - { - final String ret = GLX.glXQueryExtensionsString(ns.getDisplayHandle(), ns.getScreenIndex()); - if (DEBUG) { - System.err.println("GLX extensions (glXQueryExtensionsString): " + ret); + if(((X11GLXDrawableFactory)drawable.getFactoryImpl()).isGLXVersionGreaterEqualOneOne(x11Device)) { + { + final String ret = GLX.glXGetClientString(x11Device.getHandle(), GLX.GLX_EXTENSIONS); + if (DEBUG) { + System.err.println("GLX extensions (glXGetClientString): " + ret); + } + sb.append(ret).append(" "); } - sb.append(ret).append(" "); - } - { - final String ret = GLX.glXQueryServerString(ns.getDisplayHandle(), ns.getScreenIndex(), GLX.GLX_EXTENSIONS); - if (DEBUG) { - System.err.println("GLX extensions (glXQueryServerString): " + ret); + { + final String ret = GLX.glXQueryExtensionsString(x11Device.getHandle(), ns.getScreenIndex()); + if (DEBUG) { + System.err.println("GLX extensions (glXQueryExtensionsString): " + ret); + } + sb.append(ret).append(" "); + } + { + final String ret = GLX.glXQueryServerString(x11Device.getHandle(), ns.getScreenIndex(), GLX.GLX_EXTENSIONS); + if (DEBUG) { + System.err.println("GLX extensions (glXQueryServerString): " + ret); + } + sb.append(ret).append(" "); } - sb.append(ret).append(" "); } + } finally { + x11Device.unlock(); } return sb; } - public boolean isExtensionAvailable(String glExtensionName) { - if (glExtensionName.equals("GL_ARB_pbuffer") || - glExtensionName.equals("GL_ARB_pixel_format")) { - return getGLDrawable().getFactory().canCreateGLPbuffer( - drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice() ); - } - return super.isExtensionAvailable(glExtensionName); - } - @Override protected boolean setSwapIntervalImpl(int interval) { - X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)drawable.getNativeSurface().getGraphicsConfiguration(); - GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); - if(!glCaps.isOnscreen()) { return false; } + if( !drawable.getChosenGLCapabilities().isOnscreen() ) { return false; } - GLXExt glXExt = getGLXExt(); - if(0==hasSwapIntervalSGI) { + final GLXExt glXExt = getGLXExt(); + if(0==hasSwapInterval) { try { - hasSwapIntervalSGI = glXExt.isExtensionAvailable("GLX_SGI_swap_control")?1:-1; - } catch (Throwable t) { hasSwapIntervalSGI=1; } + /** Same impl. .. + if( glXExt.isExtensionAvailable(GLXExtensions.GLX_MESA_swap_control) ) { + if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval using: "+GLXExtensions.GLX_MESA_swap_control); } + hasSwapInterval = 1; + } else */ + if ( glXExt.isExtensionAvailable(GLXExtensions.GLX_SGI_swap_control) ) { + if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval using: "+GLXExtensions.GLX_SGI_swap_control); } + hasSwapInterval = 2; + } else { + hasSwapInterval = -1; + } + } catch (Throwable t) { hasSwapInterval=-1; } } - if (hasSwapIntervalSGI>0) { + /* try { + switch( hasSwapInterval ) { + case 1: + return 0 == glXExt.glXSwapIntervalMESA(interval); + case 2: + return 0 == glXExt.glXSwapIntervalSGI(interval); + } + } catch (Throwable t) { hasSwapInterval = -1; } */ + if (2 == hasSwapInterval) { try { return 0 == glXExt.glXSwapIntervalSGI(interval); - } catch (Throwable t) { hasSwapIntervalSGI=-1; } + } catch (Throwable t) { hasSwapInterval=-1; } } return false; } @@ -524,15 +557,15 @@ public abstract class X11GLXContext extends GLContextImpl { private final int initSwapGroupImpl(GLXExt glXExt) { if(0==hasSwapGroupNV) { try { - hasSwapGroupNV = glXExt.isExtensionAvailable("GLX_NV_swap_group")?1:-1; + hasSwapGroupNV = glXExt.isExtensionAvailable(GLXExtensions.GLX_NV_swap_group)?1:-1; } catch (Throwable t) { hasSwapGroupNV=1; } if(DEBUG) { - System.err.println("initSwapGroupImpl: hasSwapGroupNV: "+hasSwapGroupNV); + System.err.println("initSwapGroupImpl: "+GLXExtensions.GLX_NV_swap_group+": "+hasSwapGroupNV); } } return hasSwapGroupNV; } - + @Override protected final boolean queryMaxSwapGroupsImpl(int[] maxGroups, int maxGroups_offset, int[] maxBarriers, int maxBarriers_offset) { @@ -541,16 +574,20 @@ public abstract class X11GLXContext extends GLContextImpl { if (initSwapGroupImpl(glXExt)>0) { final NativeSurface ns = drawable.getNativeSurface(); try { - if( glXExt.glXQueryMaxSwapGroupsNV(ns.getDisplayHandle(), ns.getScreenIndex(), - maxGroups, maxGroups_offset, - maxBarriers, maxBarriers_offset) ) { + final IntBuffer maxGroupsNIO = Buffers.newDirectIntBuffer(maxGroups.length - maxGroups_offset); + final IntBuffer maxBarriersNIO = Buffers.newDirectIntBuffer(maxBarriers.length - maxBarriers_offset); + + if( glXExt.glXQueryMaxSwapGroupsNV(ns.getDisplayHandle(), ns.getScreenIndex(), + maxGroupsNIO, maxBarriersNIO) ) { + maxGroupsNIO.get(maxGroups, maxGroups_offset, maxGroupsNIO.remaining()); + maxBarriersNIO.get(maxGroups, maxGroups_offset, maxBarriersNIO.remaining()); res = true; } } catch (Throwable t) { hasSwapGroupNV=-1; } } return res; } - + @Override protected final boolean joinSwapGroupImpl(int group) { boolean res = false; @@ -565,7 +602,7 @@ public abstract class X11GLXContext extends GLContextImpl { } return res; } - + @Override protected final boolean bindSwapBarrierImpl(int group, int barrier) { boolean res = false; @@ -577,34 +614,20 @@ public abstract class X11GLXContext extends GLContextImpl { } } catch (Throwable t) { hasSwapGroupNV=-1; } } - return res; + return res; } @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); } - public int getOffscreenContextPixelDataType() { - throw new GLException("Should not call this"); - } - - public int getOffscreenContextReadBuffer() { - throw new GLException("Should not call this"); - } - - public boolean offscreenImageNeedsVerticalFlip() { - throw new GLException("Should not call this"); - } - - public void bindPbufferToTexture() { - throw new GLException("Should not call this"); - } - - public void releasePbufferFromTexture() { - throw new GLException("Should not call this"); + @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/X11GLXDrawable.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawable.java index 61f2ef9c9..155c00c4c 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawable.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,45 +29,50 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.x11.glx; -import javax.media.nativewindow.*; -import javax.media.opengl.*; +import javax.media.nativewindow.NativeSurface; +import javax.media.opengl.GLDrawableFactory; -import jogamp.opengl.*; +import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLDynamicLookupHelper; public abstract class X11GLXDrawable extends GLDrawableImpl { protected X11GLXDrawable(GLDrawableFactory factory, NativeSurface comp, boolean realized) { super(factory, comp, realized); } + @Override public GLDynamicLookupHelper getGLDynamicLookupHelper() { return getFactoryImpl().getGLDynamicLookupHelper(0); } + @Override protected void setRealizedImpl() { if(realized) { X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)getNativeSurface().getGraphicsConfiguration(); config.updateGraphicsConfiguration(); if (DEBUG) { - System.err.println("X11GLXDrawable.setRealized(true): "+config); + System.err.println(getThreadName()+": X11GLXDrawable.setRealized(true): "+config); } } } - protected final void swapBuffersImpl() { - // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() - GLX.glXSwapBuffers(getNativeSurface().getDisplayHandle(), getHandle()); + @Override + protected final void swapBuffersImpl(boolean doubleBuffered) { + if(doubleBuffered) { + GLX.glXSwapBuffers(getNativeSurface().getDisplayHandle(), getHandle()); + } } //--------------------------------------------------------------------------- diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java index 092d3439e..f7938f463 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -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; @@ -47,16 +49,19 @@ import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import javax.media.opengl.GLProfile.ShutdownType; +import jogamp.nativewindow.WrappedSurface; +import jogamp.nativewindow.x11.X11DummyUpstreamSurfaceHook; import jogamp.nativewindow.x11.X11Lib; import jogamp.nativewindow.x11.X11Util; import jogamp.opengl.DesktopGLDynamicLookupHelper; @@ -64,60 +69,78 @@ import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableFactoryImpl; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.GLDynamicLookupHelper; +import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.SharedResourceRunner; import com.jogamp.common.util.VersionNumber; -import com.jogamp.nativewindow.WrappedSurface; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; +import com.jogamp.opengl.GLRendererQuirks; public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { - + public static final VersionNumber versionOneZero = new VersionNumber(1, 0, 0); public static final VersionNumber versionOneOne = new VersionNumber(1, 1, 0); public static final VersionNumber versionOneTwo = new VersionNumber(1, 2, 0); public static final VersionNumber versionOneThree = new VersionNumber(1, 3, 0); public static final VersionNumber versionOneFour = new VersionNumber(1, 4, 0); + static final String GLX_SGIX_pbuffer = "GLX_SGIX_pbuffer"; + private static DesktopGLDynamicLookupHelper x11GLXDynamicLookupHelper = null; - + public X11GLXDrawableFactory() { 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>() { + @Override + 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); - } + } ); } } - + defaultDevice = new X11GraphicsDevice(X11Util.getNullDisplayName(), AbstractGraphicsDevice.DEFAULT_UNIT); - + if(null!=x11GLXDynamicLookupHelper) { // Register our GraphicsConfigurationFactory implementations // The act of constructing them causes them to be registered X11GLXGraphicsConfigurationFactory.registerFactory(); - + sharedMap = new HashMap<String, SharedResourceRunner.Resource>(); - + // Init shared resources off thread // Will be released via ShutdownHook sharedResourceRunner = new SharedResourceRunner(new SharedResourceImplementation()); sharedResourceRunner.start(); - } + } + } + + @Override + protected final boolean isComplete() { + return null != x11GLXDynamicLookupHelper; } - protected final void destroy(ShutdownType shutdownType) { + @Override + protected final void shutdownImpl() { + if( DEBUG ) { + System.err.println("X11GLXDrawableFactory.shutdown"); + } if(null != sharedResourceRunner) { sharedResourceRunner.stop(); sharedResourceRunner = null; @@ -129,57 +152,65 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { defaultDevice = null; /** * Pulling away the native library may cause havoc .. - * - if(ShutdownType.COMPLETE == shutdownType && null != x11GLXDynamicLookupHelper) { - x11GLXDynamicLookupHelper.destroy(); - x11GLXDynamicLookupHelper = null; - } */ - - // Don't really close pending Display connections, - // since this may trigger a JVM exception - X11Util.shutdown( false, DEBUG ); + * + x11GLXDynamicLookupHelper.destroy(); + */ + x11GLXDynamicLookupHelper = null; } + @Override public final GLDynamicLookupHelper getGLDynamicLookupHelper(int profile) { return x11GLXDynamicLookupHelper; } private X11GraphicsDevice defaultDevice; private SharedResourceRunner sharedResourceRunner; - private HashMap<String /* connection */, SharedResourceRunner.Resource> sharedMap; + private HashMap<String /* connection */, SharedResourceRunner.Resource> sharedMap; static class SharedResource implements SharedResourceRunner.Resource { + private final String glXServerVendorName; + private final boolean isGLXServerVendorATI; + private final boolean isGLXServerVendorNVIDIA; + private final VersionNumber glXServerVersion; + private final boolean glXServerVersionOneOneCapable; + private final boolean glXServerVersionOneThreeCapable; + private final boolean glXMultisampleAvailable; X11GraphicsDevice device; X11GraphicsScreen screen; - X11DummyGLXDrawable drawable; - X11GLXContext context; - String glXServerVendorName; - boolean isGLXServerVendorATI; - boolean isGLXServerVendorNVIDIA; - VersionNumber glXServerVersion; - boolean glXServerVersionOneOneCapable; - boolean glXServerVersionOneThreeCapable; - boolean glXMultisampleAvailable; + GLDrawableImpl drawable; + GLContextImpl context; SharedResource(X11GraphicsDevice dev, X11GraphicsScreen scrn, - X11DummyGLXDrawable draw, X11GLXContext ctx, + GLDrawableImpl draw, GLContextImpl ctx, VersionNumber glXServerVer, String glXServerVendor, boolean glXServerMultisampleAvail) { device = dev; screen = scrn; drawable = draw; context = ctx; glXServerVersion = glXServerVer; - glXServerVersionOneOneCapable = glXServerVersion.compareTo(versionOneOne) >= 0 ; - glXServerVersionOneThreeCapable = glXServerVersion.compareTo(versionOneThree) >= 0 ; + glXServerVersionOneOneCapable = glXServerVersion.compareTo(versionOneOne) >= 0 ; + glXServerVersionOneThreeCapable = glXServerVersion.compareTo(versionOneThree) >= 0 ; glXServerVendorName = glXServerVendor; isGLXServerVendorATI = GLXUtil.isVendorATI(glXServerVendorName); isGLXServerVendorNVIDIA = GLXUtil.isVendorNVIDIA(glXServerVendorName); glXMultisampleAvailable = glXServerMultisampleAvail; } + @Override + public final boolean isValid() { + return null != context; + } + @Override final public AbstractGraphicsDevice getDevice() { return device; } + @Override final public AbstractGraphicsScreen getScreen() { return screen; } + @Override final public GLDrawableImpl getDrawable() { return drawable; } + @Override final public GLContextImpl getContext() { return context; } + @Override + public GLRendererQuirks getRendererQuirks() { + return null != context ? context.getRendererQuirks() : null; + } final String getGLXVendorName() { return glXServerVendorName; } final boolean isGLXVendorATI() { return isGLXServerVendorATI; } @@ -191,60 +222,72 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } class SharedResourceImplementation implements SharedResourceRunner.Implementation { + @Override public void clear() { - synchronized(sharedMap) { - sharedMap.clear(); - } + sharedMap.clear(); } + @Override public SharedResourceRunner.Resource mapPut(String connection, SharedResourceRunner.Resource resource) { - synchronized(sharedMap) { - return sharedMap.put(connection, resource); - } + return sharedMap.put(connection, resource); } + @Override public SharedResourceRunner.Resource mapGet(String connection) { - synchronized(sharedMap) { - return sharedMap.get(connection); - } + return sharedMap.get(connection); } + @Override public Collection<SharedResourceRunner.Resource> mapValues() { - synchronized(sharedMap) { - return sharedMap.values(); + return sharedMap.values(); + } + + @Override + public boolean isDeviceSupported(String connection) { + final boolean res; + final X11GraphicsDevice x11Device = new X11GraphicsDevice(X11Util.openDisplay(connection), AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); + x11Device.lock(); + try { + res = GLXUtil.isGLXAvailableOnServer(x11Device); + } finally { + x11Device.unlock(); + x11Device.close(); + } + if(DEBUG) { + System.err.println("GLX "+(res ? "is" : "not")+" available on device/server: "+x11Device); } + return res; } + @Override public SharedResourceRunner.Resource createSharedResource(String connection) { - X11GraphicsDevice sharedDevice = - new X11GraphicsDevice(X11Util.openDisplay(connection), AbstractGraphicsDevice.DEFAULT_UNIT, - true); // own non-shared display connection, no locking - // new X11GraphicsDevice(X11Util.openDisplay(connection), AbstractGraphicsDevice.DEFAULT_UNIT, - // NativeWindowFactory.getNullToolkitLock(), true); // own non-shared display connection, no locking + final X11GraphicsDevice sharedDevice = new X11GraphicsDevice(X11Util.openDisplay(connection), AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); sharedDevice.lock(); try { - if(!GLXUtil.isGLXAvailableOnServer(sharedDevice)) { - throw new GLException("GLX not available on device/server: "+sharedDevice); - } - GLXUtil.initGLXClientDataSingleton(sharedDevice); + final X11GraphicsScreen sharedScreen = new X11GraphicsScreen(sharedDevice, sharedDevice.getDefaultScreen()); + + GLXUtil.initGLXClientDataSingleton(sharedDevice); final String glXServerVendorName = GLX.glXQueryServerString(sharedDevice.getHandle(), 0, GLX.GLX_VENDOR); - final VersionNumber glXServerVersion = GLXUtil.getGLXServerVersionNumber(sharedDevice.getHandle()); - final boolean glXServerMultisampleAvailable = GLXUtil.isMultisampleAvailable(GLX.glXQueryServerString(sharedDevice.getHandle(), 0, GLX.GLX_EXTENSIONS)); - if(X11Util.ATI_HAS_XCLOSEDISPLAY_BUG && GLXUtil.isVendorATI(glXServerVendorName)) { - X11Util.setMarkAllDisplaysUnclosable(true); - X11Util.markDisplayUncloseable(sharedDevice.getHandle()); - } - X11GraphicsScreen sharedScreen = new X11GraphicsScreen(sharedDevice, 0); + final boolean glXServerMultisampleAvailable = GLXUtil.isMultisampleAvailable(GLX.glXQueryServerString(sharedDevice.getHandle(), 0, GLX.GLX_EXTENSIONS)); - GLProfile glp = GLProfile.get(sharedDevice, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false); + final GLProfile glp = GLProfile.get(sharedDevice, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false); if (null == glp) { throw new GLException("Couldn't get default GLProfile for device: "+sharedDevice); } - X11DummyGLXDrawable sharedDrawable = X11DummyGLXDrawable.create(sharedScreen, X11GLXDrawableFactory.this, glp); - if (null == sharedDrawable) { - throw new GLException("Couldn't create shared drawable for screen: "+sharedScreen+", "+glp); + + final GLCapabilitiesImmutable caps = new GLCapabilities(glp); + final GLDrawableImpl sharedDrawable = createOnscreenDrawableImpl(createDummySurfaceImpl(sharedDevice, false, caps, caps, null, 64, 64)); + sharedDrawable.setRealized(true); + final X11GLCapabilities chosenCaps = (X11GLCapabilities) sharedDrawable.getChosenGLCapabilities(); + final boolean glxForcedOneOne = !chosenCaps.hasFBConfig(); + final VersionNumber glXServerVersion; + if( glxForcedOneOne ) { + glXServerVersion = versionOneOne; + } else { + glXServerVersion = GLXUtil.getGLXServerVersionNumber(sharedDevice); } - X11GLXContext sharedContext = (X11GLXContext) sharedDrawable.createContext(null); + final GLContextImpl sharedContext = (GLContextImpl) sharedDrawable.createContext(null); if (null == sharedContext) { throw new GLException("Couldn't create shared context for drawable: "+sharedDrawable); } + boolean madeCurrent = false; sharedContext.makeCurrent(); try { @@ -252,19 +295,22 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } finally { sharedContext.release(); } + if( sharedContext.hasRendererQuirk( GLRendererQuirks.DontCloseX11Display ) ) { + X11Util.markAllDisplaysUnclosable(); + } if (DEBUG) { System.err.println("SharedDevice: " + sharedDevice); System.err.println("SharedScreen: " + sharedScreen); System.err.println("SharedContext: " + sharedContext + ", madeCurrent " + madeCurrent); System.err.println("GLX Server Vendor: " + glXServerVendorName); - System.err.println("GLX Server Version: " + glXServerVersion); + System.err.println("GLX Server Version: " + glXServerVersion + ", forced "+glxForcedOneOne); System.err.println("GLX Server Multisample: " + glXServerMultisampleAvailable); System.err.println("GLX Client Vendor: " + GLXUtil.getClientVendorName()); System.err.println("GLX Client Version: " + GLXUtil.getClientVersionNumber()); System.err.println("GLX Client Multisample: " + GLXUtil.isClientMultisampleAvailable()); } - return new SharedResource(sharedDevice, sharedScreen, sharedDrawable, sharedContext, - glXServerVersion, glXServerVendorName, + return new SharedResource(sharedDevice, sharedScreen, sharedDrawable, sharedContext, + glXServerVersion, glXServerVendorName, glXServerMultisampleAvailable && GLXUtil.isClientMultisampleAvailable()); } catch (Throwable t) { throw new GLException("X11GLXDrawableFactory - Could not initialize shared resources for "+connection, t); @@ -273,6 +319,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } } + @Override public void releaseSharedResource(SharedResourceRunner.Resource shared) { SharedResource sr = (SharedResource) shared; if (DEBUG) { @@ -285,14 +332,14 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } if (null != sr.context) { - // may cause JVM SIGSEGV: - sr.context.destroy(); + // may cause JVM SIGSEGV, or freeze (ATI fglrx 3-6-beta2 32on64 shared ctx): + sr.context.destroy(); // will also pull the dummy MutableSurface sr.context = null; } if (null != sr.drawable) { // may cause JVM SIGSEGV: - sr.drawable.destroy(); + sr.drawable.setRealized(false); sr.drawable = null; } @@ -308,10 +355,12 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } } + @Override public final AbstractGraphicsDevice getDefaultDevice() { return defaultDevice; } + @Override public final boolean getIsDeviceCompatible(AbstractGraphicsDevice device) { if(null != x11GLXDynamicLookupHelper && device instanceof X11GraphicsDevice) { return true; @@ -319,64 +368,38 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { return false; } + @Override protected final Thread getSharedResourceThread() { return sharedResourceRunner.start(); } - - protected final boolean createSharedResource(AbstractGraphicsDevice device) { - try { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); - if(null!=sr) { - return null != sr.getContext(); - } - } catch (GLException gle) { - if(DEBUG) { - System.err.println("Catched Exception while X11GLX Shared Resource initialization"); - gle.printStackTrace(); - } - } - return false; - } - - protected final GLContext getOrCreateSharedContextImpl(AbstractGraphicsDevice device) { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); - if(null!=sr) { - return sr.getContext(); - } - return null; - } - protected AbstractGraphicsDevice getOrCreateSharedDeviceImpl(AbstractGraphicsDevice device) { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); - if(null!=sr) { - return sr.getDevice(); - } - return null; + @Override + protected final SharedResource getOrCreateSharedResourceImpl(AbstractGraphicsDevice device) { + return (SharedResource) sharedResourceRunner.getOrCreateShared(device); } protected final long getOrCreateSharedDpy(AbstractGraphicsDevice device) { - SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device); + final SharedResourceRunner.Resource sr = getOrCreateSharedResource( device ); if(null!=sr) { return sr.getDevice().getHandle(); } return 0; } - SharedResource getOrCreateSharedResource(AbstractGraphicsDevice device) { - return (SharedResource) sharedResourceRunner.getOrCreateShared(device); - } - + @Override protected List<GLCapabilitiesImmutable> getAvailableCapabilitiesImpl(AbstractGraphicsDevice device) { return X11GLXGraphicsConfigurationFactory.getAvailableCapabilities(this, device); } + @Override protected final GLDrawableImpl createOnscreenDrawableImpl(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); } - return new X11OnscreenGLXDrawable(this, target); + return new X11OnscreenGLXDrawable(this, target, false); } + @Override protected final GLDrawableImpl createOffscreenDrawableImpl(NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); @@ -412,7 +435,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } public final boolean isGLXMultisampleAvailable(AbstractGraphicsDevice device) { - if(null != device) { + if(null != device) { SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared(device); if(null!=sr) { return sr.isGLXMultisampleAvailable(); @@ -422,47 +445,48 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { } public final VersionNumber getGLXVersionNumber(AbstractGraphicsDevice device) { - if(null != device) { + if(null != device) { SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared(device); if(null!=sr) { return sr.getGLXVersion(); } if( device instanceof X11GraphicsDevice ) { - return GLXUtil.getGLXServerVersionNumber(device.getHandle()); + return GLXUtil.getGLXServerVersionNumber((X11GraphicsDevice)device); } } return null; } - + public final boolean isGLXVersionGreaterEqualOneOne(AbstractGraphicsDevice device) { - if(null != device) { + if(null != device) { SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared(device); if(null!=sr) { return sr.isGLXVersionGreaterEqualOneOne(); } if( device instanceof X11GraphicsDevice ) { - final VersionNumber glXServerVersion = GLXUtil.getGLXServerVersionNumber(device.getHandle()); + final VersionNumber glXServerVersion = GLXUtil.getGLXServerVersionNumber((X11GraphicsDevice)device); return glXServerVersion.compareTo(versionOneOne) >= 0; } } return false; } - + public final boolean isGLXVersionGreaterEqualOneThree(AbstractGraphicsDevice device) { - if(null != device) { + if(null != device) { SharedResource sr = (SharedResource) sharedResourceRunner.getOrCreateShared(device); if(null!=sr) { return sr.isGLXVersionGreaterEqualOneThree(); } if( device instanceof X11GraphicsDevice ) { - final VersionNumber glXServerVersion = GLXUtil.getGLXServerVersionNumber(device.getHandle()); + final VersionNumber glXServerVersion = GLXUtil.getGLXServerVersionNumber((X11GraphicsDevice)device); return glXServerVersion.compareTo(versionOneThree) >= 0; } } return false; } - public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device) { + @Override + public final boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp) { if(null == device) { SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(defaultDevice); if(null!=sr) { @@ -472,68 +496,72 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { return isGLXVersionGreaterEqualOneThree(device); } - protected final NativeSurface createOffscreenSurfaceImpl(AbstractGraphicsDevice deviceReq, - GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsRequested, - GLCapabilitiesChooser chooser, - int width, int height) { - if(null == deviceReq) { - throw new InternalError("deviceReq is null"); - } - final SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(deviceReq); - if(null==sr) { - throw new InternalError("No SharedResource for: "+deviceReq); + @Override + protected final ProxySurface createMutableSurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable capsChosen, + GLCapabilitiesImmutable capsRequested, + GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstreamHook) { + final X11GraphicsDevice device; + if( createNewDevice || !(deviceReq instanceof X11GraphicsDevice) ) { + device = new X11GraphicsDevice(X11Util.openDisplay(deviceReq.getConnection()), deviceReq.getUnitID(), true /* owner */); + } else { + device = (X11GraphicsDevice) deviceReq; } - final X11GraphicsScreen sharedScreen = (X11GraphicsScreen) sr.getScreen(); - final AbstractGraphicsDevice sharedDevice = sharedScreen.getDevice(); // should be same .. - - // create screen/device pair - Null X11 locking, due to private non-shared Display handle - final X11GraphicsDevice device = new X11GraphicsDevice(X11Util.openDisplay(sharedDevice.getConnection()), AbstractGraphicsDevice.DEFAULT_UNIT, NativeWindowFactory.getNullToolkitLock(), true); - final X11GraphicsScreen screen = new X11GraphicsScreen(device, sharedScreen.getIndex()); - - WrappedSurface ns = new WrappedSurface( - X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen) ); - if(ns != null) { - ns.surfaceSizeChanged(width, height); + final X11GraphicsScreen screen = new X11GraphicsScreen(device, device.getDefaultScreen()); + final X11GLXGraphicsConfiguration config = X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen, VisualIDHolder.VID_UNDEFINED); + if(null == config) { + throw new GLException("Choosing GraphicsConfiguration failed w/ "+capsChosen+" on "+screen); } - return ns; + return new WrappedSurface(config, 0, upstreamHook, createNewDevice); } - protected final ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice adevice, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { - // FIXME device/windowHandle -> screen ?! - X11GraphicsDevice device = (X11GraphicsDevice) adevice; - X11GraphicsScreen screen = new X11GraphicsScreen(device, 0); - X11GLXGraphicsConfiguration cfg = X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen); - WrappedSurface ns = new WrappedSurface(cfg, windowHandle); - return ns; + @Override + public final ProxySurface createDummySurfaceImpl(AbstractGraphicsDevice deviceReq, boolean createNewDevice, + GLCapabilitiesImmutable chosenCaps, GLCapabilitiesImmutable requestedCaps, GLCapabilitiesChooser chooser, int width, int height) { + chosenCaps = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps); + return createMutableSurfaceImpl(deviceReq, createNewDevice, chosenCaps, requestedCaps, chooser, new X11DummyUpstreamSurfaceHook(width, height)); } - + + @Override + protected final ProxySurface createProxySurfaceImpl(AbstractGraphicsDevice deviceReq, int screenIdx, long windowHandle, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream) { + final X11GraphicsDevice device = new X11GraphicsDevice(X11Util.openDisplay(deviceReq.getConnection()), deviceReq.getUnitID(), true /* owner */); + final X11GraphicsScreen screen = new X11GraphicsScreen(device, screenIdx); + final int xvisualID = X11Lib.GetVisualIDFromWindow(device.getHandle(), windowHandle); + if(VisualIDHolder.VID_UNDEFINED == xvisualID) { + throw new GLException("Undefined VisualID of window 0x"+Long.toHexString(windowHandle)+", window probably invalid"); + } + if(DEBUG) { + System.err.println("X11GLXDrawableFactory.createProxySurfaceImpl 0x"+Long.toHexString(windowHandle)+": visualID 0x"+Integer.toHexString(xvisualID)); + } + final X11GLXGraphicsConfiguration cfg = X11GLXGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen, xvisualID); + if(DEBUG) { + System.err.println("X11GLXDrawableFactory.createProxySurfaceImpl 0x"+Long.toHexString(windowHandle)+": "+cfg); + } + return new WrappedSurface(cfg, windowHandle, upstream, true); + } + + @Override protected final GLContext createExternalGLContextImpl() { return X11ExternalGLXContext.create(this, null); } + @Override public final boolean canCreateExternalGLDrawable(AbstractGraphicsDevice device) { - return canCreateGLPbuffer(device); + return canCreateGLPbuffer(device, null /* GLProfile not used for query on X11 */); } + @Override protected final GLDrawable createExternalGLDrawableImpl() { return X11ExternalGLXDrawable.create(this, null); } - public final boolean canCreateContextOnJava2DSurface(AbstractGraphicsDevice device) { - return false; - } - - public final GLContext createContextOnJava2DSurface(Object graphics, GLContext shareWith) - throws GLException { - throw new GLException("Unimplemented on this platform"); - } - //---------------------------------------------------------------------- // Gamma-related functionality // private boolean gotGammaRampLength; private int gammaRampLength; + @Override protected final synchronized int getGammaRampLength() { if (gotGammaRampLength) { return gammaRampLength; @@ -556,6 +584,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { return gammaRampLength; } + @Override protected final boolean setGammaRamp(float[] ramp) { long display = getOrCreateSharedDpy(defaultDevice); if(0 == display) { @@ -577,6 +606,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { return res; } + @Override protected final Buffer getGammaRamp() { long display = getOrCreateSharedDpy(defaultDevice); if(0 == display) { @@ -607,6 +637,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { return rampData; } + @Override protected final void resetGammaRamp(Buffer originalGammaRamp) { if (originalGammaRamp == null) { return; // getGammaRamp failed originally diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java index 108c157a8..951174f71 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,28 +20,28 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package 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>(); - - // Be aware that on DRI systems, eg ATI fglrx, etc, + + // Be aware that on DRI systems, eg ATI fglrx, etc, // you have to set LIBGL_DRIVERS_PATH env variable. // Eg on Ubuntu 64bit systems this is: // export LIBGL_DRIVERS_PATH=/usr/lib/fglrx/dri:/usr/lib32/fglrx/dri @@ -55,19 +55,10 @@ public class X11GLXDynamicLibraryBundleInfo extends DesktopGLDynamicLibraryBundl // last but not least .. the generic one libsGL.add("GL"); - - libsList.add(libsGL); + + libsList.add(libsGL); 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() { diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfiguration.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfiguration.java index 0af62394c..ee3e1a3d7 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfiguration.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfiguration.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -33,9 +33,13 @@ package jogamp.opengl.x11.glx; +import java.nio.IntBuffer; import java.util.ArrayList; +import java.util.List; +import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; +import javax.media.nativewindow.VisualIDHolder; import javax.media.opengl.DefaultGLCapabilitiesChooser; import javax.media.opengl.GL; import javax.media.opengl.GLCapabilities; @@ -51,22 +55,63 @@ import jogamp.nativewindow.x11.XRenderPictFormat; import jogamp.nativewindow.x11.XVisualInfo; import jogamp.opengl.GLGraphicsConfigurationUtil; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.nativewindow.x11.X11GraphicsConfiguration; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implements Cloneable { public static final int MAX_ATTRIBS = 128; - private GLCapabilitiesChooser chooser; + private final GLCapabilitiesChooser chooser; - X11GLXGraphicsConfiguration(X11GraphicsScreen screen, + X11GLXGraphicsConfiguration(X11GraphicsScreen screen, X11GLCapabilities capsChosen, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser) { super(screen, capsChosen, capsRequested, capsChosen.getXVisualInfo()); this.chooser=chooser; } + @Override + public Object clone() { + return super.clone(); + } + + public final long getFBConfig() { + return ((X11GLCapabilities)capabilitiesChosen).getFBConfig(); + } + public final int getFBConfigID() { + return ((X11GLCapabilities)capabilitiesChosen).getFBConfigID(); + } + public final boolean hasFBConfig() { + return ((X11GLCapabilities)capabilitiesChosen).hasFBConfig(); + } + + void updateGraphicsConfiguration() { + final CapabilitiesImmutable aChosenCaps = getChosenCapabilities(); + if( !(aChosenCaps instanceof X11GLCapabilities) || VisualIDHolder.VID_UNDEFINED == aChosenCaps.getVisualID(VIDType.X11_XVISUAL) ) { + // This case is actually quite impossible, since on X11 the visualID and hence GraphicsConfiguration + // must be determined _before_ window creation! + final X11GLXGraphicsConfiguration newConfig = (X11GLXGraphicsConfiguration) + GraphicsConfigurationFactory.getFactory(getScreen().getDevice(), aChosenCaps).chooseGraphicsConfiguration( + aChosenCaps, getRequestedCapabilities(), chooser, getScreen(), VisualIDHolder.VID_UNDEFINED); + if(null!=newConfig) { + // FIXME: setScreen( ... ); + setXVisualInfo(newConfig.getXVisualInfo()); + setChosenCapabilities(newConfig.getChosenCapabilities()); + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.updateGraphicsConfiguration updated:"+this); + } + } else { + throw new GLException("No native VisualID pre-chosen and update failed: "+this); + } + } else if (DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.updateGraphicsConfiguration kept:"+this); + } + } + static X11GLXGraphicsConfiguration create(GLProfile glp, X11GraphicsScreen x11Screen, int fbcfgID) { - final long display = x11Screen.getDevice().getHandle(); + final X11GraphicsDevice device = (X11GraphicsDevice) x11Screen.getDevice(); + final long display = device.getHandle(); if(0==display) { throw new GLException("Display null of "+x11Screen); } @@ -79,43 +124,16 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem glp = GLProfile.getDefault(x11Screen.getDevice()); } final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); - final X11GLCapabilities caps = GLXFBConfig2GLCapabilities(glp, display, fbcfg, true, true, true, factory.isGLXMultisampleAvailable(x11Screen.getDevice())); + final X11GLCapabilities caps = GLXFBConfig2GLCapabilities(device, glp, fbcfg, GLGraphicsConfigurationUtil.ALL_BITS, factory.isGLXMultisampleAvailable(device)); if(null==caps) { throw new GLException("GLCapabilities null of "+toHexString(fbcfg)); } return new X11GLXGraphicsConfiguration(x11Screen, caps, caps, new DefaultGLCapabilitiesChooser()); } - public Object clone() { - return super.clone(); - } - - public final long getFBConfig() { - return ((X11GLCapabilities)capabilitiesChosen).getFBConfig(); - } - public final int getFBConfigID() { - return ((X11GLCapabilities)capabilitiesChosen).getFBConfigID(); - } - - void updateGraphicsConfiguration() { - X11GLXGraphicsConfiguration newConfig = (X11GLXGraphicsConfiguration) - GraphicsConfigurationFactory.getFactory(getScreen().getDevice()).chooseGraphicsConfiguration( - getChosenCapabilities(), getRequestedCapabilities(), chooser, getScreen()); - if(null!=newConfig) { - // FIXME: setScreen( ... ); - setXVisualInfo(newConfig.getXVisualInfo()); - setChosenCapabilities(newConfig.getChosenCapabilities()); - if(DEBUG) { - System.err.println("updateGraphicsConfiguration: "+this); - } - } - } - - static int[] GLCapabilities2AttribList(GLCapabilitiesImmutable caps, - boolean forFBAttr, - boolean isMultisampleAvailable, - long display, - int screen) + static IntBuffer GLCapabilities2AttribList(GLCapabilitiesImmutable caps, + boolean forFBAttr, boolean isMultisampleAvailable, + long display, int screen) { int colorDepth = (caps.getRedBits() + caps.getGreenBits() + @@ -123,123 +141,124 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem if (colorDepth < 15) { throw new GLException("Bit depths < 15 (i.e., non-true-color) not supported"); } - int[] res = new int[MAX_ATTRIBS]; + final IntBuffer res = Buffers.newDirectIntBuffer(MAX_ATTRIBS); int idx = 0; if (forFBAttr) { - res[idx++] = GLX.GLX_DRAWABLE_TYPE; - res[idx++] = caps.isOnscreen() ? ( GLX.GLX_WINDOW_BIT ) : ( caps.isPBuffer() ? GLX.GLX_PBUFFER_BIT : GLX.GLX_PIXMAP_BIT ) ; - } + res.put(idx++, GLX.GLX_DRAWABLE_TYPE); + + final int surfaceType; + if( caps.isOnscreen() ) { + surfaceType = GLX.GLX_WINDOW_BIT; + } else if( caps.isFBO() ) { + surfaceType = GLX.GLX_WINDOW_BIT; // native replacement! + } else if( caps.isPBuffer() ) { + surfaceType = GLX.GLX_PBUFFER_BIT; + } else if( caps.isBitmap() ) { + surfaceType = GLX.GLX_PIXMAP_BIT; + } else { + throw new GLException("no surface type set in caps: "+caps); + } + res.put(idx++, surfaceType); - if (forFBAttr) { - res[idx++] = GLX.GLX_RENDER_TYPE; - res[idx++] = GLX.GLX_RGBA_BIT; + res.put(idx++, GLX.GLX_RENDER_TYPE); + res.put(idx++, GLX.GLX_RGBA_BIT); } else { - res[idx++] = GLX.GLX_RGBA; + res.put(idx++, GLX.GLX_RGBA); } // FIXME: Still a bug is Mesa: PBUFFER && GLX_STEREO==GL_FALSE ? if (forFBAttr) { - res[idx++] = GLX.GLX_DOUBLEBUFFER; - res[idx++] = caps.getDoubleBuffered()?GL.GL_TRUE:GL.GL_FALSE; - res[idx++] = GLX.GLX_STEREO; - res[idx++] = caps.getStereo()?GL.GL_TRUE:GL.GL_FALSE; - res[idx++] = GLX.GLX_TRANSPARENT_TYPE; - res[idx++] = GLX.GLX_NONE; + res.put(idx++, GLX.GLX_DOUBLEBUFFER); + res.put(idx++, caps.getDoubleBuffered()?GL.GL_TRUE:GL.GL_FALSE); + res.put(idx++, GLX.GLX_STEREO); + res.put(idx++, caps.getStereo()?GL.GL_TRUE:GL.GL_FALSE); + res.put(idx++, GLX.GLX_TRANSPARENT_TYPE); + res.put(idx++, GLX.GLX_NONE); /** - res[idx++] = caps.isBackgroundOpaque()?GLX.GLX_NONE:GLX.GLX_TRANSPARENT_RGB; + res.put(idx++, caps.isBackgroundOpaque()?GLX.GLX_NONE:GLX.GLX_TRANSPARENT_RGB; if(!caps.isBackgroundOpaque()) { - res[idx++] = GLX.GLX_TRANSPARENT_RED_VALUE; - res[idx++] = caps.getTransparentRedValue()>=0?caps.getTransparentRedValue():(int)GLX.GLX_DONT_CARE; - res[idx++] = GLX.GLX_TRANSPARENT_GREEN_VALUE; - res[idx++] = caps.getTransparentGreenValue()>=0?caps.getTransparentGreenValue():(int)GLX.GLX_DONT_CARE; - res[idx++] = GLX.GLX_TRANSPARENT_BLUE_VALUE; - res[idx++] = caps.getTransparentBlueValue()>=0?caps.getTransparentBlueValue():(int)GLX.GLX_DONT_CARE; - res[idx++] = GLX.GLX_TRANSPARENT_ALPHA_VALUE; - res[idx++] = caps.getTransparentAlphaValue()>=0?caps.getTransparentAlphaValue():(int)GLX.GLX_DONT_CARE; + res.put(idx++, GLX.GLX_TRANSPARENT_RED_VALUE); + res.put(idx++, caps.getTransparentRedValue()>=0?caps.getTransparentRedValue():(int)GLX.GLX_DONT_CARE); + res.put(idx++, GLX.GLX_TRANSPARENT_GREEN_VALUE); + res.put(idx++, caps.getTransparentGreenValue()>=0?caps.getTransparentGreenValue():(int)GLX.GLX_DONT_CARE); + res.put(idx++, GLX.GLX_TRANSPARENT_BLUE_VALUE); + res.put(idx++, caps.getTransparentBlueValue()>=0?caps.getTransparentBlueValue():(int)GLX.GLX_DONT_CARE); + res.put(idx++, GLX.GLX_TRANSPARENT_ALPHA_VALUE); + res.put(idx++, caps.getTransparentAlphaValue()>=0?caps.getTransparentAlphaValue():(int)GLX.GLX_DONT_CARE); } */ } else { if (caps.getDoubleBuffered()) { - res[idx++] = GLX.GLX_DOUBLEBUFFER; + res.put(idx++, GLX.GLX_DOUBLEBUFFER); } if (caps.getStereo()) { - res[idx++] = GLX.GLX_STEREO; + res.put(idx++, GLX.GLX_STEREO); } } - res[idx++] = GLX.GLX_RED_SIZE; - res[idx++] = caps.getRedBits(); - res[idx++] = GLX.GLX_GREEN_SIZE; - res[idx++] = caps.getGreenBits(); - res[idx++] = GLX.GLX_BLUE_SIZE; - res[idx++] = caps.getBlueBits(); + res.put(idx++, GLX.GLX_RED_SIZE); + res.put(idx++, caps.getRedBits()); + res.put(idx++, GLX.GLX_GREEN_SIZE); + res.put(idx++, caps.getGreenBits()); + res.put(idx++, GLX.GLX_BLUE_SIZE); + res.put(idx++, caps.getBlueBits()); if(caps.getAlphaBits()>0) { - res[idx++] = GLX.GLX_ALPHA_SIZE; - res[idx++] = caps.getAlphaBits(); + res.put(idx++, GLX.GLX_ALPHA_SIZE); + res.put(idx++, caps.getAlphaBits()); } if (caps.getStencilBits() > 0) { - res[idx++] = GLX.GLX_STENCIL_SIZE; - res[idx++] = caps.getStencilBits(); + res.put(idx++, GLX.GLX_STENCIL_SIZE); + res.put(idx++, caps.getStencilBits()); } - res[idx++] = GLX.GLX_DEPTH_SIZE; - res[idx++] = caps.getDepthBits(); + res.put(idx++, GLX.GLX_DEPTH_SIZE); + res.put(idx++, caps.getDepthBits()); if (caps.getAccumRedBits() > 0 || caps.getAccumGreenBits() > 0 || caps.getAccumBlueBits() > 0 || caps.getAccumAlphaBits() > 0) { - res[idx++] = GLX.GLX_ACCUM_RED_SIZE; - res[idx++] = caps.getAccumRedBits(); - res[idx++] = GLX.GLX_ACCUM_GREEN_SIZE; - res[idx++] = caps.getAccumGreenBits(); - res[idx++] = GLX.GLX_ACCUM_BLUE_SIZE; - res[idx++] = caps.getAccumBlueBits(); - res[idx++] = GLX.GLX_ACCUM_ALPHA_SIZE; - res[idx++] = caps.getAccumAlphaBits(); + res.put(idx++, GLX.GLX_ACCUM_RED_SIZE); + res.put(idx++, caps.getAccumRedBits()); + res.put(idx++, GLX.GLX_ACCUM_GREEN_SIZE); + res.put(idx++, caps.getAccumGreenBits()); + res.put(idx++, GLX.GLX_ACCUM_BLUE_SIZE); + res.put(idx++, caps.getAccumBlueBits()); + res.put(idx++, GLX.GLX_ACCUM_ALPHA_SIZE); + res.put(idx++, caps.getAccumAlphaBits()); } if (isMultisampleAvailable && caps.getSampleBuffers()) { - res[idx++] = GLX.GLX_SAMPLE_BUFFERS; - res[idx++] = GL.GL_TRUE; - res[idx++] = GLX.GLX_SAMPLES; - res[idx++] = caps.getNumSamples(); + res.put(idx++, GLX.GLX_SAMPLE_BUFFERS); + res.put(idx++, GL.GL_TRUE); + res.put(idx++, GLX.GLX_SAMPLES); + res.put(idx++, caps.getNumSamples()); } - if (caps.isPBuffer()) { - if (caps.getPbufferFloatingPointBuffers()) { - String glXExtensions = GLX.glXQueryExtensionsString(display, screen); - if (glXExtensions == null || - glXExtensions.indexOf("GLX_NV_float_buffer") < 0) { - throw new GLException("Floating-point pbuffers on X11 currently require NVidia hardware: "+glXExtensions); - } - res[idx++] = GLXExt.GLX_FLOAT_COMPONENTS_NV; - res[idx++] = GL.GL_TRUE; - } - } - res[idx++] = 0; + res.put(idx++, 0); return res; } // FBConfig - - static boolean GLXFBConfigIDValid(long display, int screen, int fbcfgid) { + + static boolean GLXFBConfigIDValid(long display, int screen, int fbcfgid) { long fbcfg = X11GLXGraphicsConfiguration.glXFBConfigID2FBConfig(display, screen, fbcfgid); return (0 != fbcfg) ? X11GLXGraphicsConfiguration.GLXFBConfigValid( display, fbcfg ) : false ; } static boolean GLXFBConfigValid(long display, long fbcfg) { - int[] tmp = new int[1]; - if(GLX.GLX_BAD_ATTRIBUTE == GLX.glXGetFBConfigAttrib(display, fbcfg, GLX.GLX_RENDER_TYPE, tmp, 0)) { + final IntBuffer tmp = Buffers.newDirectIntBuffer(1); + if(GLX.GLX_BAD_ATTRIBUTE == GLX.glXGetFBConfigAttrib(display, fbcfg, GLX.GLX_RENDER_TYPE, tmp)) { return false; } return true; } - static int FBCfgDrawableTypeBits(final long display, final long fbcfg) { + static int FBCfgDrawableTypeBits(final X11GraphicsDevice device, final long fbcfg) { int val = 0; - int[] tmp = new int[1]; - int fbtype = glXGetFBConfig(display, fbcfg, GLX.GLX_DRAWABLE_TYPE, tmp, 0); + final IntBuffer tmp = Buffers.newDirectIntBuffer(1); + int fbtype = glXGetFBConfig(device.getHandle(), fbcfg, GLX.GLX_DRAWABLE_TYPE, tmp); if ( 0 != ( fbtype & GLX.GLX_WINDOW_BIT ) ) { - val |= GLGraphicsConfigurationUtil.WINDOW_BIT; + val |= GLGraphicsConfigurationUtil.WINDOW_BIT | + GLGraphicsConfigurationUtil.FBO_BIT; } if ( 0 != ( fbtype & GLX.GLX_PIXMAP_BIT ) ) { val |= GLGraphicsConfigurationUtil.BITMAP_BIT; @@ -250,19 +269,6 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem return val; } - static X11GLCapabilities GLXFBConfig2GLCapabilities(GLProfile glp, long display, long fbcfg, - boolean relaxed, boolean onscreen, boolean usePBuffer, - boolean isMultisampleAvailable) { - ArrayList bucket = new ArrayList(); - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); - if( GLXFBConfig2GLCapabilities(bucket, glp, display, fbcfg, winattrmask, isMultisampleAvailable) ) { - return (X11GLCapabilities) bucket.get(0); - } else if ( relaxed && GLXFBConfig2GLCapabilities(bucket, glp, display, fbcfg, GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable) ) { - return (X11GLCapabilities) bucket.get(0); - } - return null; - } - static XRenderDirectFormat XVisual2XRenderMask(long dpy, long visual) { XRenderPictFormat renderPictFmt = X11Lib.XRenderFindVisualFormat(dpy, visual); if(null == renderPictFmt) { @@ -270,70 +276,135 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem } return renderPictFmt.getDirect(); } + static XRenderDirectFormat XVisual2XRenderMask(long dpy, long visual, XRenderPictFormat dest) { + if( !X11Lib.XRenderFindVisualFormat(dpy, visual, dest) ) { + return null; + } else { + return dest.getDirect(); + } + } + + static X11GLCapabilities GLXFBConfig2GLCapabilities(final X11GraphicsDevice device, final GLProfile glp, final long fbcfg, + final int winattrmask, final boolean isMultisampleAvailable) { + final IntBuffer tmp = Buffers.newDirectIntBuffer(1); + final XRenderPictFormat xRenderPictFormat= XRenderPictFormat.create(); + return GLXFBConfig2GLCapabilities(device, glp, fbcfg, winattrmask, isMultisampleAvailable, tmp, xRenderPictFormat); + } - static boolean GLXFBConfig2GLCapabilities(ArrayList capsBucket, - GLProfile glp, long display, long fbcfg, - int winattrmask, boolean isMultisampleAvailable) { - final int allDrawableTypeBits = FBCfgDrawableTypeBits(display, fbcfg); + static List<GLCapabilitiesImmutable> GLXFBConfig2GLCapabilities(final X11GraphicsDevice device, final GLProfile glp, final PointerBuffer fbcfgsL, + final int winattrmask, final boolean isMultisampleAvailable, boolean onlyFirstValid) { + final IntBuffer tmp = Buffers.newDirectIntBuffer(1); + final XRenderPictFormat xRenderPictFormat= XRenderPictFormat.create(); + final List<GLCapabilitiesImmutable> result = new ArrayList<GLCapabilitiesImmutable>(); + for (int i = 0; i < fbcfgsL.limit(); i++) { + final long fbcfg = fbcfgsL.get(i); + final GLCapabilitiesImmutable c = GLXFBConfig2GLCapabilities(device, glp, fbcfg, winattrmask, isMultisampleAvailable, tmp, xRenderPictFormat); + if( null != c ) { + result.add(c); + if( onlyFirstValid ) { + break; + } + } + } + return result; + } + static X11GLCapabilities GLXFBConfig2GLCapabilities(final X11GraphicsDevice device, final GLProfile glp, final long fbcfg, + final int winattrmask, final boolean isMultisampleAvailable, + final IntBuffer tmp, final XRenderPictFormat xRenderPictFormat) { + final long display = device.getHandle(); + final int allDrawableTypeBits = FBCfgDrawableTypeBits(device, fbcfg); int drawableTypeBits = winattrmask & allDrawableTypeBits; - int fbcfgid = X11GLXGraphicsConfiguration.glXFBConfig2FBConfigID(display, fbcfg); - XVisualInfo visualInfo = GLX.glXGetVisualFromFBConfig(display, fbcfg); + final int fbcfgid = X11GLXGraphicsConfiguration.glXFBConfig2FBConfigID(display, fbcfg); + final XVisualInfo visualInfo = GLX.glXGetVisualFromFBConfig(display, fbcfg); if(null == visualInfo) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities: Null XVisualInfo for FBConfigID 0x" + Integer.toHexString(fbcfgid)); } // onscreen must have an XVisualInfo - drawableTypeBits = drawableTypeBits & ~GLGraphicsConfigurationUtil.WINDOW_BIT; + drawableTypeBits &= ~(GLGraphicsConfigurationUtil.WINDOW_BIT | GLGraphicsConfigurationUtil.FBO_BIT); } - if( 0 == drawableTypeBits ) { - return false; + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities: zero drawablebits: winattrmask: "+toHexString(winattrmask)+", offscreen "+(null == visualInfo)); + } + return null; } - int[] tmp = new int[1]; - if(GLX.GLX_BAD_ATTRIBUTE == GLX.glXGetFBConfigAttrib(display, fbcfg, GLX.GLX_RENDER_TYPE, tmp, 0)) { - return false; + if(GLX.GLX_BAD_ATTRIBUTE == GLX.glXGetFBConfigAttrib(display, fbcfg, GLX.GLX_RENDER_TYPE, tmp)) { + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities: FBConfig invalid (1): fbcfg: "+toHexString(fbcfg)); + } + return null; } - if( 0 == ( GLX.GLX_RGBA_BIT & tmp[0] ) ) { - return false; // no RGBA -> color index not supported - } - - GLCapabilities res = new X11GLCapabilities(visualInfo, fbcfg, fbcfgid, glp); - res.setDoubleBuffered(glXGetFBConfig(display, fbcfg, GLX.GLX_DOUBLEBUFFER, tmp, 0) != 0); - res.setStereo (glXGetFBConfig(display, fbcfg, GLX.GLX_STEREO, tmp, 0) != 0); - res.setHardwareAccelerated(glXGetFBConfig(display, fbcfg, GLX.GLX_CONFIG_CAVEAT, tmp, 0) != GLX.GLX_SLOW_CONFIG); - res.setDepthBits (glXGetFBConfig(display, fbcfg, GLX.GLX_DEPTH_SIZE, tmp, 0)); - res.setStencilBits (glXGetFBConfig(display, fbcfg, GLX.GLX_STENCIL_SIZE, tmp, 0)); - res.setRedBits (glXGetFBConfig(display, fbcfg, GLX.GLX_RED_SIZE, tmp, 0)); - res.setGreenBits (glXGetFBConfig(display, fbcfg, GLX.GLX_GREEN_SIZE, tmp, 0)); - res.setBlueBits (glXGetFBConfig(display, fbcfg, GLX.GLX_BLUE_SIZE, tmp, 0)); - res.setAlphaBits (glXGetFBConfig(display, fbcfg, GLX.GLX_ALPHA_SIZE, tmp, 0)); - res.setAccumRedBits (glXGetFBConfig(display, fbcfg, GLX.GLX_ACCUM_RED_SIZE, tmp, 0)); - res.setAccumGreenBits(glXGetFBConfig(display, fbcfg, GLX.GLX_ACCUM_GREEN_SIZE, tmp, 0)); - res.setAccumBlueBits (glXGetFBConfig(display, fbcfg, GLX.GLX_ACCUM_BLUE_SIZE, tmp, 0)); - res.setAccumAlphaBits(glXGetFBConfig(display, fbcfg, GLX.GLX_ACCUM_ALPHA_SIZE, tmp, 0)); - if (isMultisampleAvailable) { - res.setSampleBuffers(glXGetFBConfig(display, fbcfg, GLX.GLX_SAMPLE_BUFFERS, tmp, 0) != 0); - res.setNumSamples (glXGetFBConfig(display, fbcfg, GLX.GLX_SAMPLES, tmp, 0)); + if( 0 == ( GLX.GLX_RGBA_BIT & tmp.get(0) ) ) { + // no RGBA -> color index not supported + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities: FBConfig not RGBA (2): fbcfg: "+toHexString(fbcfg)); + } + return null; } - final XRenderDirectFormat xrmask = ( null != visualInfo ) ? - XVisual2XRenderMask( display, visualInfo.getVisual() ) : + + final X11GLCapabilities caps = new X11GLCapabilities(visualInfo, fbcfg, fbcfgid, glp); + + final XRenderDirectFormat xrmask = ( null != visualInfo ) ? + XVisual2XRenderMask( display, visualInfo.getVisual(), xRenderPictFormat) : null ; + + final int _attributes[] = { + GLX.GLX_SAMPLE_BUFFERS, + GLX.GLX_SAMPLES, + GLX.GLX_DOUBLEBUFFER, + GLX.GLX_STEREO, + GLX.GLX_CONFIG_CAVEAT, + GLX.GLX_RED_SIZE, + GLX.GLX_GREEN_SIZE, + GLX.GLX_BLUE_SIZE, + GLX.GLX_ALPHA_SIZE, + GLX.GLX_ACCUM_RED_SIZE, + GLX.GLX_ACCUM_GREEN_SIZE, + GLX.GLX_ACCUM_BLUE_SIZE, + GLX.GLX_ACCUM_ALPHA_SIZE, + GLX.GLX_DEPTH_SIZE, + GLX.GLX_STENCIL_SIZE + }; + final int offset = isMultisampleAvailable ? 0 : 2; + final IntBuffer attributes = Buffers.newDirectIntBuffer(_attributes); + attributes.position(offset); + final IntBuffer values = Buffers.newDirectIntBuffer(attributes.remaining()); + final int err = GLX.glXGetFBConfigAttributes(display, fbcfg, attributes, values); + if (0 != err) { + throw new GLException("glXGetFBConfig("+toHexString(attributes.get(offset+values.get(0)))+") failed: error code " + glXGetFBConfigErrorCode(err)); + } + int j=0; + if (isMultisampleAvailable) { + caps.setSampleBuffers(values.get(j++) != 0); + caps.setNumSamples (values.get(j++)); + } final int alphaMask = ( null != xrmask ) ? xrmask.getAlphaMask() : 0; - res.setBackgroundOpaque( 0 >= alphaMask ); - if( !res.isBackgroundOpaque() ) { - res.setTransparentRedValue(xrmask.getRedMask()); - res.setTransparentGreenValue(xrmask.getGreenMask()); - res.setTransparentBlueValue(xrmask.getBlueMask()); - res.setTransparentAlphaValue(alphaMask); + caps.setBackgroundOpaque( 0 >= alphaMask ); + if( !caps.isBackgroundOpaque() ) { + caps.setTransparentRedValue(xrmask.getRedMask()); + caps.setTransparentGreenValue(xrmask.getGreenMask()); + caps.setTransparentBlueValue(xrmask.getBlueMask()); + caps.setTransparentAlphaValue(alphaMask); } - - try { - res.setPbufferFloatingPointBuffers(glXGetFBConfig(display, fbcfg, GLXExt.GLX_FLOAT_COMPONENTS_NV, tmp, 0) != GL.GL_FALSE); - } catch (Exception e) {} - - return GLGraphicsConfigurationUtil.addGLCapabilitiesPermutations(capsBucket, res, drawableTypeBits ); + // ALPHA shall be set at last - due to it's auto setting by the above (!opaque / samples) + caps.setDoubleBuffered(values.get(j++) != 0); + caps.setStereo (values.get(j++) != 0); + caps.setHardwareAccelerated(values.get(j++) != GLX.GLX_SLOW_CONFIG); + caps.setRedBits (values.get(j++)); + caps.setGreenBits (values.get(j++)); + caps.setBlueBits (values.get(j++)); + caps.setAlphaBits (values.get(j++)); + caps.setAccumRedBits (values.get(j++)); + caps.setAccumGreenBits(values.get(j++)); + caps.setAccumBlueBits (values.get(j++)); + caps.setAccumAlphaBits(values.get(j++)); + caps.setDepthBits (values.get(j++)); + caps.setStencilBits (values.get(j++)); + + return (X11GLCapabilities) GLGraphicsConfigurationUtil.fixWinAttribBitsAndHwAccel(device, drawableTypeBits, caps); } private static String glXGetFBConfigErrorCode(int err) { @@ -344,26 +415,27 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem } } - static int glXGetFBConfig(long display, long cfg, int attrib, int[] tmp, int tmp_offset) { + static int glXGetFBConfig(long display, long cfg, int attrib, IntBuffer tmp) { if (display == 0) { throw new GLException("No display connection"); } - int res = GLX.glXGetFBConfigAttrib(display, cfg, attrib, tmp, tmp_offset); + int res = GLX.glXGetFBConfigAttrib(display, cfg, attrib, tmp); if (res != 0) { throw new GLException("glXGetFBConfig("+toHexString(attrib)+") failed: error code " + glXGetFBConfigErrorCode(res)); } - return tmp[tmp_offset]; + return tmp.get(tmp.position()); } static int glXFBConfig2FBConfigID(long display, long cfg) { - int[] tmpID = new int[1]; - return glXGetFBConfig(display, cfg, GLX.GLX_FBCONFIG_ID, tmpID, 0); + final IntBuffer tmpID = Buffers.newDirectIntBuffer(1); + return glXGetFBConfig(display, cfg, GLX.GLX_FBCONFIG_ID, tmpID); } static long glXFBConfigID2FBConfig(long display, int screen, int id) { - int[] attribs = new int[] { GLX.GLX_FBCONFIG_ID, id, 0 }; - int[] count = { -1 }; - PointerBuffer fbcfgsL = GLX.glXChooseFBConfig(display, screen, attribs, 0, count, 0); + final IntBuffer attribs = Buffers.newDirectIntBuffer(new int[] { GLX.GLX_FBCONFIG_ID, id, 0 }); + final IntBuffer count = Buffers.newDirectIntBuffer(1); + count.put(0, -1); + PointerBuffer fbcfgsL = GLX.glXChooseFBConfig(display, screen, attribs, count); if (fbcfgsL == null || fbcfgsL.limit()<1) { return 0; } @@ -379,7 +451,7 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem XVisualInfo[] infos = X11Lib.XGetVisualInfo(display, X11Lib.VisualIDMask, template, count, 0); if (infos == null || infos.length == 0) { return null; - } + } XVisualInfo res = XVisualInfo.create(infos[0]); if (DEBUG) { System.err.println("Fetched XVisualInfo for visual ID " + toHexString(visualID)); @@ -388,57 +460,49 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem return res; } - static boolean XVisualInfo2GLCapabilities(ArrayList capsBucket, - GLProfile glp, long display, XVisualInfo info, - final int winattrmask, boolean isMultisampleEnabled) { - final int allDrawableTypeBits = GLGraphicsConfigurationUtil.WINDOW_BIT | GLGraphicsConfigurationUtil.BITMAP_BIT ; + static X11GLCapabilities XVisualInfo2GLCapabilities(final X11GraphicsDevice device, GLProfile glp, XVisualInfo info, + final int winattrmask, boolean isMultisampleEnabled) { + final int allDrawableTypeBits = GLGraphicsConfigurationUtil.WINDOW_BIT | + GLGraphicsConfigurationUtil.BITMAP_BIT | + GLGraphicsConfigurationUtil.FBO_BIT ; + final int drawableTypeBits = winattrmask & allDrawableTypeBits; if( 0 == drawableTypeBits ) { - return false; + return null; } - int[] tmp = new int[1]; - int val = glXGetConfig(display, info, GLX.GLX_USE_GL, tmp, 0); + final long display = device.getHandle(); + final IntBuffer tmp = Buffers.newDirectIntBuffer(1); + int val = glXGetConfig(display, info, GLX.GLX_USE_GL, tmp); if (val == 0) { if(DEBUG) { System.err.println("Visual ("+toHexString(info.getVisualid())+") does not support OpenGL"); } - return false; + return null; } - val = glXGetConfig(display, info, GLX.GLX_RGBA, tmp, 0); + val = glXGetConfig(display, info, GLX.GLX_RGBA, tmp); if (val == 0) { if(DEBUG) { System.err.println("Visual ("+toHexString(info.getVisualid())+") does not support RGBA"); } - return false; + return null; } GLCapabilities res = new X11GLCapabilities(info, glp); - res.setDoubleBuffered(glXGetConfig(display, info, GLX.GLX_DOUBLEBUFFER, tmp, 0) != 0); - res.setStereo (glXGetConfig(display, info, GLX.GLX_STEREO, tmp, 0) != 0); + res.setDoubleBuffered(glXGetConfig(display, info, GLX.GLX_DOUBLEBUFFER, tmp) != 0); + res.setStereo (glXGetConfig(display, info, GLX.GLX_STEREO, tmp) != 0); // Note: use of hardware acceleration is determined by // glXCreateContext, not by the XVisualInfo. Optimistically claim // that all GLCapabilities have the capability to be hardware // accelerated. - res.setHardwareAccelerated(true); - res.setDepthBits (glXGetConfig(display, info, GLX.GLX_DEPTH_SIZE, tmp, 0)); - res.setStencilBits (glXGetConfig(display, info, GLX.GLX_STENCIL_SIZE, tmp, 0)); - res.setRedBits (glXGetConfig(display, info, GLX.GLX_RED_SIZE, tmp, 0)); - res.setGreenBits (glXGetConfig(display, info, GLX.GLX_GREEN_SIZE, tmp, 0)); - res.setBlueBits (glXGetConfig(display, info, GLX.GLX_BLUE_SIZE, tmp, 0)); - res.setAlphaBits (glXGetConfig(display, info, GLX.GLX_ALPHA_SIZE, tmp, 0)); - res.setAccumRedBits (glXGetConfig(display, info, GLX.GLX_ACCUM_RED_SIZE, tmp, 0)); - res.setAccumGreenBits(glXGetConfig(display, info, GLX.GLX_ACCUM_GREEN_SIZE, tmp, 0)); - res.setAccumBlueBits (glXGetConfig(display, info, GLX.GLX_ACCUM_BLUE_SIZE, tmp, 0)); - res.setAccumAlphaBits(glXGetConfig(display, info, GLX.GLX_ACCUM_ALPHA_SIZE, tmp, 0)); if (isMultisampleEnabled) { - res.setSampleBuffers(glXGetConfig(display, info, GLX.GLX_SAMPLE_BUFFERS, tmp, 0) != 0); - res.setNumSamples (glXGetConfig(display, info, GLX.GLX_SAMPLES, tmp, 0)); + res.setSampleBuffers(glXGetConfig(display, info, GLX.GLX_SAMPLE_BUFFERS, tmp) != 0); + res.setNumSamples (glXGetConfig(display, info, GLX.GLX_SAMPLES, tmp)); } - final XRenderDirectFormat xrmask = ( null != info ) ? - XVisual2XRenderMask( display, info.getVisual() ) : + final XRenderDirectFormat xrmask = ( null != info ) ? + XVisual2XRenderMask( display, info.getVisual() ) : null ; final int alphaMask = ( null != xrmask ) ? xrmask.getAlphaMask() : 0; res.setBackgroundOpaque( 0 >= alphaMask ); @@ -448,8 +512,20 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem res.setTransparentBlueValue(xrmask.getBlueMask()); res.setTransparentAlphaValue(alphaMask); } - - return GLGraphicsConfigurationUtil.addGLCapabilitiesPermutations(capsBucket, res, drawableTypeBits); + // ALPHA shall be set at last - due to it's auto setting by the above (!opaque / samples) + res.setHardwareAccelerated(true); + res.setDepthBits (glXGetConfig(display, info, GLX.GLX_DEPTH_SIZE, tmp)); + res.setStencilBits (glXGetConfig(display, info, GLX.GLX_STENCIL_SIZE, tmp)); + res.setRedBits (glXGetConfig(display, info, GLX.GLX_RED_SIZE, tmp)); + res.setGreenBits (glXGetConfig(display, info, GLX.GLX_GREEN_SIZE, tmp)); + res.setBlueBits (glXGetConfig(display, info, GLX.GLX_BLUE_SIZE, tmp)); + res.setAlphaBits (glXGetConfig(display, info, GLX.GLX_ALPHA_SIZE, tmp)); + res.setAccumRedBits (glXGetConfig(display, info, GLX.GLX_ACCUM_RED_SIZE, tmp)); + res.setAccumGreenBits(glXGetConfig(display, info, GLX.GLX_ACCUM_GREEN_SIZE, tmp)); + res.setAccumBlueBits (glXGetConfig(display, info, GLX.GLX_ACCUM_BLUE_SIZE, tmp)); + res.setAccumAlphaBits(glXGetConfig(display, info, GLX.GLX_ACCUM_ALPHA_SIZE, tmp)); + + return (X11GLCapabilities) GLGraphicsConfigurationUtil.fixWinAttribBitsAndHwAccel(device, drawableTypeBits, res); } private static String glXGetConfigErrorCode(int err) { @@ -462,17 +538,18 @@ public class X11GLXGraphicsConfiguration extends X11GraphicsConfiguration implem } } - static int glXGetConfig(long display, XVisualInfo info, int attrib, int[] tmp, int tmp_offset) { + static int glXGetConfig(long display, XVisualInfo info, int attrib, IntBuffer tmp) { if (display == 0) { throw new GLException("No display connection"); } - int res = GLX.glXGetConfig(display, info, attrib, tmp, tmp_offset); + int res = GLX.glXGetConfig(display, info, attrib, tmp); if (res != 0) { throw new GLException("glXGetConfig("+toHexString(attrib)+") failed: error code " + glXGetConfigErrorCode(res)); } - return tmp[tmp_offset]; + return tmp.get(tmp.position()); } + @Override public String toString() { return "X11GLXGraphicsConfiguration["+getScreen()+", visualID " + toHexString(getXVisualID()) + ", fbConfigID " + toHexString(getFBConfigID()) + ",\n\trequested " + getRequestedCapabilities()+ diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfigurationFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfigurationFactory.java index 0ec2f234c..1f92960bc 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfigurationFactory.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXGraphicsConfigurationFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -40,6 +40,7 @@ import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.opengl.DefaultGLCapabilitiesChooser; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; @@ -48,6 +49,7 @@ import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; @@ -57,6 +59,7 @@ import jogamp.nativewindow.x11.XVisualInfo; import jogamp.opengl.GLGraphicsConfigurationFactory; import jogamp.opengl.GLGraphicsConfigurationUtil; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -73,20 +76,25 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF static GraphicsConfigurationFactory fallbackX11GraphicsConfigurationFactory = null; static void registerFactory() { final GraphicsConfigurationFactory newFactory = new X11GLXGraphicsConfigurationFactory(); - final GraphicsConfigurationFactory oldFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, newFactory); + final GraphicsConfigurationFactory oldFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, GLCapabilitiesImmutable.class, newFactory); if(oldFactory == newFactory) { throw new InternalError("GraphicsConfigurationFactory lifecycle impl. error"); } - if(null == oldFactory) { - throw new InternalError("Missing fallback GraphicsConfigurationFactory"); + if(null != oldFactory) { + fallbackX11GraphicsConfigurationFactory = oldFactory; + } else { + fallbackX11GraphicsConfigurationFactory = GraphicsConfigurationFactory.getFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, CapabilitiesImmutable.class); + if( null == fallbackX11GraphicsConfigurationFactory ) { + throw new InternalError("Missing fallback GraphicsConfigurationFactory"); + } } - fallbackX11GraphicsConfigurationFactory = oldFactory; } private X11GLXGraphicsConfigurationFactory() { } + @Override protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( - CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { + CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen, int nativeVisualID) { if (!(absScreen instanceof X11GraphicsScreen)) { throw new IllegalArgumentException("Only X11GraphicsScreen are allowed here"); } @@ -102,38 +110,42 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF if (chooser != null && !(chooser instanceof GLCapabilitiesChooser)) { throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilitiesChooser objects"); } - + if(!GLXUtil.isGLXAvailableOnServer((X11GraphicsDevice)absScreen.getDevice())) { if(null != fallbackX11GraphicsConfigurationFactory) { if(DEBUG) { System.err.println("No GLX available, fallback to "+fallbackX11GraphicsConfigurationFactory.getClass().getSimpleName()+" for: "+absScreen); } - return fallbackX11GraphicsConfigurationFactory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, absScreen); + return fallbackX11GraphicsConfigurationFactory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, absScreen, VisualIDHolder.VID_UNDEFINED); } throw new InternalError("No GLX and no fallback GraphicsConfigurationFactory available for: "+absScreen); - } + } return chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested, - (GLCapabilitiesChooser)chooser, (X11GraphicsScreen)absScreen); + (GLCapabilitiesChooser)chooser, (X11GraphicsScreen)absScreen, nativeVisualID); } protected static List<GLCapabilitiesImmutable> getAvailableCapabilities(X11GLXDrawableFactory factory, AbstractGraphicsDevice device) { - X11GLXDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResource(device); + X11GLXDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResourceImpl(device); if(null == sharedResource) { throw new GLException("Shared resource for device n/a: "+device); } final X11GraphicsScreen sharedScreen = (X11GraphicsScreen) sharedResource.getScreen(); - final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(sharedScreen.getDevice()); - final X11GLXDrawable sharedDrawable = (X11GLXDrawable) sharedResource.getDrawable(); - final GLCapabilitiesImmutable capsChosen = sharedDrawable.getChosenGLCapabilities(); - final GLProfile glp = capsChosen.getGLProfile(); + final X11GraphicsDevice sharedDevice = (X11GraphicsDevice) sharedScreen.getDevice(); + final boolean isMultisampleAvailable = sharedResource.isGLXMultisampleAvailable(); + final GLProfile glp = GLProfile.getDefault(device); List<GLCapabilitiesImmutable> availableCaps = null; - if( sharedResource.isGLXVersionGreaterEqualOneThree() ) { - availableCaps = getAvailableGLCapabilitiesFBConfig(sharedScreen, glp, isMultisampleAvailable); - } - if( null == availableCaps || availableCaps.isEmpty() ) { - availableCaps = getAvailableGLCapabilitiesXVisual(sharedScreen, glp, isMultisampleAvailable); + sharedDevice.lock(); + try { + if( sharedResource.isGLXVersionGreaterEqualOneThree() ) { + availableCaps = getAvailableGLCapabilitiesFBConfig(sharedScreen, glp, isMultisampleAvailable); + } + if( null == availableCaps || availableCaps.isEmpty() ) { + availableCaps = getAvailableGLCapabilitiesXVisual(sharedScreen, glp, isMultisampleAvailable); + } + } finally { + sharedDevice.unlock(); } if( null != availableCaps && availableCaps.size() > 1 ) { Collections.sort(availableCaps, XVisualIDComparator); @@ -146,33 +158,35 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF // Utilizing FBConfig // - AbstractGraphicsDevice absDevice = x11Screen.getDevice(); - long display = absDevice.getHandle(); + final X11GraphicsDevice absDevice = (X11GraphicsDevice) x11Screen.getDevice(); + final long display = absDevice.getHandle(); - int screen = x11Screen.getIndex(); - int[] count = { -1 }; - ArrayList<GLCapabilitiesImmutable> availableCaps = new ArrayList<GLCapabilitiesImmutable>(); + final int screen = x11Screen.getIndex(); + final IntBuffer count = Buffers.newDirectIntBuffer(1); + count.put(0, -1); + final ArrayList<GLCapabilitiesImmutable> availableCaps = new ArrayList<GLCapabilitiesImmutable>(); - fbcfgsL = GLX.glXChooseFBConfig(display, screen, null, 0, count, 0); + fbcfgsL = GLX.glXChooseFBConfig(display, screen, null, count); if (fbcfgsL == null || fbcfgsL.limit()<=0) { if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesFBConfig: Failed glXChooseFBConfig ("+x11Screen+"): "+fbcfgsL+", "+count[0]); + System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesFBConfig: Failed glXChooseFBConfig ("+x11Screen+"): "+fbcfgsL+", "+count.get(0)); } return null; } for (int i = 0; i < fbcfgsL.limit(); i++) { - if( !X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(availableCaps, glProfile, display, fbcfgsL.get(i), GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable) ) { - if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesFBConfig: FBConfig invalid (2): ("+x11Screen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); - } + final GLCapabilitiesImmutable caps = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(absDevice, glProfile, fbcfgsL.get(i), GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable); + if(null != caps) { + availableCaps.add(caps); + } else if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesFBConfig: FBConfig invalid (2): ("+x11Screen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); } } return availableCaps; } static List<GLCapabilitiesImmutable> getAvailableGLCapabilitiesXVisual(X11GraphicsScreen x11Screen, GLProfile glProfile, boolean isMultisampleAvailable) { - AbstractGraphicsDevice absDevice = x11Screen.getDevice(); - long display = absDevice.getHandle(); + final X11GraphicsDevice absDevice = (X11GraphicsDevice) x11Screen.getDevice(); + final long display = absDevice.getHandle(); int screen = x11Screen.getIndex(); @@ -185,10 +199,11 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF } ArrayList<GLCapabilitiesImmutable> availableCaps = new ArrayList<GLCapabilitiesImmutable>(); for (int i = 0; i < infos.length; i++) { - if( !X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities(availableCaps, glProfile, display, infos[i], GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable) ) { - if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesXVisual: XVisual invalid: ("+x11Screen+"): fbcfg: "+toHexString(infos[i].getVisualid())); - } + final GLCapabilitiesImmutable caps = X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities(absDevice, glProfile, infos[i], GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable); + if(null != caps) { + availableCaps.add(caps); + } if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesXVisual: XVisual invalid: ("+x11Screen+"): fbcfg: "+toHexString(infos[i].getVisualid())); } } return availableCaps; @@ -198,42 +213,46 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF static X11GLXGraphicsConfiguration chooseGraphicsConfigurationStatic(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, - X11GraphicsScreen x11Screen) { + X11GraphicsScreen x11Screen, int xvisualID) { if (x11Screen == null) { throw new IllegalArgumentException("AbstractGraphicsScreen is null"); } - if (capsChosen == null) { capsChosen = new GLCapabilities(null); } - X11GraphicsDevice x11Device = (X11GraphicsDevice) x11Screen.getDevice(); + X11GraphicsDevice x11Device = (X11GraphicsDevice) x11Screen.getDevice(); X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); - capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, factory.canCreateGLPbuffer(x11Device) ); - boolean usePBuffer = capsChosen.isPBuffer(); - + capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, factory, x11Device); + final boolean usePBuffer = !capsChosen.isOnscreen() && capsChosen.isPBuffer(); + X11GLXGraphicsConfiguration res = null; - if( factory.isGLXVersionGreaterEqualOneThree(x11Device) ) { - res = chooseGraphicsConfigurationFBConfig(capsChosen, capsReq, chooser, x11Screen); - } - if(null==res) { - if(usePBuffer) { - throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig for "+capsChosen); + x11Device.lock(); + try { + if( factory.isGLXVersionGreaterEqualOneThree(x11Device) ) { + res = chooseGraphicsConfigurationFBConfig(capsChosen, capsReq, chooser, x11Screen, xvisualID); + } + if(null==res) { + if(usePBuffer) { + throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig for visualID "+toHexString(xvisualID)+", "+capsChosen); + } + res = chooseGraphicsConfigurationXVisual(capsChosen, capsReq, chooser, x11Screen, xvisualID); } - res = chooseGraphicsConfigurationXVisual(capsChosen, capsReq, chooser, x11Screen); + } finally { + x11Device.unlock(); } if(null==res) { - throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig and XVisual for "+capsChosen); + throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig and XVisual for visualID "+toHexString(xvisualID)+", "+x11Screen+", "+capsChosen); } if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationStatic("+x11Screen+","+capsChosen+"): "+res); + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationStatic(visualID "+toHexString(xvisualID)+", "+x11Screen+","+capsChosen+"): "+res); } return res; } static X11GLXGraphicsConfiguration fetchGraphicsConfigurationFBConfig(X11GraphicsScreen x11Screen, int fbID, GLProfile glp) { - final AbstractGraphicsDevice absDevice = x11Screen.getDevice(); - final long display = absDevice.getHandle(); + final X11GraphicsDevice x11Device = (X11GraphicsDevice) x11Screen.getDevice(); + final long display = x11Device.getHandle(); final int screen = x11Screen.getIndex(); final long fbcfg = X11GLXGraphicsConfiguration.glXFBConfigID2FBConfig(display, screen, fbID); @@ -244,55 +263,60 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF return null; } final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); - - final X11GLCapabilities caps = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(glp, display, fbcfg, true, true, true, factory.isGLXMultisampleAvailable(absDevice)); + + final X11GLCapabilities caps = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(x11Device, glp, fbcfg, GLGraphicsConfigurationUtil.ALL_BITS, factory.isGLXMultisampleAvailable(x11Device)); return new X11GLXGraphicsConfiguration(x11Screen, caps, caps, new DefaultGLCapabilitiesChooser()); } private static X11GLXGraphicsConfiguration chooseGraphicsConfigurationFBConfig(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, - X11GraphicsScreen x11Screen) { + X11GraphicsScreen x11Screen, int xvisualID) { int recommendedIndex = -1; PointerBuffer fbcfgsL = null; GLProfile glProfile = capsChosen.getGLProfile(); - boolean onscreen = capsChosen.isOnscreen(); - boolean usePBuffer = capsChosen.isPBuffer(); // Utilizing FBConfig // - AbstractGraphicsDevice absDevice = x11Screen.getDevice(); - long display = absDevice.getHandle(); + X11GraphicsDevice x11Device = (X11GraphicsDevice) x11Screen.getDevice(); + long display = x11Device.getHandle(); int screen = x11Screen.getIndex(); - - final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); - final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(absDevice); - int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList(capsChosen, true, isMultisampleAvailable, display, screen); - int[] count = { -1 }; - ArrayList/*<X11GLCapabilities>*/ availableCaps = new ArrayList(); - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); - // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice - fbcfgsL = GLX.glXChooseFBConfig(display, screen, attribs, 0, count, 0); - if (fbcfgsL != null && fbcfgsL.limit()>0) { - for (int i = 0; i < fbcfgsL.limit(); i++) { - if( !X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(availableCaps, glProfile, display, fbcfgsL.get(i), winattrmask, isMultisampleAvailable) ) { - if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: FBConfig invalid (1): ("+x11Screen+","+capsChosen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); - } - } - } + final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); + final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(x11Device); + final IntBuffer attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList(capsChosen, true, isMultisampleAvailable, display, screen); + final IntBuffer count = Buffers.newDirectIntBuffer(1); + count.put(0, -1); + final int winattrmask = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(capsChosen); + List<GLCapabilitiesImmutable> availableCaps; + // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice, + // skipped if xvisualID is given + final boolean hasGLXChosenCaps; + if( VisualIDHolder.VID_UNDEFINED == xvisualID ) { + fbcfgsL = GLX.glXChooseFBConfig(display, screen, attribs, count); + hasGLXChosenCaps = fbcfgsL != null && fbcfgsL.limit()>0; + } else { + hasGLXChosenCaps = false; + } + final boolean useRecommendedIndex = hasGLXChosenCaps && capsChosen.isBackgroundOpaque(); // only use recommended idx if not translucent + final boolean skipCapsChooser = null == chooser && useRecommendedIndex; // fast path: skip choosing if using recommended idx and null chooser is used + if (hasGLXChosenCaps) { + availableCaps = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(x11Device, glProfile, fbcfgsL, winattrmask, isMultisampleAvailable, skipCapsChooser /* onlyFirstValid */); if(availableCaps.size() > 0) { - recommendedIndex = capsChosen.isBackgroundOpaque() ? 0 : -1; // only use recommended idx if not translucent + recommendedIndex = useRecommendedIndex ? 0 : -1; if (DEBUG) { System.err.println("glXChooseFBConfig recommended fbcfg " + toHexString(fbcfgsL.get(0)) + ", idx " + recommendedIndex); + System.err.println("useRecommendedIndex "+useRecommendedIndex+", skipCapsChooser "+skipCapsChooser); System.err.println("user caps " + capsChosen); - System.err.println("fbcfg caps " + availableCaps.get(0)); + System.err.println("fbcfg caps " + fbcfgsL.limit()+", availCaps "+availableCaps.get(0)); } } else if (DEBUG) { System.err.println("glXChooseFBConfig no caps for recommended fbcfg " + toHexString(fbcfgsL.get(0))); + System.err.println("useRecommendedIndex "+useRecommendedIndex+", skipCapsChooser "+skipCapsChooser); System.err.println("user caps " + capsChosen); } + } else { + availableCaps = new ArrayList<GLCapabilitiesImmutable>(); } // 2nd choice: get all GLCapabilities available, no preferred recommendedIndex available @@ -300,23 +324,48 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF // reset .. recommendedIndex = -1; - fbcfgsL = GLX.glXChooseFBConfig(display, screen, null, 0, count, 0); + fbcfgsL = GLX.glXChooseFBConfig(display, screen, null, count); if (fbcfgsL == null || fbcfgsL.limit()<=0) { if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed glXChooseFBConfig ("+x11Screen+","+capsChosen+"): "+fbcfgsL+", "+count[0]); + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed glXChooseFBConfig ("+x11Screen+","+capsChosen+"): "+fbcfgsL+", "+count.get(0)); } return null; } + availableCaps = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(x11Device, glProfile, fbcfgsL, winattrmask, isMultisampleAvailable, false /* onlyOneValid */); + } + + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: got configs: "+availableCaps.size()); + for(int i=0; i<availableCaps.size(); i++) { + System.err.println(i+": "+availableCaps.get(i)); + } + } - for (int i = 0; i < fbcfgsL.limit(); i++) { - if( !X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(availableCaps, glProfile, display, fbcfgsL.get(i), winattrmask, isMultisampleAvailable) ) { - if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: FBConfig invalid (2): ("+x11Screen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); - } + if( VisualIDHolder.VID_UNDEFINED != xvisualID ) { // implies !hasGLXChosenCaps + for(int i=0; i<availableCaps.size(); ) { + final VisualIDHolder vidh = availableCaps.get(i); + if(vidh.getVisualID(VIDType.X11_XVISUAL) != xvisualID ) { + availableCaps.remove(i); + } else { + i++; + } + } + if(0==availableCaps.size()) { + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: post filter visualID "+toHexString(xvisualID )+" no config found, failed - return null"); } + return null; + } else if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: post filter visualID "+toHexString(xvisualID)+" got configs: "+availableCaps.size()); } } - int chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); + + final int chosenIndex; + if( skipCapsChooser && 0 <= recommendedIndex ) { + chosenIndex = recommendedIndex; + } else { + chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); + } if ( 0 > chosenIndex ) { if (DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: failed, return null"); @@ -332,32 +381,36 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF private static X11GLXGraphicsConfiguration chooseGraphicsConfigurationXVisual(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, - X11GraphicsScreen x11Screen) { + X11GraphicsScreen x11Screen, int xvisualID) { if (chooser == null) { chooser = new DefaultGLCapabilitiesChooser(); } GLProfile glProfile = capsChosen.getGLProfile(); - final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(capsChosen.isOnscreen(), false /* pbuffer */); - ArrayList availableCaps = new ArrayList(); + final int winattrmask = GLGraphicsConfigurationUtil.getExclusiveWinAttributeBits(capsChosen.isOnscreen(), capsChosen.isFBO(), false /* pbuffer */, capsChosen.isBitmap()); + List<GLCapabilitiesImmutable> availableCaps = new ArrayList<GLCapabilitiesImmutable>(); int recommendedIndex = -1; - AbstractGraphicsDevice absDevice = x11Screen.getDevice(); + X11GraphicsDevice absDevice = (X11GraphicsDevice) x11Screen.getDevice(); long display = absDevice.getHandle(); int screen = x11Screen.getIndex(); - - final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); - final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(absDevice); - int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList(capsChosen, false, isMultisampleAvailable, display, screen); + final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); + final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(absDevice); + final IntBuffer attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList(capsChosen, false, isMultisampleAvailable, display, screen); + + XVisualInfo recommendedVis = null; // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice - XVisualInfo recommendedVis = GLX.glXChooseVisual(display, screen, attribs, 0); - if (DEBUG) { - System.err.print("glXChooseVisual recommended "); - if (recommendedVis == null) { - System.err.println("null visual"); - } else { - System.err.println("visual id " + toHexString(recommendedVis.getVisualid())); + // skipped if xvisualID is given + if( VisualIDHolder.VID_UNDEFINED == xvisualID ) { + recommendedVis = GLX.glXChooseVisual(display, screen, attribs); + if (DEBUG) { + System.err.print("glXChooseVisual recommended "); + if (recommendedVis == null) { + System.err.println("null visual"); + } else { + System.err.println("visual id " + toHexString(recommendedVis.getVisualid())); + } } } @@ -371,15 +424,41 @@ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationF } for (int i = 0; i < infos.length; i++) { - if( !X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities(availableCaps, glProfile, display, infos[i], winattrmask, isMultisampleAvailable) ) { - if(DEBUG) { - System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: XVisual invalid: ("+x11Screen+"): fbcfg: "+toHexString(infos[i].getVisualid())); - } - } else { + final GLCapabilitiesImmutable caps = X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities(absDevice, glProfile, infos[i], winattrmask, isMultisampleAvailable); + if( null != caps ) { + availableCaps.add(caps); // Attempt to find the visual chosenIndex by glXChooseVisual, if not translucent if (capsChosen.isBackgroundOpaque() && recommendedVis != null && recommendedVis.getVisualid() == infos[i].getVisualid()) { recommendedIndex = availableCaps.size() - 1; } + } else if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: XVisual invalid: ("+x11Screen+"): fbcfg: "+toHexString(infos[i].getVisualid())); + } + } + + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: got configs: "+availableCaps.size()); + for(int i=0; i<availableCaps.size(); i++) { + System.err.println(i+": "+availableCaps.get(i)); + } + } + + if( VisualIDHolder.VID_UNDEFINED != xvisualID ) { + for(int i=0; i<availableCaps.size(); ) { + VisualIDHolder vidh = availableCaps.get(i); + if(vidh.getVisualID(VIDType.X11_XVISUAL) != xvisualID ) { + availableCaps.remove(i); + } else { + i++; + } + } + if(0==availableCaps.size()) { + if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: post filter visualID "+toHexString(xvisualID )+" no config found, failed - return null"); + } + return null; + } else if(DEBUG) { + System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: post filter visualID "+toHexString(xvisualID)+" got configs: "+availableCaps.size()); } } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXContext.java deleted file mode 100644 index ce5d466d4..000000000 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXContext.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ -package jogamp.opengl.x11.glx; - -import javax.media.opengl.*; - -public class X11OnscreenGLXContext extends X11GLXContext { - - public X11OnscreenGLXContext(X11OnscreenGLXDrawable drawable, GLContext shareWith) { - super(drawable, shareWith); - } -} diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXDrawable.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXDrawable.java index b2a8326cb..9da189290 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXDrawable.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11OnscreenGLXDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,19 +29,21 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.x11.glx; -import javax.media.nativewindow.*; -import javax.media.opengl.*; +import javax.media.nativewindow.NativeSurface; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLException; public class X11OnscreenGLXDrawable extends X11GLXDrawable { /** GLXWindow can't be made current on AWT with NVidia driver, hence disabled for now */ @@ -49,33 +51,38 @@ public class X11OnscreenGLXDrawable extends X11GLXDrawable { long glXWindow; // GLXWindow, a GLXDrawable representation boolean useGLXWindow; - protected X11OnscreenGLXDrawable(GLDrawableFactory factory, NativeSurface component) { - super(factory, component, false); + protected X11OnscreenGLXDrawable(GLDrawableFactory factory, NativeSurface component, boolean realized) { + super(factory, component, realized); glXWindow=0; useGLXWindow=false; + if(realized) { + createHandle(); + } } - @SuppressWarnings("unused") @Override public long getHandle() { - if(USE_GLXWINDOW && useGLXWindow) { - return glXWindow; - } + if(USE_GLXWINDOW) { + if(useGLXWindow) { + return glXWindow; + } + } return super.getHandle(); } - @SuppressWarnings("unused") @Override protected void destroyHandle() { - if(USE_GLXWINDOW && 0!=glXWindow) { - GLX.glXDestroyWindow(getNativeSurface().getDisplayHandle(), glXWindow); - glXWindow = 0; - useGLXWindow=false; + if(USE_GLXWINDOW) { + if(0!=glXWindow) { + GLX.glXDestroyWindow(getNativeSurface().getDisplayHandle(), glXWindow); + glXWindow = 0; + useGLXWindow=false; + } } } @Override - protected final void updateHandle() { + protected final void createHandle() { if(USE_GLXWINDOW) { X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration)getNativeSurface().getGraphicsConfiguration(); if(config.getFBConfig()>=0) { @@ -84,7 +91,7 @@ public class X11OnscreenGLXDrawable extends X11GLXDrawable { if(0!=glXWindow) { GLX.glXDestroyWindow(dpy, glXWindow); } - glXWindow = GLX.glXCreateWindow(dpy, config.getFBConfig(), getNativeSurface().getSurfaceHandle(), null, 0); + glXWindow = GLX.glXCreateWindow(dpy, config.getFBConfig(), getNativeSurface().getSurfaceHandle(), null); if (DEBUG) { System.err.println("X11OnscreenGLXDrawable.setRealized(true): glXWindow: "+toHexString(getNativeSurface().getSurfaceHandle())+" -> "+toHexString(glXWindow)); } @@ -95,7 +102,8 @@ public class X11OnscreenGLXDrawable extends X11GLXDrawable { } } + @Override public GLContext createContext(GLContext shareWith) { - return new X11OnscreenGLXContext(this, shareWith); + return new X11GLXContext(this, shareWith); } } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXContext.java deleted file mode 100644 index 765a8207a..000000000 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXContext.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.x11.glx; - -import javax.media.opengl.*; - -public class X11PbufferGLXContext extends X11GLXContext { - - public X11PbufferGLXContext(X11PbufferGLXDrawable drawable, GLContext shareWith) { - super(drawable, shareWith); - } - - public void bindPbufferToTexture() { - // FIXME: figure out how to implement this - throw new GLException("Not yet implemented"); - } - - public void releasePbufferFromTexture() { - // FIXME: figure out how to implement this - throw new GLException("Not yet implemented"); - } - - - public int getFloatingPointMode() { - return ((X11PbufferGLXDrawable)drawable).getFloatingPointMode(); - } -} diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXDrawable.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXDrawable.java index da7b535cb..0e771fd0f 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXDrawable.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11PbufferGLXDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,32 +29,38 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.x11.glx; -import javax.media.opengl.*; -import javax.media.nativewindow.*; +import java.nio.IntBuffer; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.MutableSurface; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLException; + +import com.jogamp.common.nio.Buffers; public class X11PbufferGLXDrawable extends X11GLXDrawable { protected X11PbufferGLXDrawable(GLDrawableFactory factory, NativeSurface target) { - /* GLCapabilities caps, + /* GLCapabilities caps, GLCapabilitiesChooser chooser, int width, int height */ super(factory, target, false); } - protected void destroyImpl() { - setRealized(false); - } - + @Override protected void setRealizedImpl() { if(realized) { createPbuffer(); @@ -63,8 +69,9 @@ public class X11PbufferGLXDrawable extends X11GLXDrawable { } } + @Override public GLContext createContext(GLContext shareWith) { - return new X11PbufferGLXContext(this, shareWith); + return new X11GLXContext(this, shareWith); } protected void destroyPbuffer() { @@ -72,69 +79,50 @@ public class X11PbufferGLXDrawable extends X11GLXDrawable { if (ns.getSurfaceHandle() != 0) { GLX.glXDestroyPbuffer(ns.getDisplayHandle(), ns.getSurfaceHandle()); } - ((SurfaceChangeable)ns).setSurfaceHandle(0); + ((MutableSurface)ns).setSurfaceHandle(0); + if (DEBUG) { + System.err.println(getThreadName()+": Destroyed pbuffer " + this); + } } private void createPbuffer() { - X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration) getNativeSurface().getGraphicsConfiguration(); - AbstractGraphicsScreen aScreen = config.getScreen(); - AbstractGraphicsDevice aDevice = aScreen.getDevice(); - long display = aDevice.getHandle(); + final MutableSurface ms = (MutableSurface) getNativeSurface(); + final X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration) ms.getGraphicsConfiguration(); + final AbstractGraphicsScreen aScreen = config.getScreen(); + final AbstractGraphicsDevice aDevice = aScreen.getDevice(); + final long display = aDevice.getHandle(); if (DEBUG) { - System.out.println("Pbuffer config: " + config); + System.out.println(getThreadName()+": Pbuffer config: " + config); } if (display==0) { throw new GLException("Null display"); } - NativeSurface ns = getNativeSurface(); - - GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); - - if (chosenCaps.getPbufferRenderToTexture()) { - throw new GLException("Render-to-texture pbuffers not supported yet on X11"); - } - - if (chosenCaps.getPbufferRenderToTextureRectangle()) { - throw new GLException("Render-to-texture-rectangle pbuffers not supported yet on X11"); - } - // Create the p-buffer. int niattribs = 0; - int[] iattributes = new int[5]; + IntBuffer iattributes = Buffers.newDirectIntBuffer(7); - iattributes[niattribs++] = GLX.GLX_PBUFFER_WIDTH; - iattributes[niattribs++] = ns.getWidth(); - iattributes[niattribs++] = GLX.GLX_PBUFFER_HEIGHT; - iattributes[niattribs++] = ns.getHeight(); - iattributes[niattribs++] = 0; + iattributes.put(niattribs++, GLX.GLX_PBUFFER_WIDTH); + iattributes.put(niattribs++, ms.getWidth()); + iattributes.put(niattribs++, GLX.GLX_PBUFFER_HEIGHT); + iattributes.put(niattribs++, ms.getHeight()); + iattributes.put(niattribs++, GLX.GLX_LARGEST_PBUFFER); // exact + iattributes.put(niattribs++, 0); + iattributes.put(niattribs++, 0); - long pbuffer = GLX.glXCreatePbuffer(display, config.getFBConfig(), iattributes, 0); + long pbuffer = GLX.glXCreatePbuffer(display, config.getFBConfig(), iattributes); if (pbuffer == 0) { // FIXME: query X error code for detail error message throw new GLException("pbuffer creation error: glXCreatePbuffer() failed"); } // Set up instance variables - ((SurfaceChangeable)ns).setSurfaceHandle(pbuffer); - - // Determine the actual width and height we were able to create. - int[] tmp = new int[1]; - GLX.glXQueryDrawable(display, pbuffer, GLX.GLX_WIDTH, tmp, 0); - int width = tmp[0]; - GLX.glXQueryDrawable(display, pbuffer, GLX.GLX_HEIGHT, tmp, 0); - int height = tmp[0]; - ((SurfaceChangeable)ns).surfaceSizeChanged(width, height); - + ms.setSurfaceHandle(pbuffer); + if (DEBUG) { - System.err.println("Created pbuffer " + this); + System.err.println(getThreadName()+": Created pbuffer " + this); } } - - public int getFloatingPointMode() { - // Floating-point pbuffers currently require NVidia hardware on X11 - return GLPbuffer.NV_FLOAT; - } } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXContext.java deleted file mode 100644 index e19dfd1b3..000000000 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXContext.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogamp.opengl.x11.glx; - -import javax.media.opengl.*; - -public class X11PixmapGLXContext extends X11GLXContext { - - public X11PixmapGLXContext(X11PixmapGLXDrawable drawable, - GLContext shareWith) { - super(drawable, shareWith); - } - - public int getOffscreenContextPixelDataType() { - GL gl = getGL(); - return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; - } - - public int getOffscreenContextReadBuffer() { - GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable)drawable.getNativeSurface().getGraphicsConfiguration().getChosenCapabilities(); - if (caps.getDoubleBuffered()) { - return GL.GL_BACK; - } - return GL.GL_FRONT; - } - - public boolean offscreenImageNeedsVerticalFlip() { - // There doesn't seem to be a way to do this in the construction - // of the Pixmap or GLXPixmap - return true; - } -} diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXDrawable.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXDrawable.java index 7dae20f80..c1388db8a 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXDrawable.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11PixmapGLXDrawable.java @@ -1,22 +1,22 @@ /* * 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 * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,20 +29,27 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.x11.glx; -import javax.media.nativewindow.*; -import javax.media.opengl.*; -import jogamp.nativewindow.x11.*; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.MutableSurface; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLException; + +import jogamp.nativewindow.x11.X11Lib; +import jogamp.nativewindow.x11.XVisualInfo; public class X11PixmapGLXDrawable extends X11GLXDrawable { private long pixmap; @@ -51,10 +58,7 @@ public class X11PixmapGLXDrawable extends X11GLXDrawable { super(factory, target, false); } - protected void destroyImpl() { - setRealized(false); - } - + @Override protected void setRealizedImpl() { if(realized) { createPixmap(); @@ -63,10 +67,11 @@ public class X11PixmapGLXDrawable extends X11GLXDrawable { } } + @Override public GLContext createContext(GLContext shareWith) { - return new X11PixmapGLXContext(this, shareWith); + return new X11GLXContext(this, shareWith); } - + private void createPixmap() { NativeSurface ns = getNativeSurface(); X11GLXGraphicsConfiguration config = (X11GLXGraphicsConfiguration) ns.getGraphicsConfiguration(); @@ -88,9 +93,9 @@ public class X11PixmapGLXDrawable extends X11GLXDrawable { pixmap = 0; throw new GLException("glXCreateGLXPixmap failed"); } - ((SurfaceChangeable)ns).setSurfaceHandle(drawable); + ((MutableSurface)ns).setSurfaceHandle(drawable); if (DEBUG) { - System.err.println("Created pixmap " + toHexString(pixmap) + + System.err.println(getThreadName()+": Created pixmap " + toHexString(pixmap) + ", GLXPixmap " + toHexString(drawable) + ", display " + toHexString(dpy)); } @@ -104,7 +109,7 @@ public class X11PixmapGLXDrawable extends X11GLXDrawable { long drawable = ns.getSurfaceHandle(); if (DEBUG) { - System.err.println("Destroying pixmap " + toHexString(pixmap) + + System.err.println(getThreadName()+": Destroying pixmap " + toHexString(pixmap) + ", GLXPixmap " + toHexString(drawable) + ", display " + toHexString(display)); } @@ -128,7 +133,7 @@ public class X11PixmapGLXDrawable extends X11GLXDrawable { X11Lib.XFreePixmap(display, pixmap); drawable = 0; pixmap = 0; - ((SurfaceChangeable)ns).setSurfaceHandle(0); + ((MutableSurface)ns).setSurfaceHandle(0); display = 0; } } |