diff options
Diffstat (limited to 'src/nativewindow')
18 files changed, 1324 insertions, 252 deletions
diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/GenericUpstreamSurfacelessHook.java b/src/nativewindow/classes/com/jogamp/nativewindow/GenericUpstreamSurfacelessHook.java new file mode 100644 index 000000000..3fd5ad902 --- /dev/null +++ b/src/nativewindow/classes/com/jogamp/nativewindow/GenericUpstreamSurfacelessHook.java @@ -0,0 +1,88 @@ +/** + * 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 com.jogamp.nativewindow; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.nativewindow.UpstreamSurfaceHook; + +import com.jogamp.nativewindow.UpstreamSurfaceHookMutableSize; + +public class GenericUpstreamSurfacelessHook extends UpstreamSurfaceHookMutableSize { + /** + * @param width the initial width as returned by {@link NativeSurface#getSurfaceWidth()} via {@link UpstreamSurfaceHook#getSurfaceWidth(ProxySurface)}, + * not the actual dummy surface width. + * The latter is platform specific and small + * @param height the initial height as returned by {@link NativeSurface#getSurfaceHeight()} via {@link UpstreamSurfaceHook#getSurfaceHeight(ProxySurface)}, + * not the actual dummy surface height, + * The latter is platform specific and small + */ + public GenericUpstreamSurfacelessHook(final int width, final int height) { + super(width, height); + } + + @Override + public final void create(final ProxySurface s) { + final AbstractGraphicsDevice device = s.getGraphicsConfiguration().getScreen().getDevice(); + device.lock(); + try { + if(0 == device.getHandle()) { + device.open(); + s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + if( 0 != s.getSurfaceHandle() ) { + throw new InternalError("Upstream surface not null: "+s); + } + s.addUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_SURFACELESS | + ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE | + ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE ); + } finally { + device.unlock(); + } + } + + @Override + public final void destroy(final ProxySurface s) { + if( s.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + final AbstractGraphicsDevice device = s.getGraphicsConfiguration().getScreen().getDevice(); + if( !s.containsUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_SURFACELESS ) ) { + throw new InternalError("Owns upstream surface, but not a valid zero surface: "+s); + } + if( 0 != s.getSurfaceHandle() ) { + throw new InternalError("Owns upstream valid zero surface, but non zero surface: "+s); + } + device.lock(); + try { + s.clearUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_SURFACELESS | ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); + } finally { + device.unlock(); + } + } + } +} diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java index c7055099f..0f103bfb3 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/DirectDataBufferInt.java @@ -36,6 +36,7 @@ import java.awt.image.DirectColorModel; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; +import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.Hashtable; @@ -54,9 +55,12 @@ public final class DirectDataBufferInt extends DataBuffer { public static class BufferedImageInt extends BufferedImage { final int customImageType; - public BufferedImageInt (final int customImageType, final ColorModel cm, final WritableRaster raster, final Hashtable<?,?> properties) { + final DirectDataBufferInt dataBuffer; + public BufferedImageInt (final int customImageType, final ColorModel cm, + final DirectDataBufferInt dataBuffer, final WritableRaster raster, final Hashtable<?,?> properties) { super(cm, raster, false /* isRasterPremultiplied */, properties); this.customImageType = customImageType; + this.dataBuffer = dataBuffer; } /** @@ -68,6 +72,11 @@ public final class DirectDataBufferInt extends DataBuffer { return customImageType; } + /** + * Returns the underlying {@link DirectDataBufferInt} associated with this instance via {@link Raster} {@link #getRaster() instance}. + */ + public DirectDataBufferInt getDataBuffer() { return dataBuffer; } + @Override public String toString() { return "BufferedImageInt@"+Integer.toHexString(hashCode()) @@ -163,14 +172,18 @@ public final class DirectDataBufferInt extends DataBuffer { // final WritableRaster raster = new SunWritableRaster(sppsm, dataBuffer, location); final WritableRaster raster = new DirectWritableRaster(sppsm, dataBuffer, location); - return new BufferedImageInt(imageType, colorModel, raster, properties); + return new BufferedImageInt(imageType, colorModel, dataBuffer, raster, properties); } - /** Default data bank. */ - private final IntBuffer data; + /** Default NIO data bank storage, {@link ByteBuffer} representation. */ + private final ByteBuffer dataBytes; + /** Default NIO data bank storage, {@link IntBuffer} representation. */ + private final IntBuffer dataInts; - /** All data banks */ - private final IntBuffer bankdata[]; + /** All NIO data banks, {@link ByteBuffer} representation. */ + private final ByteBuffer bankdataBytes[]; + /** All NIO data banks, {@link IntBuffer} representation. */ + private final IntBuffer bankdataInts[]; /** * Constructs an nio integer-based {@link DataBuffer} with a single bank @@ -180,9 +193,12 @@ public final class DirectDataBufferInt extends DataBuffer { */ public DirectDataBufferInt(final int size) { super(TYPE_INT, size); - data = Buffers.newDirectIntBuffer(size); - bankdata = new IntBuffer[1]; - bankdata[0] = data; + dataBytes = Buffers.newDirectByteBuffer(size * Buffers.SIZEOF_INT); + dataInts = dataBytes.asIntBuffer(); + bankdataBytes = new ByteBuffer[1]; + bankdataInts = new IntBuffer[1]; + bankdataBytes[0] = dataBytes; + bankdataInts[0] = dataInts; } /** @@ -194,11 +210,14 @@ public final class DirectDataBufferInt extends DataBuffer { */ public DirectDataBufferInt(final int size, final int numBanks) { super(TYPE_INT,size,numBanks); - bankdata = new IntBuffer[numBanks]; + bankdataBytes = new ByteBuffer[numBanks]; + bankdataInts = new IntBuffer[numBanks]; for (int i= 0; i < numBanks; i++) { - bankdata[i] = Buffers.newDirectIntBuffer(size); + bankdataBytes[i] = Buffers.newDirectByteBuffer(size * Buffers.SIZEOF_INT); + bankdataInts[i] = bankdataBytes[i].asIntBuffer(); } - data = bankdata[0]; + dataBytes = bankdataBytes[0]; + dataInts = bankdataInts[0]; } /** @@ -210,33 +229,57 @@ public final class DirectDataBufferInt extends DataBuffer { * hold <code>size</code> elements. * </p> * - * @param dataArray The integer array for the {@link DataBuffer}. + * @param dataArray The NIO {@link ByteBuffer} array, holding the integer data for the {@link DataBuffer}. * @param size The size of the {@link DataBuffer} bank. */ - public DirectDataBufferInt(final IntBuffer dataArray, final int size) { + public DirectDataBufferInt(final ByteBuffer dataArray, final int size) { super(TYPE_INT,size); - data = dataArray; - bankdata = new IntBuffer[1]; - bankdata[0] = data; + dataBytes = Buffers.nativeOrder(dataArray); + dataInts = dataBytes.asIntBuffer(); + bankdataBytes = new ByteBuffer[1]; + bankdataInts = new IntBuffer[1]; + bankdataBytes[0] = dataBytes; + bankdataInts[0] = dataInts; } /** - * Returns the default (first) int data array in {@link DataBuffer}. + * Returns the default (first) int data array in {@link DataBuffer} as an {@link IntBuffer} representation. * * @return The first integer data array. + * @see #getDataBytes() */ public IntBuffer getData() { - return data; + return dataInts; + } + /** + * Returns the default (first) int data array in {@link DataBuffer} as a {@link ByteBuffer} representation. + * + * @return The first integer data array. + * @see #getData() + */ + public ByteBuffer getDataBytes() { + return dataBytes; } /** - * Returns the data array for the specified bank. + * Returns the data array for the specified bank as an {@link IntBuffer} representation. * * @param bank The bank whose data array you want to get. * @return The data array for the specified bank. + * @see #getDataBytes(int) */ public IntBuffer getData(final int bank) { - return bankdata[bank]; + return bankdataInts[bank]; + } + /** + * Returns the data array for the specified bank as a {@link ByteBuffer} representation. + * + * @param bank The bank whose data array you want to get. + * @return The data array for the specified bank. + * @see #getData(int) + */ + public ByteBuffer getDataBytes(final int bank) { + return bankdataBytes[bank]; } /** @@ -249,7 +292,7 @@ public final class DirectDataBufferInt extends DataBuffer { */ @Override public int getElem(final int i) { - return data.get(i+offset); + return dataInts.get(i+offset); } /** @@ -263,7 +306,7 @@ public final class DirectDataBufferInt extends DataBuffer { */ @Override public int getElem(final int bank, final int i) { - return bankdata[bank].get(i+offsets[bank]); + return bankdataInts[bank].get(i+offsets[bank]); } /** @@ -277,7 +320,7 @@ public final class DirectDataBufferInt extends DataBuffer { */ @Override public void setElem(final int i, final int val) { - data.put(i+offset, val); + dataInts.put(i+offset, val); } /** @@ -291,7 +334,7 @@ public final class DirectDataBufferInt extends DataBuffer { */ @Override public void setElem(final int bank, final int i, final int val) { - bankdata[bank].put(i+offsets[bank], val); + bankdataInts[bank].put(i+offsets[bank], val); } } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java index 6498ebd1e..32e4add75 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java @@ -37,6 +37,7 @@ package com.jogamp.nativewindow.awt; +import com.jogamp.common.ExceptionUtils; import com.jogamp.common.os.Platform; import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.common.util.locks.LockFactory; @@ -558,7 +559,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, surfaceLock.unlock(); if(DEBUG) { System.err.println("JAWTWindow: Can't lock surface, component peer n/a. Component displayable "+component.isDisplayable()+", "+component); - Thread.dumpStack(); + ExceptionUtils.dumpStack(System.err); } } else { determineIfApplet(); @@ -765,7 +766,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, // avoid deadlock .. if(DEBUG) { System.err.println("Warning: JAWT Lock hold, but not the AWT tree lock: "+this); - Thread.dumpStack(); + ExceptionUtils.dumpStack(System.err); } if( null == storage ) { storage = new Point(); @@ -788,7 +789,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, if(LOCK_SURFACE_NOT_READY == lockRes) { if(DEBUG) { System.err.println("Warning: JAWT Lock couldn't be acquired: "+this); - Thread.dumpStack(); + ExceptionUtils.dumpStack(System.err); } return null; } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/egl/EGLGraphicsDevice.java b/src/nativewindow/classes/com/jogamp/nativewindow/egl/EGLGraphicsDevice.java index d21994ea5..a38a48b85 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/egl/EGLGraphicsDevice.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/egl/EGLGraphicsDevice.java @@ -34,11 +34,14 @@ package com.jogamp.nativewindow.egl; import javax.media.nativewindow.*; +import com.jogamp.common.util.VersionNumber; + /** Encapsulates a graphics device on EGL platforms. */ public class EGLGraphicsDevice extends DefaultGraphicsDevice implements Cloneable { - final long[] nativeDisplayID = new long[1]; - /* final */ EGLDisplayLifecycleCallback eglLifecycleCallback; + private final long[] nativeDisplayID = new long[1]; + private /* final */ EGLDisplayLifecycleCallback eglLifecycleCallback; + private VersionNumber eglVersion = VersionNumber.zeroVersion; /** * Hack to allow inject a EGL termination call. @@ -52,9 +55,11 @@ public class EGLGraphicsDevice extends DefaultGraphicsDevice implements Cloneabl * Implementation should issue an <code>EGL.eglGetDisplay(nativeDisplayID)</code> * inclusive <code>EGL.eglInitialize(eglDisplayHandle, ..)</code> call. * @param nativeDisplayID in/out array of size 1, passing the requested nativeVisualID, may return a different revised nativeVisualID handle + * @param major out array for EGL major version + * @param minor out array for EGL minor version * @return the initialized EGL display ID, or <code>0</code> if not successful */ - public long eglGetAndInitDisplay(long[] nativeDisplayID); + public long eglGetAndInitDisplay(final long[] nativeDisplayID, final int[] major, final int[] minor); /** * Implementation should issue an <code>EGL.eglTerminate(eglDisplayHandle)</code> call. @@ -79,6 +84,9 @@ public class EGLGraphicsDevice extends DefaultGraphicsDevice implements Cloneabl this.eglLifecycleCallback = eglLifecycleCallback; } + /** EGL server version as returned by {@code eglInitialize(..)}. Only valid after {@link #open()}. */ + public VersionNumber getEGLVersion() { return eglVersion; } + public long getNativeDisplayID() { return nativeDisplayID[0]; } @Override @@ -98,11 +106,16 @@ public class EGLGraphicsDevice extends DefaultGraphicsDevice implements Cloneabl if(DEBUG) { System.err.println(Thread.currentThread().getName() + " - EGLGraphicsDevice.open(): "+this); } - handle = eglLifecycleCallback.eglGetAndInitDisplay(nativeDisplayID); + final int[] major = { 0 }; + final int[] minor = { 0 }; + handle = eglLifecycleCallback.eglGetAndInitDisplay(nativeDisplayID, major, minor); if(0 == handle) { + eglVersion = VersionNumber.zeroVersion; throw new NativeWindowException("EGLGraphicsDevice.open() failed: "+this); + } else { + eglVersion = new VersionNumber(major[0], minor[0], 0); + return true; } - return true; } return false; } @@ -142,5 +155,10 @@ public class EGLGraphicsDevice extends DefaultGraphicsDevice implements Cloneabl eglLifecycleCallback = (EGLDisplayLifecycleCallback) newOwnership; return oldOwnership; } + + @Override + public String toString() { + return getClass().getSimpleName()+"[type "+getType()+", v"+eglVersion+", connection "+getConnection()+", unitID "+getUnitID()+", handle 0x"+Long.toHexString(getHandle())+", owner "+isHandleOwner()+", "+toolkitLock+"]"; + } } diff --git a/src/nativewindow/classes/javax/media/nativewindow/GraphicsConfigurationFactory.java b/src/nativewindow/classes/javax/media/nativewindow/GraphicsConfigurationFactory.java index 3f8113baa..c73f544b0 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/GraphicsConfigurationFactory.java +++ b/src/nativewindow/classes/javax/media/nativewindow/GraphicsConfigurationFactory.java @@ -33,7 +33,9 @@ package javax.media.nativewindow; +import com.jogamp.common.ExceptionUtils; import com.jogamp.common.util.ReflectionUtil; + import jogamp.nativewindow.Debug; import jogamp.nativewindow.DefaultGraphicsConfigurationFactoryImpl; @@ -226,7 +228,7 @@ public abstract class GraphicsConfigurationFactory { throw new IllegalArgumentException("Given capabilities class must implement CapabilitiesImmutable"); } if(DEBUG) { - Thread.dumpStack(); + ExceptionUtils.dumpStack(System.err); System.err.println("GraphicsConfigurationFactory.getFactory: "+deviceType.getName()+", "+capabilitiesType.getName()); dumpFactories(); } diff --git a/src/nativewindow/classes/javax/media/nativewindow/ProxySurface.java b/src/nativewindow/classes/javax/media/nativewindow/ProxySurface.java index 7a69b9a40..377dd55cd 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/ProxySurface.java +++ b/src/nativewindow/classes/javax/media/nativewindow/ProxySurface.java @@ -65,6 +65,14 @@ public interface ProxySurface extends MutableSurface { */ public static final int OPT_UPSTREAM_WINDOW_INVISIBLE = 1 << 8; + /** + * Implementation specific bitvalue stating the upstream's {@link NativeSurface}'s zero handle is valid. + * @see #addUpstreamOptionBits(int) + * @see #clearUpstreamOptionBits(int) + * @see #getUpstreamOptionBits() + */ + public static final int OPT_UPSTREAM_SURFACELESS = 1 << 9; + /** Allow redefining the AbstractGraphicsConfiguration */ public void setGraphicsConfiguration(AbstractGraphicsConfiguration cfg); diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormat.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormat.java index e5901f584..2016b2885 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormat.java +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormat.java @@ -28,6 +28,10 @@ package javax.media.nativewindow.util; +import java.util.Arrays; + +import com.jogamp.common.util.IntBitfield; + /** * Basic pixel formats * <p> @@ -60,7 +64,7 @@ package javax.media.nativewindow.util; */ public enum PixelFormat { /** - * Pixel size is 1 bytes (8 bits) with one component of size 1 byte (8 bits). + * Stride is 8 bits, 8 bits per pixel, 1 component of 8 bits. * Compatible with: * <ul> * <li>OpenGL: data-format GL_ALPHA (< GL3), GL_RED (>= GL3), data-type GL_UNSIGNED_BYTE</li> @@ -68,34 +72,138 @@ public enum PixelFormat { * </ul> * </p> */ - LUMINANCE(1, 8), + LUMINANCE(new CType[]{ CType.Y }, 1, 8, 8), /** - * Pixel size is 3 bytes (24 bits) with each component of size 1 byte (8 bits). + * Stride is 16 bits, 16 bits per pixel, 3 {@link PackedComposition#isUniform() discrete} components. + * <p> + * The {@link PackedComposition#isUniform() discrete} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>R: 0x1F << 0</li> + * <li>G: 0x3F << 5</li> + * <li>B: 0x1F << 11</li> + * </ol> + * </p> * <p> - * The components are interleaved in the order: + * Compatible with: * <ul> - * <li>Low to High: R, G, B</li> + * <li>OpenGL: data-format GL_RGB, data-type GL_UNSIGNED_SHORT_5_6_5_REV</li> + * <li>AWT: <i>None</i></li> * </ul> * </p> + */ + RGB565(new CType[]{ CType.R, CType.G, CType.B }, + new int[]{ 0x1F, 0x3F, 0x1F }, + new int[]{ 0, 5, 5+6 }, + 16 ), + + /** + * Stride is 16 bits, 16 bits per pixel, 3 {@link PackedComposition#isUniform() discrete} components. + * <p> + * The {@link PackedComposition#isUniform() discrete} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>B: 0x1F << 0</li> + * <li>G: 0x3F << 5</li> + * <li>R: 0x1F << 11</li> + * </ol> + * </p> * <p> * Compatible with: * <ul> - * <li>OpenGL: data-format GL_RGB, data-type GL_UNSIGNED_BYTE</li> + * <li>OpenGL: data-format GL_RGB, data-type GL_UNSIGNED_SHORT_5_6_5</li> + * <li>AWT: <i>None</i></li> + * </ul> + * </p> + */ + BGR565(new CType[]{ CType.B, CType.G, CType.R}, + new int[]{ 0x1F, 0x3F, 0x1F }, + new int[]{ 0, 5, 5+6 }, + 16 ), + + /** + * Stride is 16 bits, 16 bits per pixel, 4 {@link PackedComposition#isUniform() discrete} components. + * <p> + * The {@link PackedComposition#isUniform() discrete} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>R: 0x1F << 0</li> + * <li>G: 0x1F << 5</li> + * <li>B: 0x1F << 10</li> + * <li>A: 0x01 << 15</li> + * </ol> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_SHORT_1_5_5_5_REV</li> + * <li>AWT: <i>None</i></li> + * </ul> + * </p> + */ + RGBA5551(new CType[]{ CType.R, CType.G, CType.B, CType.A}, + new int[]{ 0x1F, 0x1F, 0x1F, 0x01 }, + new int[]{ 0, 5, 5+5, 5+5+5 }, + 16 ), + + /** + * Stride is 16 bits, 16 bits per pixel, 4 {@link PackedComposition#isUniform() discrete} components. + * <p> + * The {@link PackedComposition#isUniform() discrete} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>A: 0x01 << 0</li> + * <li>B: 0x1F << 1</li> + * <li>G: 0x1F << 6</li> + * <li>R: 0x1F << 11</li> + * </ol> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_SHORT_5_5_5_1</li> * <li>AWT: <i>None</i></li> * </ul> * </p> */ - RGB888(3, 24), + ABGR1555(new CType[]{ CType.A, CType.B, CType.G, CType.R }, + new int[]{ 0x01, 0x1F, 0x1F, 0x1F }, + new int[]{ 0, 1, 1+5, 1+5+5 }, + 16 ), /** - * Pixel size is 3 bytes (24 bits) with each component of size 1 byte (8 bits). + * Stride 24 bits, 24 bits per pixel, 3 {@link PackedComposition#isUniform() uniform} components of 8 bits. + * <p> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>R: 0xFF << 0</li> + * <li>G: 0xFF << 8</li> + * <li>B: 0xFF << 16</li> + * </ol> + * </p> * <p> - * The components are interleaved in the order: + * Compatible with: * <ul> - * <li>Low to High: B, G, R</li> + * <li>OpenGL: data-format GL_RGB, data-type GL_UNSIGNED_BYTE</li> + * <li>AWT: <i>None</i></li> * </ul> * </p> + */ + RGB888(new CType[]{ CType.R, CType.G, CType.B }, 3, 8, 24), + + /** + * Stride is 24 bits, 24 bits per pixel, 3 {@link PackedComposition#isUniform() uniform} components of of 8 bits. + * <p> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>B: 0xFF << 0</li> + * <li>G: 0xFF << 8</li> + * <li>R: 0xFF << 16</li> + * </ol> + * </p> * <p> * Compatible with: * <ul> @@ -104,16 +212,62 @@ public enum PixelFormat { * </ul> * </p> */ - BGR888(3, 24), + BGR888(new CType[]{ CType.B, CType.G, CType.R }, 3, 8, 24), + + /** + * Stride is 32 bits, 24 bits per pixel, 3 {@link PackedComposition#isUniform() uniform} components of 8 bits. + * <p> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>R: 0xFF << 0</li> + * <li>G: 0xFF << 8</li> + * <li>B: 0xFF << 16</li> + * </ol> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_BYTE, with alpha discarded!</li> + * <li>AWT: {@link java.awt.image.BufferedImage#TYPE_INT_BGR TYPE_INT_BGR}</li> + * </ul> + * </p> + */ + RGBx8888(new CType[]{ CType.R, CType.G, CType.B }, 3, 8, 32), /** - * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * Stride is 32 bits, 24 bits per pixel, 3 {@link PackedComposition#isUniform() uniform} components of 8 bits. + * <p> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>B: 0xFF << 0</li> + * <li>G: 0xFF << 8</li> + * <li>R: 0xFF << 16</li> + * </ol> + * </p> * <p> - * The components are interleaved in the order: + * Compatible with: * <ul> - * <li>Low to High: R, G, B, A</li> + * <li>OpenGL: data-format GL_BGRA, data-type GL_UNSIGNED_BYTE - with alpha discarded!</li> + * <li>AWT: {@link java.awt.image.BufferedImage#TYPE_INT_RGB TYPE_INT_RGB}</li> * </ul> * </p> + */ + BGRx8888(new CType[]{ CType.B, CType.G, CType.R }, 3, 8, 32), + + /** + * Stride is 32 bits, 32 bits per pixel, 4 {@link PackedComposition#isUniform() uniform} components of 8 bits. + * <p> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>R: 0xFF << 0</li> + * <li>G: 0xFF << 8</li> + * <li>B: 0xFF << 16</li> + * <li>A: 0xFF << 24</li> + * </ol> + * </p> * <p> * Compatible with: * <ul> @@ -125,33 +279,41 @@ public enum PixelFormat { * </ul> * </p> */ - RGBA8888(4, 32), + RGBA8888(new CType[]{ CType.R, CType.G, CType.B, CType.A }, 4, 8, 32), /** - * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * Stride is 32 bits, 32 bits per pixel, 4 {@link PackedComposition#isUniform() uniform} components of 8 bits. * <p> - * The components are interleaved in the order: - * <ul> - * <li>Low to High: A, B, G, R</li> - * </ul> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>A: 0xFF << 0</li> + * <li>B: 0xFF << 8</li> + * <li>G: 0xFF << 16</li> + * <li>R: 0xFF << 24</li> + * </ol> * </p> * <p> * Compatible with: * <ul> - * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_8_8_8_8</li> + * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_INT_8_8_8_8</li> * <li>AWT: {@link java.awt.image.BufferedImage#TYPE_4BYTE_ABGR TYPE_4BYTE_ABGR}</li> * </ul> * </p> */ - ABGR8888(4, 32), + ABGR8888(new CType[]{ CType.A, CType.B, CType.G, CType.R }, 4, 8, 32), /** - * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * Stride is 32 bits, 32 bits per pixel, 4 {@link PackedComposition#isUniform() uniform} components of 8 bits. * <p> - * The components are interleaved in the order: - * <ul> - * <li>Low to High: A, R, G, B</li> - * </ul> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>A: 0xFF << 0</li> + * <li>R: 0xFF << 8</li> + * <li>G: 0xFF << 16</li> + * <li>B: 0xFF << 24</li> + * </ol> * </p> * <p> * Compatible with: @@ -161,15 +323,19 @@ public enum PixelFormat { * </ul> * </p> */ - ARGB8888(4, 32), + ARGB8888(new CType[]{ CType.A, CType.R, CType.G, CType.B }, 4, 8, 32), /** - * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * Stride is 32 bits, 32 bits per pixel, 4 {@link PackedComposition#isUniform() uniform} components of 8 bits. * <p> - * The components are interleaved in the order: - * <ul> - * <li>Low to High: B, G, R, A</li> - * </ul> + * The {@link PackedComposition#isUniform() uniform} {@link PixelFormat#composition components} + * are interleaved in the order Low to High: + * <ol> + * <li>B: 0xFF << 0</li> + * <li>G: 0xFF << 8</li> + * <li>R: 0xFF << 16</li> + * <li>A: 0xFF << 24</li> + * </ol> * </p> * <p> * Compatible with: @@ -181,17 +347,393 @@ public enum PixelFormat { * </ul> * </p> */ - BGRA8888(4, 32); + BGRA8888(new CType[]{ CType.B, CType.G, CType.R, CType.A }, 4, 8, 32); + + /** Unique {@link Composition Pixel Composition}, i.e. layout of its components. */ + public final Composition comp; + + /** + * @param componentOrder {@link CType Component type} order of all components, see {@link Composition#componentBitMask()}. + * @param componentCount number of components + * @param bpc bits per component + * @param bitStride stride bits to next pixel + */ + private PixelFormat(final CType[] componentOrder, final int componentCount, final int bpc, final int bitStride) { + this.comp = new PackedComposition(componentOrder, componentCount, bpc, bitStride); + } + + /** + * @param componentOrder {@link CType Component type} order of all components, see {@link Composition#componentBitMask()}. + * @param componentMask bit-mask of of all components, see {@link Composition##componentBitMask()}. + * @param componentBitShift bit-shift of all components, see {@link Composition##componentBitMask()}. + * @param bitStride stride bits to next pixel + */ + private PixelFormat(final CType[] componentOrder, final int[] componentMask, final int[] componentBitShift, final int bitStride) { + this.comp = new PackedComposition(componentOrder, componentMask, componentBitShift, bitStride); + } + + /** + * Returns the unique matching {@link PixelFormat} of the given {@link Composition} + * or {@code null} if none is available. + */ + public static PixelFormat valueOf(final Composition comp) { + final PixelFormat[] all = PixelFormat.values(); + for(int i=all.length-1; i>=0; i--) { + final PixelFormat pf = all[i]; + if( comp.hashCode() == pf.comp.hashCode() && comp.equals(pf.comp) ) { + return pf; + } + } + return null; + } + + /** Component types */ + public static enum CType { + /** Red component */ + R, + /** Green component */ + G, + /** Blue component */ + B, + /** Alpha component */ + A, + /** Luminance component, e.g. grayscale or Y of YUV */ + Y, + /** U component of YUV */ + U, + /** V component of YUV */ + V; + } + + /** + * Pixel composition, i.e. layout of its components. + */ + public static interface Composition { + /** {@value} */ + public static final int UNDEF = -1; + + /** + * Returns {@code true} if all components are of same bit-size, e.g. {@link PixelFormat#RGBA8888 RGBA8888}, + * otherwise {@code false}, e.g. {@link PixelFormat#RGBA5551 RGBA5551} + */ + boolean isUniform(); + + /** + * Returns {@code true} if all components are packed, i.e. interleaved, e.g. {@link PixelFormat#RGBA8888 RGBA8888}, + * otherwise {@code false}. + */ + boolean isInterleaved(); + + /** Number of components per pixel, e.g. 3 for {@link PixelFormat#RGBx8888 RGBx8888}. */ + int componenCount(); + /** Number of bits per pixel, e.g. 24 bits for {@link PixelFormat#RGBx8888 RGBx8888}. */ + int bitsPerPixel(); + /** + * Bit distance between pixels. + * <p> + * For packed pixels e.g. 32 bits for {@link PixelFormat#RGBx8888 RGBx8888}. + * </p> + */ + int bitStride(); + /** Number of bytes per pixel, i.e. packed {@link #bitStride()} in bytes, e.g. 4 for {@link PixelFormat#RGBx8888 RGBx8888}. */ + int bytesPerPixel(); + /** + * Returns the {@link CType Component type} order of all components, see {@link #componentBitMask()}. + */ + CType[] componentOrder(); + /** + * Returns the index of given {@link CType} within {@link #componentOrder()}, -1 if not exists. + */ + int find(final PixelFormat.CType s); + /** + * Returns the un-shifted bit-mask of all components. + * <p> + * Components mask is returned in the order Low-Index to High-Index, e.g.: + * <ul> + * <li>{@link PixelFormat#RGB565 RGB565}: 0: R 0x1F, 1: G 0x3F, 2: B 0x1F</li> + * <li>{@link PixelFormat#RGBA5551 RGBA5551}: 0: R 0x1F, 1: G 0x1F, 2: B 0x1F, 3: A 0x01</li> + * <li>{@link PixelFormat#RGBA8888 RGBA8888}: 0: R 0xFF, 1: G 0xFF, 2: B 0xFF, 3: A 0xFF</li> + * </ul> + * </p> + * <p> + */ + int[] componentBitMask(); + /** + * Returns the number of bits of all components, see {@link #componentBitMask()}. + */ + int[] componentBitCount(); + /** + * Returns the bit-shift of all components, see {@link #componentBitMask()}. + */ + int[] componentBitShift(); + + /** + * Decodes a component from the shifted pixel data with a {@link #bytesPerPixel()} of up to 32bit. + * @param shifted complete pixel encoded into on 32bit integer + * @param cIdx the desired component index + * @return the decoded component value + */ + int decodeSingleI32(final int shifted, final int cIdx); + /** + * Decodes a component from the shifted pixel data with a {@link #bytesPerPixel()} of up to 64bit. + * @param shifted complete pixel encoded into on 64bit integer + * @param cIdx the desired component index + * @return the decoded component value + */ + int decodeSingleI64(final long shifted, final int cIdx); + + int encodeSingleI32(final int norm, final int cIdx); + long encodeSingleI64(final int norm, final int cIdx); + + int encode3CompI32(final int c1NormI32, final int c2NormI32, final int c3NormI32); + int encode4CompI32(final int c1NormI32, final int c2NormI32, final int c3NormI32, final int c4NormI32); + + int encodeSingleI8(final byte normalI8, final int cIdx); + int encode3CompI8(final byte c1NormI8, final byte c2NormI8, final byte c3NormI8); + int encode4CompI8(final byte c1NormI8, final byte c2NormI8, final byte c3NormI8, final byte c4NormI8); + + float toFloat(final int i32, final int cIdx, final boolean i32Shifted); + int fromFloat(final float f, final int cIdx, final boolean shiftResult); + + int defaultValue(final int cIdx, final boolean shiftResult); + + /** + * Returns cached immutable hash value, see {@link Object#hashCode()}. + */ + int hashCode(); + /** + * Returns {@link Object#equals(Object)} + */ + boolean equals(final Object o); + + /** + * Returns {@link Object#toString()}. + */ + String toString(); + } + + /** + * Packed pixel composition, see {@link Composition}. + * <p> + * Components are interleaved, i.e. packed. + * </p> + */ + public static class PackedComposition implements Composition { + private final CType[] compOrder; + private final int[] compMask; + private final int[] compBitCount; + private final int[] compBitShift; + private final int bitsPerPixel; + private final int bitStride; + private final boolean uniform; + private final int hashCode; + + public final String toString() { + return String.format("PackedComp[order %s, stride %d, bpp %d, uni %b, comp %d: %s]", + Arrays.toString(compOrder), bitStride, bitsPerPixel, uniform, + compMask.length, toHexString(compBitCount, compMask, compBitShift)); + } + + /** + * @param componentOrder {@link CType Component type} order of all components, see {@link #componentBitMask()}. + * @param componentCount number of components + * @param bpc bits per component + * @param bitStride stride bits to next pixel + */ + public PackedComposition(final CType[] componentOrder, final int componentCount, final int bpc, final int bitStride) { + this.compOrder = componentOrder; + this.compMask = new int[componentCount]; + this.compBitShift = new int[componentCount]; + this.compBitCount = new int[componentCount]; + final int compMask = ( 1 << bpc ) - 1; + for(int i=0; i<componentCount; i++) { + this.compMask[i] = compMask; + this.compBitShift[i] = bpc * i; + this.compBitCount[i] = bpc; + } + this.uniform = true; + this.bitsPerPixel = bpc * componentCount; + this.bitStride = bitStride; + if( this.bitStride < this.bitsPerPixel ) { + throw new IllegalArgumentException(String.format("bit-stride %d < bitsPerPixel %d", this.bitStride, this.bitsPerPixel)); + } + this.hashCode = hashCodeImpl(); + } - /** Number of components per pixel, e.g. 4 for RGBA. */ - public final int componentCount; - /** Number of bits per pixel, e.g. 32 for RGBA. */ - public final int bitsPerPixel; - /** Number of bytes per pixel, e.g. 4 for RGBA. */ - public final int bytesPerPixel() { return (7+bitsPerPixel)/8; } + /** + * @param componentOrder {@link CType Component type} order of all components, see {@link #componentBitMask()}. + * @param componentMask bit-mask of of all components, see {@link #componentBitMask()}. + * @param componentBitShift bit-shift of all components, see {@link #componentBitMask()}. + * @param bitStride stride bits to next pixel + */ + public PackedComposition(final CType[] componentOrder, final int[] componentMask, final int[] componentBitShift, final int bitStride) { + this.compOrder = componentOrder; + this.compMask = componentMask; + this.compBitShift = componentBitShift; + this.compBitCount = new int[componentMask.length]; + int bpp = 0; + boolean uniform = true; + for(int i = componentMask.length-1; i>=0; i--) { + final int cmask = componentMask[i]; + final int bitCount = IntBitfield.getBitCount(cmask); + bpp += bitCount; + this.compBitCount[i] = bitCount; + if( i > 0 && uniform ) { + uniform = componentMask[i-1] == cmask; + } + } + this.uniform = uniform; + this.bitsPerPixel = bpp; + this.bitStride = bitStride; + if( this.bitStride < this.bitsPerPixel ) { + throw new IllegalArgumentException(String.format("bit-stride %d < bitsPerPixel %d", this.bitStride, this.bitsPerPixel)); + } + this.hashCode = hashCodeImpl(); + } + + @Override + public final boolean isUniform() { return uniform; } + /** + * {@inheritDoc} + * <p> + * Instances of {@link PackedComposition} returns {@code true}. + * </p> + */ + @Override + public final boolean isInterleaved() { return true; } + @Override + public final int componenCount() { return compMask.length; } + @Override + public final int bitsPerPixel() { return bitsPerPixel; } + @Override + public final int bitStride() { return bitStride; } + @Override + public final int bytesPerPixel() { return (7+bitStride)/8; } + @Override + public final CType[] componentOrder() { return compOrder; } + @Override + public final int find(final PixelFormat.CType s) { return PixelFormatUtil.find(s, compOrder, false /* mapRGB2Y */); } + @Override + public final int[] componentBitMask() { return compMask; } + @Override + public final int[] componentBitCount() { return compBitCount; } + @Override + public final int[] componentBitShift() { return compBitShift; } + + @Override + public final int decodeSingleI32(final int shifted, final int cIdx) { + return ( shifted >>> compBitShift[cIdx] ) & compMask[cIdx]; + } + @Override + public final int decodeSingleI64(final long shifted, final int cIdx) { + return ( (int)( 0xffffffffL & ( shifted >>> compBitShift[cIdx] ) ) ) & compMask[cIdx]; + } + @Override + public final int encodeSingleI32(final int norm, final int cIdx) { + return ( norm & compMask[cIdx] ) << compBitShift[cIdx] ; + } + @Override + public final long encodeSingleI64(final int norm, final int cIdx) { + return ( 0xffffffffL & ( norm & compMask[cIdx] ) ) << compBitShift[cIdx] ; + } + @Override + public final int encode3CompI32(final int c1NormI32, final int c2NormI32, final int c3NormI32) { + return ( c1NormI32 & compMask[0] ) << compBitShift[0] | + ( c2NormI32 & compMask[1] ) << compBitShift[1] | + ( c3NormI32 & compMask[2] ) << compBitShift[2] ; + } + @Override + public final int encode4CompI32(final int c1NormI32, final int c2NormI32, final int c3NormI32, final int c4NormI32) { + return ( c1NormI32 & compMask[0] ) << compBitShift[0] | + ( c2NormI32 & compMask[1] ) << compBitShift[1] | + ( c3NormI32 & compMask[2] ) << compBitShift[2] | + ( c4NormI32 & compMask[3] ) << compBitShift[3] ; + } + @Override + public final int encodeSingleI8(final byte normI8, final int cIdx) { + return ( normI8 & compMask[cIdx] ) << compBitShift[cIdx] ; + } + @Override + public final int encode3CompI8(final byte c1NormI8, final byte c2NormI8, final byte c3NormI8) { + return ( c1NormI8 & compMask[0] ) << compBitShift[0] | + ( c2NormI8 & compMask[1] ) << compBitShift[1] | + ( c3NormI8 & compMask[2] ) << compBitShift[2] ; + } + @Override + public final int encode4CompI8(final byte c1NormI8, final byte c2NormI8, final byte c3NormI8, final byte c4NormI8) { + return ( c1NormI8 & compMask[0] ) << compBitShift[0] | + ( c2NormI8 & compMask[1] ) << compBitShift[1] | + ( c3NormI8 & compMask[2] ) << compBitShift[2] | + ( c4NormI8 & compMask[3] ) << compBitShift[3] ; + } + + @Override + public final float toFloat(final int i32, final int cIdx, final boolean i32Shifted) { + if( i32Shifted ) { + return ( ( i32 >>> compBitShift[cIdx] ) & compMask[cIdx] ) / (float)( compMask[cIdx] ) ; + } else { + return ( i32 & compMask[cIdx] ) / (float)( compMask[cIdx] ) ; + } + } + @Override + public final int fromFloat(final float f, final int cIdx, final boolean shiftResult) { + final int v = (int)(f * compMask[cIdx] + 0.5f); + return shiftResult ? v << compBitShift[cIdx] : v; + } + + @Override + public final int defaultValue(final int cIdx, final boolean shiftResult) { + final int v = ( CType.A == compOrder[cIdx] || CType.Y == compOrder[cIdx] ) + ? compMask[cIdx] : 0; + return shiftResult ? v << compBitShift[cIdx] : v; + } + + @Override + public final int hashCode() { return hashCode; } + private final int hashCodeImpl() { + // 31 * x == (x << 5) - x + int hash = 31 + bitStride; + hash = ((hash << 5) - hash) + bitsPerPixel; + hash = ((hash << 5) - hash) + compMask.length; + for(int i=compOrder.length-1; i>=0; i--) { + hash = ((hash << 5) - hash) + compOrder[i].ordinal(); + } + for(int i=compMask.length-1; i>=0; i--) { + hash = ((hash << 5) - hash) + compMask[i]; + } + for(int i=compBitShift.length-1; i>=0; i--) { + hash = ((hash << 5) - hash) + compBitShift[i]; + } + return hash; + } + + @Override + public final boolean equals(final Object obj) { + if(this == obj) { return true; } + if( obj instanceof PackedComposition ) { + final PackedComposition other = (PackedComposition) obj; + return bitStride == other.bitStride && + bitsPerPixel == other.bitsPerPixel && + Arrays.equals(compOrder, other.compOrder) && + Arrays.equals(compMask, other.compMask) && + Arrays.equals(compBitShift, other.compBitShift); + } else { + return false; + } + } + } - private PixelFormat(final int componentCount, final int bpp) { - this.componentCount = componentCount; - this.bitsPerPixel = bpp; + private static String toHexString(final int[] bitCount, final int[] mask, final int[] shift) { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + final int l = mask.length; + for(int i=0; i < l; i++) { + if(i > 0) { + sb.append(", "); + } + sb.append(bitCount[i]).append(": "). + append("0x").append(Integer.toHexString(mask[i])).append(" << ").append(shift[i]); + } + return sb.append("]").toString(); } } diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java index 21bfa8a54..a449f8706 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java @@ -27,10 +27,12 @@ */ package javax.media.nativewindow.util; +import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import java.util.Arrays; import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.Bitstream; /** * Pixel Rectangle Utilities. @@ -39,46 +41,157 @@ import com.jogamp.common.nio.Buffers; * </p> */ public class PixelFormatUtil { - public static interface PixelSink { - /** Return the sink's destination pixelformat. */ - PixelFormat getPixelformat(); + private static boolean DEBUG = false; + public static class ComponentMap { /** - * Returns stride in byte-size, i.e. byte count from one line to the next. - * <p> - * Must be >= {@link #getPixelformat()}.{@link PixelFormat#bytesPerPixel() bytesPerPixel()} * {@link #getSize()}.{@link DimensionImmutable#getWidth() getWidth()}. - * </p> + * Contains the source index for each destination index, + * length is {@link Composition#componenCount()} of destination. */ - int getStride(); + final int[] dst2src; + /** + * Contains the destination index for each source index, + * length is {@link Composition#componenCount()} of source. + */ + final int[] src2dst; /** - * Returns <code>true</code> if the sink's memory is laid out in - * OpenGL's coordinate system, <i>origin at bottom left</i>. - * Otherwise returns <code>false</code>, i.e. <i>origin at top left</i>. + * Contains the source index of RGBA components. */ - boolean isGLOriented(); + final int[] srcRGBA; + final boolean hasSrcRGB; + + public ComponentMap(final PixelFormat.Composition src, final PixelFormat.Composition dst) { + final int sCompCount = src.componenCount(); + final int dCompCount = dst.componenCount(); + final PixelFormat.CType[] sCompOrder = src.componentOrder(); + final PixelFormat.CType[] dCompOrder = dst.componentOrder(); + + dst2src = new int[dCompCount]; + for(int dIdx=0; dIdx<dCompCount; dIdx++) { + dst2src[dIdx] = PixelFormatUtil.find(dCompOrder[dIdx], sCompOrder, true); + } + src2dst = new int[sCompCount]; + for(int sIdx=0; sIdx<sCompCount; sIdx++) { + src2dst[sIdx] = PixelFormatUtil.find(sCompOrder[sIdx], dCompOrder, true); + } + srcRGBA = new int[4]; + srcRGBA[0] = PixelFormatUtil.find(PixelFormat.CType.R, sCompOrder, false); + srcRGBA[1] = PixelFormatUtil.find(PixelFormat.CType.G, sCompOrder, false); + srcRGBA[2] = PixelFormatUtil.find(PixelFormat.CType.B, sCompOrder, false); + srcRGBA[3] = PixelFormatUtil.find(PixelFormat.CType.A, sCompOrder, false); + hasSrcRGB = 0 <= srcRGBA[0] && 0 <= srcRGBA[1] && 0 <= srcRGBA[2]; + } } + + public static final int find(final PixelFormat.CType s, + final PixelFormat.CType[] pool, final boolean mapRGB2Y) { + int i=pool.length-1; + while( i >= 0 && pool[i] != s) { i--; } + + if( 0 > i && mapRGB2Y && 1 == pool.length && pool[0] == PixelFormat.CType.Y && + ( PixelFormat.CType.R == s || + PixelFormat.CType.G == s || + PixelFormat.CType.B == s ) ) + { + // Special case, fallback for RGB mapping -> LUMINANCE/Y + return 0; + } else { + return i; + } + } + /** - * Pixel sink for up-to 32bit. + * Returns shifted bytes from the given {@code data} at given {@code offset} + * of maximal 4 {@code bytesPerPixel}. + * @param bytesPerPixel number of bytes per pixel to fetch, a maximum of 4 are allowed + * @param data byte buffer covering complete pixel at position {@code offset} + * @param offset byte offset of pixel {@code data} start + * @return the shifted 32bit integer value of the pixel */ - public static interface PixelSink32 extends PixelSink { - /** - * Will be invoked over all rows top-to down - * and all columns left-to-right. - * <p> - * Shall consider dest pixelformat and only store as much components - * as defined, up to 32bit. - * </p> - * <p> - * Implementation may better write single bytes from low-to-high bits, - * e.g. {@link ByteOrder#LITTLE_ENDIAN} order. - * Otherwise a possible endian conversion must be taken into consideration. - * </p> - * @param x - * @param y - * @param pixel - */ - void store(int x, int y, int pixel); + public static int getShiftedI32(final int bytesPerPixel, final byte[] data, final int offset) { + if( bytesPerPixel <= 4 ) { + int shiftedI32 = 0; + for(int i=0; i<bytesPerPixel; i++) { + shiftedI32 |= ( 0xff & data[offset+i] ) << 8*i; + } + return shiftedI32; + } else { + throw new UnsupportedOperationException(bytesPerPixel+" bytesPerPixel too big, i.e. > 4"); + } + } + /** + * Returns shifted bytes from the given {@code data} at given {@code offset} + * of maximal 8 {@code bytesPerPixel}. + * @param bytesPerPixel number of bytes per pixel to fetch, a maximum of 4 are allowed + * @param data byte buffer covering complete pixel at position {@code offset} + * @param offset byte offset of pixel {@code data} start + * @return the shifted 64bit integer value of the pixel + */ + public static long getShiftedI64(final int bytesPerPixel, final byte[] data, final int offset) { + if( bytesPerPixel <= 8 ) { + long shiftedI64 = 0; + for(int i=0; i<bytesPerPixel; i++) { + shiftedI64 |= ( 0xff & data[offset+i] ) << 8*i; + } + return shiftedI64; + } else { + throw new UnsupportedOperationException(bytesPerPixel+" bytesPerPixel too big, i.e. > 8"); + } + } + /** + * Returns shifted bytes from the given {@code data} at current position + * of maximal 4 {@code bytesPerPixel}. + * @param bytesPerPixel number of bytes per pixel to fetch, a maximum of 4 are allowed + * @param data byte buffer covering complete pixel at position {@code offset} + * @param retainDataPos if true, absolute {@link ByteBuffer#get(int)} is used and the {@code data} position stays unchanged. + * Otherwise relative {@link ByteBuffer#get()} is used and the {@code data} position changes. + * @return the shifted 32bit integer value of the pixel + */ + public static int getShiftedI32(final int bytesPerPixel, final ByteBuffer data, final boolean retainDataPos) { + if( bytesPerPixel <= 4 ) { + int shiftedI32 = 0; + if( retainDataPos ) { + final int offset = data.position(); + for(int i=0; i<bytesPerPixel; i++) { + shiftedI32 |= ( 0xff & data.get(offset+i) ) << 8*i; + } + } else { + for(int i=0; i<bytesPerPixel; i++) { + shiftedI32 |= ( 0xff & data.get() ) << 8*i; + } + } + return shiftedI32; + } else { + throw new UnsupportedOperationException(bytesPerPixel+" bytesPerPixel too big, i.e. > 4"); + } + } + /** + * Returns shifted bytes from the given {@code data} at current position + * of maximal 8 {@code bytesPerPixel}. + * @param bytesPerPixel number of bytes per pixel to fetch, a maximum of 4 are allowed + * @param data byte buffer covering complete pixel at position {@code offset} + * @param retainDataPos if true, absolute {@link ByteBuffer#get(int)} is used and the {@code data} position stays unchanged. + * Otherwise relative {@link ByteBuffer#get()} is used and the {@code data} position changes. + * @return the shifted 64bit integer value of the pixel + */ + public static long getShiftedI64(final int bytesPerPixel, final ByteBuffer data, final boolean retainDataPos) { + if( bytesPerPixel <= 8 ) { + long shiftedI64 = 0; + if( retainDataPos ) { + final int offset = data.position(); + for(int i=0; i<bytesPerPixel; i++) { + shiftedI64 |= ( 0xff & data.get(offset+i) ) << 8*i; + } + } else { + for(int i=0; i<bytesPerPixel; i++) { + shiftedI64 |= ( 0xff & data.get() ) << 8*i; + } + } + return shiftedI64; + } else { + throw new UnsupportedOperationException(bytesPerPixel+" bytesPerPixel too big, i.e. > 8"); + } } /** @@ -87,8 +200,14 @@ public class PixelFormatUtil { */ public static PixelFormat getReversed(final PixelFormat fmt) { switch(fmt) { - case LUMINANCE: - return PixelFormat.LUMINANCE; + case RGB565: + return PixelFormat.BGR565; + case BGR565: + return PixelFormat.RGB565; + case RGBA5551: + return PixelFormat.ABGR1555; + case ABGR1555: + return PixelFormat.RGBA5551; case RGB888: return PixelFormat.BGR888; case BGR888: @@ -102,42 +221,14 @@ public class PixelFormatUtil { case BGRA8888: return PixelFormat.ABGR8888; default: - throw new InternalError("Unhandled format "+fmt); + return fmt; } } - public static int getValue32(final PixelFormat src_fmt, final ByteBuffer src, int srcOff) { - switch(src_fmt) { + public static int convertToInt32(final PixelFormat dst_fmt, final byte r, final byte g, final byte b, final byte a) { + switch(dst_fmt) { case LUMINANCE: { - final byte c1 = src.get(srcOff++); - return ( 0xff ) << 24 | ( 0xff & c1 ) << 16 | ( 0xff & c1 ) << 8 | ( 0xff & c1 ); - } - case RGB888: - case BGR888: { - final byte c1 = src.get(srcOff++); - final byte c2 = src.get(srcOff++); - final byte c3 = src.get(srcOff++); - return ( 0xff ) << 24 | ( 0xff & c3 ) << 16 | ( 0xff & c2 ) << 8 | ( 0xff & c1 ); - } - case RGBA8888: - case ABGR8888: - case ARGB8888: - case BGRA8888: { - final byte c1 = src.get(srcOff++); - final byte c2 = src.get(srcOff++); - final byte c3 = src.get(srcOff++); - final byte c4 = src.get(srcOff++); - return ( 0xff & c4 ) << 24 | ( 0xff & c3 ) << 16 | ( 0xff & c2 ) << 8 | ( 0xff & c1 ); - } - default: - throw new InternalError("Unhandled format "+src_fmt); - } - } - - public static int convertToInt32(final PixelFormat dest_fmt, final byte r, final byte g, final byte b, final byte a) { - switch(dest_fmt) { - case LUMINANCE: { - final byte l = ( byte) ( ( ( ( 0xff & r ) + ( 0xff & g ) + ( 0xff & b ) ) / 3 ) ); + final byte l = ( byte) ( ( ( ( 0xff & r ) + ( 0xff & g ) + ( 0xff & b ) ) / 3 ) * a ); return ( 0xff ) << 24 | ( 0xff & l ) << 16 | ( 0xff & l ) << 8 | ( 0xff & l ); } case RGB888: @@ -153,11 +244,11 @@ public class PixelFormatUtil { case BGRA8888: return ( 0xff & a ) << 24 | ( 0xff & r ) << 16 | ( 0xff & g ) << 8 | ( 0xff & b ); default: - throw new InternalError("Unhandled format "+dest_fmt); + throw new InternalError("Unhandled format "+dst_fmt); } } - public static int convertToInt32(final PixelFormat dest_fmt, final PixelFormat src_fmt, final ByteBuffer src, int srcOff) { + public static int convertToInt32(final PixelFormat dst_fmt, final PixelFormat src_fmt, final ByteBuffer src, int srcOff) { final byte r, g, b, a; switch(src_fmt) { case LUMINANCE: @@ -205,7 +296,7 @@ public class PixelFormatUtil { default: throw new InternalError("Unhandled format "+src_fmt); } - return convertToInt32(dest_fmt, r, g, b, a); + return convertToInt32(dst_fmt, r, g, b, a); } public static int convertToInt32(final PixelFormat dest_fmt, final PixelFormat src_fmt, final int src_pixel) { @@ -259,115 +350,251 @@ public class PixelFormatUtil { return convertToInt32(dest_fmt, r, g, b, a); } - public static PixelRectangle convert32(final PixelRectangle src, - final PixelFormat destFmt, final int ddestStride, final boolean isGLOriented, - final boolean destIsDirect) { + public static PixelRectangle convert(final PixelRectangle src, + final PixelFormat destFmt, final int ddestStride, final boolean isGLOriented, + final boolean destIsDirect) { final int width = src.getSize().getWidth(); final int height = src.getSize().getHeight(); - final int bpp = destFmt.bytesPerPixel(); + final int bpp = destFmt.comp.bytesPerPixel(); final int destStride; if( 0 != ddestStride ) { destStride = ddestStride; - if( destStride < bpp * width ) { - throw new IllegalArgumentException("Invalid stride "+destStride+", must be greater than bytesPerPixel "+bpp+" * width "+width); - } } else { destStride = bpp * width; } final int capacity = destStride*height; - final ByteBuffer bb = destIsDirect ? Buffers.newDirectByteBuffer(capacity) : ByteBuffer.allocate(capacity).order(src.getPixels().order()); - - // System.err.println("XXX: SOURCE "+src); - // System.err.println("XXX: DEST fmt "+destFmt+", stride "+destStride+" ("+ddestStride+"), isGL "+isGLOriented+", "+width+"x"+height+", capacity "+capacity+", "+bb); - - final PixelFormatUtil.PixelSink32 imgSink = new PixelFormatUtil.PixelSink32() { - public void store(final int x, final int y, final int pixel) { - int o = destStride*y+x*bpp; - bb.put(o++, (byte) ( pixel )); // 1 - if( 3 <= bpp ) { - bb.put(o++, (byte) ( pixel >>> 8 )); // 2 - bb.put(o++, (byte) ( pixel >>> 16 )); // 3 - if( 4 <= bpp ) { - bb.put(o++, (byte) ( pixel >>> 24 )); // 4 - } - } - } - @Override - public final PixelFormat getPixelformat() { - return destFmt; - } - @Override - public final int getStride() { - return destStride; - } - @Override - public final boolean isGLOriented() { - return isGLOriented; - } - }; - convert32(imgSink, src); - return new PixelRectangle.GenericPixelRect(destFmt, src.getSize(), destStride, isGLOriented, bb); + final ByteBuffer destBB = destIsDirect ? Buffers.newDirectByteBuffer(capacity) : ByteBuffer.allocate(capacity).order(src.getPixels().order()); + convert(src, destBB, destFmt, isGLOriented, destStride); + return new PixelRectangle.GenericPixelRect(destFmt, src.getSize(), destStride, isGLOriented, destBB); } - public static void convert32(final PixelSink32 destInt32, final PixelRectangle src) { - convert32(destInt32, - src.getPixels(), src.getPixelformat(), - src.isGLOriented(), - src.getSize().getWidth(), src.getSize().getHeight(), - src.getStride()); + /** + * @param src + * @param dst_bb {@link ByteBuffer} sink + * @param dst_fmt destination {@link PixelFormat} + * @param dst_glOriented if true, the source memory is laid out in OpenGL's coordinate system, <i>origin at bottom left</i>, + * otherwise <i>origin at top left</i>. + * @param dst_lineStride line stride in byte-size for destination, i.e. byte count from one line to the next. + * Must be >= {@link PixelFormat.Composition#bytesPerPixel() dst_fmt.comp.bytesPerPixel()} * width + * or {@code zero} for default stride. + * + * @throws IllegalStateException + * @throws IllegalArgumentException if {@code src_lineStride} or {@code dst_lineStride} is invalid + */ + public static void convert(final PixelRectangle src, + final ByteBuffer dst_bb, final PixelFormat dst_fmt, final boolean dst_glOriented, final int dst_lineStride) + throws IllegalStateException + { + convert(src.getSize().getWidth(), src.getSize().getHeight(), + src.getPixels(), src.getPixelformat(), src.isGLOriented(), src.getStride(), + dst_bb, dst_fmt, dst_glOriented, dst_lineStride); } + /** - * - * @param dest32 32bit pixel sink - * @param src_bb - * @param src_fmt + * @param width width of the to be converted pixel rectangle + * @param height height of the to be converted pixel rectangle + * @param src_bb {@link ByteBuffer} source + * @param src_fmt source {@link PixelFormat} * @param src_glOriented if true, the source memory is laid out in OpenGL's coordinate system, <i>origin at bottom left</i>, * otherwise <i>origin at top left</i>. - * @param width - * @param height - * @param strideInBytes stride in byte-size, i.e. byte count from one line to the next. - * If zero, stride is set to <code>width * bytes-per-pixel</code>. - * If not zero, value must be >= <code>width * bytes-per-pixel</code>. - * @param stride_bytes stride in byte-size, i.e. byte count from one line to the next. - * Must be >= {@link PixelFormat#bytesPerPixel() src_fmt.bytesPerPixel()} * width. - * @throws IllegalArgumentException if <code>strideInBytes</code> is invalid + * @param src_lineStride line stride in byte-size for source, i.e. byte count from one line to the next. + * Must be >= {@link PixelFormat.Composition#bytesPerPixel() src_fmt.comp.bytesPerPixel()} * width + * or {@code zero} for default stride. + * @param dst_bb {@link ByteBuffer} sink + * @param dst_fmt destination {@link PixelFormat} + * @param dst_glOriented if true, the source memory is laid out in OpenGL's coordinate system, <i>origin at bottom left</i>, + * otherwise <i>origin at top left</i>. + * @param dst_lineStride line stride in byte-size for destination, i.e. byte count from one line to the next. + * Must be >= {@link PixelFormat.Composition#bytesPerPixel() dst_fmt.comp.bytesPerPixel()} * width + * or {@code zero} for default stride. + * + * @throws IllegalStateException + * @throws IllegalArgumentException if {@code src_lineStride} or {@code dst_lineStride} is invalid */ - public static void convert32(final PixelSink32 dest32, - final ByteBuffer src_bb, final PixelFormat src_fmt, final boolean src_glOriented, final int width, final int height, int stride_bytes) { - final int src_bpp = src_fmt.bytesPerPixel(); - if( 0 != stride_bytes ) { - if( stride_bytes < src_bpp * width ) { - throw new IllegalArgumentException("Invalid stride "+stride_bytes+", must be greater than bytesPerPixel "+src_bpp+" * width "+width); + public static void convert(final int width, final int height, + final ByteBuffer src_bb, final PixelFormat src_fmt, final boolean src_glOriented, int src_lineStride, + final ByteBuffer dst_bb, final PixelFormat dst_fmt, final boolean dst_glOriented, int dst_lineStride + ) throws IllegalStateException, IllegalArgumentException { + final PixelFormat.Composition src_comp = src_fmt.comp; + final PixelFormat.Composition dst_comp = dst_fmt.comp; + final int src_bpp = src_comp.bytesPerPixel(); + final int dst_bpp = dst_comp.bytesPerPixel(); + + if( 0 != src_lineStride ) { + if( src_lineStride < src_bpp * width ) { + throw new IllegalArgumentException(String.format("Invalid %s stride %d, must be greater than bytesPerPixel %d * width %d", + "source", src_lineStride, src_bpp, width)); } } else { - stride_bytes = src_bpp * width; + src_lineStride = src_bpp * width; + } + if( 0 != dst_lineStride ) { + if( dst_lineStride < dst_bpp * width ) { + throw new IllegalArgumentException(String.format("Invalid %s stride %d, must be greater than bytesPerPixel %d * width %d", + "destination", dst_lineStride, dst_bpp, width)); + } + } else { + dst_lineStride = dst_bpp * width; + } + + // final int src_comp_bitStride = src_comp.bitStride(); + final int dst_comp_bitStride = dst_comp.bitStride(); + final boolean vert_flip = src_glOriented != dst_glOriented; + final boolean fast_copy = src_comp.equals(dst_comp) && 0 == dst_comp_bitStride%8; + if( DEBUG ) { + System.err.println("XXX: size "+width+"x"+height+", fast_copy "+fast_copy); + System.err.println("XXX: SRC fmt "+src_fmt+", "+src_comp+", stride "+src_lineStride+", isGLOrient "+src_glOriented); + System.err.println("XXX: DST fmt "+dst_fmt+", "+dst_comp+", stride "+dst_lineStride+", isGLOrient "+dst_glOriented); } - final PixelFormat dest_fmt = dest32.getPixelformat(); - final boolean vert_flip = src_glOriented != dest32.isGLOriented(); - final boolean fast_copy = src_fmt == dest_fmt && dest_fmt.bytesPerPixel() == 4 ; - // System.err.println("XXX: SRC fmt "+src_fmt+", stride "+stride_bytes+", isGL "+src_glOriented+", "+width+"x"+height); - // System.err.println("XXX: DST fmt "+dest_fmt+", fast_copy "+fast_copy); if( fast_copy ) { // Fast copy for(int y=0; y<height; y++) { - int o = vert_flip ? ( height - 1 - y ) * stride_bytes : y * stride_bytes; + int src_off = vert_flip ? ( height - 1 - y ) * src_lineStride : y * src_lineStride; + int dst_off = dst_lineStride*y; for(int x=0; x<width; x++) { - dest32.store(x, y, getValue32(src_fmt, src_bb, o)); - o += src_bpp; + dst_bb.put(dst_off+0, src_bb.get(src_off+0)); // 1 + if( 2 <= dst_bpp ) { + dst_bb.put(dst_off+1, src_bb.get(src_off+1)); // 2 + if( 3 <= dst_bpp ) { + dst_bb.put(dst_off+2, src_bb.get(src_off+2)); // 3 + if( 4 <= dst_bpp ) { + dst_bb.put(dst_off+3, src_bb.get(src_off+3)); // 4 + } + } + } + src_off += src_bpp; + dst_off += dst_bpp; } } } else { // Conversion - for(int y=0; y<height; y++) { - int o = vert_flip ? ( height - 1 - y ) * stride_bytes : y * stride_bytes; - for(int x=0; x<width; x++) { - dest32.store( x, y, convertToInt32( dest_fmt, src_fmt, src_bb, o)); - o += src_bpp; + final ComponentMap cmap = new ComponentMap(src_fmt.comp, dst_fmt.comp); + + final Bitstream.ByteBufferStream srcBBS = new Bitstream.ByteBufferStream(src_bb); + final Bitstream<ByteBuffer> srcBitStream = new Bitstream<ByteBuffer>(srcBBS, false /* outputMode */); + srcBitStream.setThrowIOExceptionOnEOF(true); + + final Bitstream.ByteBufferStream dstBBS = new Bitstream.ByteBufferStream(dst_bb); + final Bitstream<ByteBuffer> dstBitStream = new Bitstream<ByteBuffer>(dstBBS, true /* outputMode */); + dstBitStream.setThrowIOExceptionOnEOF(true); + + if( DEBUG ) { + System.err.println("XXX: cmap.dst2src "+Arrays.toString(cmap.dst2src)); + System.err.println("XXX: cmap.src2dst "+Arrays.toString(cmap.src2dst)); + System.err.println("XXX: cmap.srcRGBA "+Arrays.toString(cmap.srcRGBA)); + System.err.println("XXX: srcBitStream "+srcBitStream); + System.err.println("XXX: dstBitStream "+dstBitStream); + } + try { + for(int y=0; y<height; y++) { + final int src_off = vert_flip ? ( height - 1 - y ) * src_lineStride * 8 : y * src_lineStride * 8; + // final int dst_off = dst_lineStride*8*y; + srcBitStream.position(src_off); + for(int x=0; x<width; x++) { + convert(cmap, dst_comp, dstBitStream, src_comp, srcBitStream); + } + // srcBitStream.skip(( src_lineStride * 8 ) - ( src_comp_bitStride * width )); + dstBitStream.skip(( dst_lineStride * 8 ) - ( dst_comp_bitStride * width )); + } + } catch(final IOException ioe) { + throw new RuntimeException(ioe); + } + if( DEBUG ) { + System.err.println("XXX: srcBitStream "+srcBitStream); + System.err.println("XXX: dstBitStream "+dstBitStream); + } + } + } + + public static void convert(final ComponentMap cmap, + final PixelFormat.Composition dstComp, + final Bitstream<ByteBuffer> dstBitStream, + final PixelFormat.Composition srcComp, + final Bitstream<ByteBuffer> srcBitStream) throws IllegalStateException, IOException { + final int sCompCount = srcComp.componenCount(); + final int dCompCount = dstComp.componenCount(); + final int[] sc = new int[sCompCount]; + final int[] dcDef = new int[dCompCount]; + final int[] srcCompBitCount = srcComp.componentBitCount(); + final int[] srcCompBitMask = srcComp.componentBitMask(); + final int[] dstCompBitCount = dstComp.componentBitCount(); + + // Fill w/ source values + for(int sIdx=0; sIdx<sCompCount; sIdx++) { + sc[sIdx] = srcBitStream.readBits31(srcCompBitCount[sIdx]) & srcCompBitMask[sIdx]; + } + srcBitStream.skip(srcComp.bitStride() - srcComp.bitsPerPixel()); + + // Cache missing defaults + for(int i=0; i<dCompCount; i++) { + dcDef[i] = dstComp.defaultValue(i, false); + } + + if( 1 == dCompCount && + PixelFormat.CType.Y == dstComp.componentOrder()[0] && + cmap.hasSrcRGB + ) + { + // RGB[A] -> Y conversion + final int r = sc[cmap.srcRGBA[0]]; + final int g = sc[cmap.srcRGBA[1]]; + final int b = sc[cmap.srcRGBA[2]]; + final float rF = srcComp.toFloat(r, cmap.srcRGBA[0], false); + final float gF = srcComp.toFloat(g, cmap.srcRGBA[1], false); + final float bF = srcComp.toFloat(b, cmap.srcRGBA[2], false); + final int a; + final float aF; + /** if( 0 <= cmap.srcRGBA[3] ) { // disable premultiplied-alpha + a = sc[cmap.srcRGBA[3]]; + aF = srcComp.toFloat(a, false, cmap.srcRGBA[3]); + } else */ { + a = 1; + aF = 1f; + } + final float lF = ( rF + gF + bF ) * aF / 3f; + final int v = dstComp.fromFloat(lF, 0, false); + + dstBitStream.writeBits31(dstCompBitCount[0], v); + dstBitStream.skip(dstComp.bitStride() - dstComp.bitsPerPixel()); + if( DEBUG ) { + if( srcBitStream.position() <= 8*4 ) { + System.err.printf("convert: rgb[a] -> Y: rgb 0x%02X 0x%02X 0x%02X 0x%02X -> %f %f %f %f"+ + " -> %f -> dstC 0 0x%08X (%d bits: %s)%n", + r, g, b, a, + rF, gF, bF, aF, + lF, v, dstCompBitCount[0], Bitstream.toBinString(true, v, dstCompBitCount[0]) + ); + } + } + return; + } + + for(int dIdx=0; dIdx<dCompCount; dIdx++) { + int sIdx; + if( 0 <= ( sIdx = cmap.dst2src[dIdx] ) ) { + final float f = srcComp.toFloat(sc[sIdx], sIdx, false); + final int v = dstComp.fromFloat(f, dIdx, false); + dstBitStream.writeBits31(dstCompBitCount[dIdx], v); + if( DEBUG ) { + if( srcBitStream.position() <= 8*4 ) { + System.err.printf("convert: srcC %d: 0x%08X -> %f -> dstC %d 0x%08X (%d bits: %s)%n", + sIdx, sc[sIdx], f, dIdx, v, dstCompBitCount[dIdx], Bitstream.toBinString(true, v, dstCompBitCount[dIdx])); + } + } + } else { + dstBitStream.writeBits31(dstCompBitCount[dIdx], dcDef[dIdx]); + if( DEBUG ) { + if( srcBitStream.position() <= 8*4 ) { + System.err.printf("convert: srcC %d: undef -> dstC %d 0x%08X (%d bits: %s)%n", + sIdx, dIdx, dcDef[dIdx], dstCompBitCount[dIdx], Bitstream.toBinString(true, dcDef[dIdx], dstCompBitCount[dIdx])); + } } } } + dstBitStream.skip(dstComp.bitStride() - dstComp.bitsPerPixel()); + return; } } diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java index 96c1f7b33..f4fc99aee 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java @@ -113,11 +113,11 @@ public interface PixelRectangle { throws IllegalArgumentException, IndexOutOfBoundsException { if( 0 != strideInBytes ) { - if( strideInBytes < pixelformat.bytesPerPixel() * size.getWidth()) { - throw new IllegalArgumentException("Invalid stride "+strideInBytes+", must be greater than bytesPerPixel "+pixelformat.bytesPerPixel()+" * width "+size.getWidth()); + if( strideInBytes < pixelformat.comp.bytesPerPixel() * size.getWidth()) { + throw new IllegalArgumentException("Invalid stride "+strideInBytes+", must be greater than bytesPerPixel "+pixelformat.comp.bytesPerPixel()+" * width "+size.getWidth()); } } else { - strideInBytes = pixelformat.bytesPerPixel() * size.getWidth(); + strideInBytes = pixelformat.comp.bytesPerPixel() * size.getWidth(); } final int reqBytes = strideInBytes * size.getHeight(); if( pixels.limit() < reqBytes ) { @@ -148,7 +148,7 @@ public interface PixelRectangle { synchronized (this) { if( !hashCodeComputed ) { // 31 * x == (x << 5) - x - int hash = 31 + pixelformat.hashCode(); + int hash = pixelformat.comp.hashCode(); hash = ((hash << 5) - hash) + size.hashCode(); hash = ((hash << 5) - hash) + strideInBytes; hash = ((hash << 5) - hash) + ( isGLOriented ? 1 : 0); diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/SurfaceSize.java b/src/nativewindow/classes/javax/media/nativewindow/util/SurfaceSize.java index 601e6dd71..fe1fa3b9d 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/util/SurfaceSize.java +++ b/src/nativewindow/classes/javax/media/nativewindow/util/SurfaceSize.java @@ -105,7 +105,7 @@ public class SurfaceSize implements Comparable<SurfaceSize> { @Override public final int hashCode() { // 31 * x == (x << 5) - x - int hash = 31 + getResolution().hashCode(); + int hash = getResolution().hashCode(); hash = ((hash << 5) - hash) + getBitsPerPixel(); return hash; } diff --git a/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java b/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java index deb685b51..10d957186 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java +++ b/src/nativewindow/classes/jogamp/nativewindow/ProxySurfaceImpl.java @@ -28,6 +28,8 @@ package jogamp.nativewindow; +import java.io.PrintStream; + import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; @@ -279,6 +281,13 @@ public abstract class ProxySurfaceImpl implements ProxySurface { sink.append("WINDOW_INVISIBLE"); needsOr = true; } + if( 0 != ( implBitfield & OPT_UPSTREAM_SURFACELESS ) ) { + if(needsOr) { + sink.append(" | "); + } + sink.append("SURFACELESS"); + needsOr = true; + } sink.append(" ]"); return sink; } @@ -297,6 +306,34 @@ public abstract class ProxySurfaceImpl implements ProxySurface { @Override public final void clearUpstreamOptionBits(final int v) { implBitfield &= ~v; } + public static void dumpHierarchy(final PrintStream out, final ProxySurface s) { + out.println("Surface Hierarchy of "+s.getClass().getName()); + dumpHierarchy(out, s, ""); + out.println(); + } + private static void dumpHierarchy(final PrintStream out, final NativeSurface s, String indentation) { + indentation = indentation + " "; + out.println(indentation+"Surface device "+s.getGraphicsConfiguration().getScreen().getDevice()); + out.println(indentation+"Surface size "+s.getSurfaceWidth()+"x"+s.getSurfaceHeight()+", handle 0x"+Long.toHexString(s.getSurfaceHandle())); + if( s instanceof ProxySurfaceImpl ) { + final ProxySurface ps = (ProxySurface)s; + out.println(indentation+"Upstream options "+ps.getUpstreamOptionBits(null).toString()); + + final UpstreamSurfaceHook psUSH = ps.getUpstreamSurfaceHook(); + if( null != psUSH ) { + out.println(indentation+"Upstream Hook "+psUSH.getClass().getName()); + final NativeSurface upstreamSurface = psUSH.getUpstreamSurface(); + indentation = indentation + " "; + if( null != upstreamSurface ) { + out.println(indentation+"Upstream Hook's Surface "+upstreamSurface.getClass().getName()); + dumpHierarchy(out, upstreamSurface, indentation); + } else { + out.println(indentation+"Upstream Hook's Surface NULL"); + } + } + } + } + @Override public StringBuilder toString(StringBuilder sink) { if(null == sink) { diff --git a/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java b/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java index 881fd56a6..b187921e4 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java +++ b/src/nativewindow/classes/jogamp/nativewindow/SharedResourceToolkitLock.java @@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.media.nativewindow.ToolkitLock; +import com.jogamp.common.ExceptionUtils; import com.jogamp.common.util.LongObjectHashMap; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; @@ -63,7 +64,7 @@ public class SharedResourceToolkitLock implements ToolkitLock { if(DEBUG || verbose || handle2Lock.size() > 0 ) { System.err.println("SharedResourceToolkitLock: Shutdown (open: "+handle2Lock.size()+")"); if(DEBUG) { - Thread.dumpStack(); + ExceptionUtils.dumpStack(System.err); } if( handle2Lock.size() > 0) { dumpOpenDisplayConnections(); diff --git a/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java b/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java index b0eda63b6..1dce9218c 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java +++ b/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java @@ -37,6 +37,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.Frame; import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; import java.util.HashMap; import javax.swing.JComponent; @@ -50,6 +51,8 @@ import javax.media.nativewindow.util.PixelFormat; import javax.media.nativewindow.util.PixelFormatUtil; import javax.swing.MenuSelectionManager; +import com.jogamp.nativewindow.awt.DirectDataBufferInt; + import jogamp.nativewindow.jawt.JAWTUtil; public class AWTMisc { @@ -210,25 +213,13 @@ public class AWTMisc { private static synchronized Cursor createCursor(final PixelRectangle pixelrect, final Point hotSpot) { final int width = pixelrect.getSize().getWidth(); final int height = pixelrect.getSize().getHeight(); - final BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); // PixelFormat.BGRA8888 - final PixelFormatUtil.PixelSink32 imgSink = new PixelFormatUtil.PixelSink32() { - public void store(final int x, final int y, final int pixel) { - img.setRGB(x, y, pixel); - } - @Override - public final PixelFormat getPixelformat() { - return PixelFormat.BGRA8888; - } - @Override - public int getStride() { - return width*4; - } - @Override - public final boolean isGLOriented() { - return false; - } - }; - PixelFormatUtil.convert32(imgSink, pixelrect); + // final BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); // PixelFormat.BGRA8888 + final DirectDataBufferInt.BufferedImageInt img = + DirectDataBufferInt.createBufferedImage(width, height, BufferedImage.TYPE_INT_ARGB, + null /* location */, null /* properties */); + final ByteBuffer imgBuffer = img.getDataBuffer().getDataBytes(); + PixelFormatUtil.convert(pixelrect, imgBuffer, PixelFormat.BGRA8888, false /* dst_glOriented */, width*4 /* dst_lineStride */); + final Toolkit toolkit = Toolkit.getDefaultToolkit(); return toolkit.createCustomCursor(img, hotSpot, pixelrect.toString()); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index cf163bd82..9af74d9f5 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -31,6 +31,8 @@ import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.Rectangle; +import javax.media.nativewindow.util.RectangleImmutable; import com.jogamp.common.util.Function; import com.jogamp.common.util.FunctionTask; @@ -107,6 +109,58 @@ public class OSXUtil implements ToolkitProperties { return (Insets) GetInsets0(windowOrView); } + /** + * Returns the pixel-scale of the NSScreen, with the highest + * {@link RectangleImmutable#coverage(RectangleImmutable) coverage} of the given rectangle in window units. + * <p> + * If no coverage is detected the pixel-scale of the first NSScreen is returned. + * </p> + * @param r arbitrary rectangle in window units + * @param screenIndexOut storage returning the native screen index containing the given rectangle + */ + public static double GetPixelScale(final RectangleImmutable r, final int[] screenIndexOut) { + if( DEBUG ) { + System.err.printf("GetPixelScale covering %s%n", r.toString()); + } + final int screenCount; + final RectangleImmutable[] screenBounds; + final double[] pixelScales; + { + final double[] sd = GetScreenData0(); + if( 0 != sd.length % 5 ) { + throw new InternalError("GetScreenData0 didn't return multiple of 5 but "+sd.length); + } + screenCount = sd.length / 5; + screenBounds = new RectangleImmutable[screenCount]; + pixelScales = new double[screenCount] ; + for(int i=0; i<screenCount; i++) { + final int j = i*5; + pixelScales[i] = sd[j+0]; + screenBounds[i] = new Rectangle((int)sd[j+1], (int)sd[j+2], (int)sd[j+3], (int)sd[j+4]); + if( DEBUG ) { + System.err.printf("GetPixelScale.Screen[%d]: scale %f, bounds[%f / %f %f x %f]%n", + i, pixelScales[i], sd[j+1], sd[j+2], sd[j+3], sd[j+4]); + } + } + } + double pixelScale = pixelScales[0]; + screenIndexOut[0] = 0; + float maxCoverage = Float.MIN_VALUE; + for(int i=screenCount-1; i>=0; i--) { + final RectangleImmutable sb = screenBounds[i]; + final float coverage = sb.coverage(r); + if( coverage > maxCoverage ) { + maxCoverage = coverage; + screenIndexOut[0] = i; + pixelScale = pixelScales[i]; + } + } + if( DEBUG ) { + System.err.printf("GetPixelScale Result: screen %d, scale %f%n%n", screenIndexOut[0], pixelScale); + } + return pixelScale; + } + public static double GetPixelScale(final int screenIndex) { return GetPixelScale0(screenIndex); } @@ -393,6 +447,7 @@ public class OSXUtil implements ToolkitProperties { private static native boolean isNSWindow0(long object); private static native Object GetLocationOnScreen0(long windowOrView, int src_x, int src_y); private static native Object GetInsets0(long windowOrView); + private static native double[] GetScreenData0(); private static native double GetPixelScale0(int screenIndex); private static native double GetPixelScale1(long windowOrView); private static native long CreateNSWindow0(int x, int y, int width, int height); diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java index c409b6a39..b3cd977d4 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java @@ -31,6 +31,8 @@ import javax.media.nativewindow.util.Point; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; +import com.jogamp.common.ExceptionUtils; + import jogamp.nativewindow.NWJNILibLoader; import jogamp.nativewindow.Debug; import jogamp.nativewindow.ToolkitProperties; @@ -132,7 +134,7 @@ public class GDIUtil implements ToolkitProperties { SetProcessThreadsAffinityMask0(affinityMask, verbose); } - private static final void dumpStack() { Thread.dumpStack(); } // Callback for JNI + private static final void dumpStack() { ExceptionUtils.dumpStack(System.err); } // Callback for JNI /** Creates WNDCLASSEX instance */ static native boolean CreateWindowClass0(long hInstance, String clazzName, long wndProc, long iconSmallHandle, long iconBigHandle); diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java index 7e61ba6d0..1b3b207f6 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11DummyUpstreamSurfaceHook.java @@ -44,7 +44,7 @@ public class X11DummyUpstreamSurfaceHook extends UpstreamSurfaceHookMutableSize s.setSurfaceHandle(windowHandle); s.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); } - s.addUpstreamOptionBits(ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE); + s.addUpstreamOptionBits( ProxySurface.OPT_UPSTREAM_WINDOW_INVISIBLE ); } finally { device.unlock(); } diff --git a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java index 2414248b4..786be7cd7 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java +++ b/src/nativewindow/classes/jogamp/nativewindow/x11/X11Util.java @@ -45,6 +45,7 @@ import jogamp.nativewindow.Debug; import jogamp.nativewindow.NWJNILibLoader; import jogamp.nativewindow.ToolkitProperties; +import com.jogamp.common.ExceptionUtils; import com.jogamp.common.util.LongObjectHashMap; import com.jogamp.common.util.PropertyAccess; import com.jogamp.nativewindow.x11.X11GraphicsDevice; @@ -196,7 +197,7 @@ public class X11Util implements ToolkitProperties { ", pending (open in creation order): "+pendingDisplayList.size()+ ")"); if(DEBUG) { - Thread.dumpStack(); + ExceptionUtils.dumpStack(System.err); } if( openDisplayList.size() > 0) { X11Util.dumpOpenDisplayConnections(); @@ -615,7 +616,7 @@ public class X11Util implements ToolkitProperties { } private static final String getCurrentThreadName() { return Thread.currentThread().getName(); } // Callback for JNI - private static final void dumpStack() { Thread.dumpStack(); } // Callback for JNI + private static final void dumpStack() { ExceptionUtils.dumpStack(System.err); } // Callback for JNI private static native boolean initialize0(boolean debug); private static native void shutdown0(); diff --git a/src/nativewindow/native/macosx/OSXmisc.m b/src/nativewindow/native/macosx/OSXmisc.m index 127b329d1..997bafba0 100644 --- a/src/nativewindow/native/macosx/OSXmisc.m +++ b/src/nativewindow/native/macosx/OSXmisc.m @@ -249,6 +249,62 @@ JNIEXPORT jobject JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetInsets0 return res; } +static CGDirectDisplayID GetCGDirectDisplayIDByNSScreen(NSScreen *screen) { + // Mind: typedef uint32_t CGDirectDisplayID; - however, we assume it's 64bit on 64bit ?! + NSDictionary * dict = [screen deviceDescription]; + NSNumber * val = (NSNumber *) [dict objectForKey: @"NSScreenNumber"]; + // [NSNumber integerValue] returns NSInteger which is 32 or 64 bit native size + return (CGDirectDisplayID) [val integerValue]; +} + +/* + * Class: Java_jogamp_nativewindow_macosx_OSXUtil + * Method: GetScreenData0 + * Signature: ()[F + */ +JNIEXPORT jdoubleArray JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenData0 + (JNIEnv *env, jclass unused) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + CGFloat pixelScale; + CGDirectDisplayID display; + NSRect dBounds; + NSScreen *screen; + NSArray *screens = [NSScreen screens]; + int sCount = [screens count]; + jdouble res[sCount*5]; + int i,j; + + for(i=0; i<sCount; i++) { + j = i*5; + screen = (NSScreen *) [screens objectAtIndex: i]; + pixelScale = 1.0; // default +NS_DURING + // Available >= 10.7 + pixelScale = [screen backingScaleFactor]; // HiDPI scaling +NS_HANDLER +NS_ENDHANDLER + display = GetCGDirectDisplayIDByNSScreen(screen); + dBounds = CGDisplayBounds (display); // origin top-left + res[j+0] = (jdouble)pixelScale; + res[j+1] = (jdouble)dBounds.origin.x; + res[j+2] = (jdouble)dBounds.origin.y; + res[j+3] = (jdouble)dBounds.size.width; + res[j+4] = (jdouble)dBounds.size.height; + } + + jdoubleArray jniRes = (*env)->NewDoubleArray(env, sCount*5); // x,y,w,h,scale + if (jniRes == NULL) { + NativewindowCommon_throwNewRuntimeException(env, "Could not allocate double array of size %d", sCount*5); + } + (*env)->SetDoubleArrayRegion(env, jniRes, 0, sCount*5, res); + + [pool release]; + + return jniRes; +} + /* * Class: Java_jogamp_nativewindow_macosx_OSXUtil * Method: GetPixelScale0 |