diff options
author | Sven Gothel <[email protected]> | 2014-12-06 21:12:18 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-12-06 21:12:18 +0100 |
commit | a53e87a84c92444e8a3173f25ce86dcfd536d6a8 (patch) | |
tree | bd19e9f40e4c67203d579c28b3775a5c17adc106 /src/nativewindow | |
parent | 241d505749b7d2bd383673a378fdca268989d2fd (diff) |
Bug 1107 - Refine PixelFormat, GLPixelBuffer and DirectDataBufferInt/BufferedImageInt
- PixelFormat
Refine definition allowing complete format conversion by its attributes
instead of static 'knowledge'.
- PixelFormat has_a *new* PixelFormat.Composition
- PixelFormat.Composition contains all pixel component layout
information as required for inspection and conversion.
Component names are enumerated via PixelFormat.CType.
- PixelFormatUtil.convert(..) utilizes generic conversion
based on PixelFormat.Composition rather static type mapping.
However, a int32 RGBA static conversion is still supported for performance.
Utilizes Bitstream for varying pixel component bit-width.
- Complete w/ hashCode() and equals(..)
- GLPixelBuffer
- Take 'pack' mode into account when determine GLPixelAttributes,
i.e. on GLES pack=true (e.g. glReadPixel) only RGBA is guaranteed to work.
Hence querying GLPixelAttributes requires the GLProfile, PixelFormat and pack mode.
- Complete GLPixelAttributes conversions from PixelFormat or GL format/data-type,
while taking GL data-type into account, as well as pack-mode.
- Complete w/ hashCode() and equals(..)
- SingletonGLPixelBufferProvider queries singleton GLPixelBuffer via
- PixelFormat.Composition hostPixelComp,
- GLPixelAttributes pixelAttributes,
- boolean pack
which comprise a unique key, allowing the implementation to utilize
a hash map. This is implemented in AWTSingletonGLPixelBufferProvider.
This allows distinct singleton GLPixelBuffer for different
host PixelFormat (conversion) and GLPixelAttributes (depending on GLProfile).
- Removes field 'componentCount' which was 'hacked in' to pass
information about an optional host memory layout.
Implementations utilizing conversion, e.g. AWTGLPixelBuffer,
can implement GLPixelBufferProvider's
'PixelFormat.Composition getHostPixelComp(final GLProfile glp, final int componentCount)'
and manage such implementation details, see use-case GLJPanel.
- DirectDataBufferInt/BufferedImageInt: Expose underlying NIO ByteBuffer
- AWTMisc.createCursor(..) uses DirectDataBufferInt.BufferedImageInt exposed
NIO ByteBuffer, allowing to use generic PixelFormatUtil.convert(..).
Diffstat (limited to 'src/nativewindow')
5 files changed, 1040 insertions, 237 deletions
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/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/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()); } |