diff options
Diffstat (limited to 'src/nativewindow/classes/javax')
3 files changed, 962 insertions, 193 deletions
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); |