diff options
author | Sven Gothel <[email protected]> | 2014-01-08 21:56:26 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-01-08 21:56:26 +0100 |
commit | bd98b927b910d9421e63cf0dbc2b746eec019f80 (patch) | |
tree | 0cf29f02720abb656f97bc369b057350a6170226 | |
parent | fe28bc125429b38cdcd016746081f4a6d521c6fd (diff) |
Bug 935: NEWT PointerIcon: Utilize Toolkit Agnostic PixelFormat and Conversion Utilities (Allowing 'arbitrary' PointerIcon data input)
Commit fe28bc125429b38cdcd016746081f4a6d521c6fd added the notion of toolkit agnostic PixelFormat and conversion utilities,
utilized and further tested by this patch.
+++
- PointerIcon is a PixelRectangle
and hence holds the decoded data.
This allows on-the-fly conversion if required
as well as recreation w/o PNG re-decoding.
- Using array-backed PointerIcon data where possible,
allowing better performance when converting PixelFormat etc.
- NEWT Display adds 'createPointerIcon(final IOUtil.ClassResources pngResource...'
method to support agnostic PointerIcon creation.
- NEWT Display adds methods to allow users to avoid PixelFormat and
Buffer NIO type forced conversion:
- PixelFormat getNativePointerIconPixelFormat()
- boolean getNativePointerIconForceDirectNIO()
+++
PNGImage -> PNGPixelRect
Deleted: com.jogamp.opengl.util.texture.spi.PNGImage
Added: com.jogamp.opengl.util.PNGPixelRect
(We hope nobody was using PNGImage directly since it was a service-plugin for TextureIO)
PNGPixelRect is a PixelRectangle
PNGPixelRect actually is implemented OpenGL agnostic,
however - since our PNGJ support lives under package 'jogamp.opengl.util.pngj'
it cannot be moved up (yet).
PNGPixelRect now handles all PixelFormat for the target format
and also added support for grayscale+alpha (2 channels).
The latter is force-converted to RGB* - similar to paletted.
Further more, PNGPixelRect allows simply passing an OutputStream to write the PNG data.
Used by: TextureIO and NEWT
+++
- OffscreenSurfaceLayer's setCursor(..) uses the agnostic PixelRectangle
instead of a PNG resource.
- AWTMisc uses the PixelRectangle to produce the AWT Cursor
and converts it to the required format.
Hence same pixels are used for NEWT and AWT pointer/cursor icon.
- TestGearsES2Newt and NewtAWTReparentingKeyAdapter 'tests'
iterate over 3 custom PointerIcon when pressed 'c'.
- JOGLNewtAppletBase uses the new custom PointerIcon
'newt/data/crosshair-lumina-trans-32x32.png', which is included in NEWT (213 bytes only).
-
33 files changed, 1433 insertions, 790 deletions
diff --git a/make/resources/assets-test/crosshair-lumina-trans-64x64.png b/make/resources/assets-test/crosshair-lumina-trans-64x64.png Binary files differnew file mode 100644 index 000000000..9be285335 --- /dev/null +++ b/make/resources/assets-test/crosshair-lumina-trans-64x64.png diff --git a/make/resources/assets/newt/data/crosshair-lumina-trans-32x32.png b/make/resources/assets/newt/data/crosshair-lumina-trans-32x32.png Binary files differnew file mode 100644 index 000000000..92279640b --- /dev/null +++ b/make/resources/assets/newt/data/crosshair-lumina-trans-32x32.png diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index db852ce42..55973eb12 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -229,7 +229,7 @@ function jrun() { #D_ARGS="-Djogl.debug.GLContext -Dnewt.debug=all" #D_ARGS="-Djogl.debug.GLCanvas -Djogl.debug.GLJPanel -Djogl.debug.TileRenderer -Djogl.debug.TileRenderer.PNG" #D_ARGS="-Djogl.debug.GLCanvas -Djogl.debug.GLJPanel -Djogl.debug.TileRenderer" - #D_ARGS="-Djogl.debug.PNGImage" + #D_ARGS="-Djogl.debug.PNG -Dnewt.debug.Display.PointerIcon" #D_ARGS="-Djogl.debug.JPEGImage" #D_ARGS="-Djogl.debug.GLDrawable -Dnativewindow.debug.GraphicsConfiguration -Djogl.debug.CapabilitiesChooser" #X_ARGS="-Dsun.java2d.noddraw=True -Dsun.java2d.opengl=True -Dsun.java2d.xrender=false" @@ -634,9 +634,10 @@ testawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasA #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestJPEGJoglAWTCompareNewtAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestJPEGJoglAWTBenchmarkNewtAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestJPEGTextureFromFileNEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGImage00NEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGImage01NEWT $* -#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.ToolPNG2CSource $* +#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGPixelRect00NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGPixelRect01NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPixelFormatUtil00NEWT $* +#testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPixelFormatUtil01NEWT $* #testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileAWT $* #testnoawt com.jogamp.opengl.test.junit.jogl.util.texture.TestPNGTextureFromFileNEWT $* #testawt com.jogamp.opengl.test.junit.jogl.util.texture.TestGLReadBufferUtilTextureIOWrite01AWT $* diff --git a/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java new file mode 100644 index 000000000..1bbc12f32 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java @@ -0,0 +1,335 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormatUtil; + +import jogamp.opengl.Debug; +import jogamp.opengl.util.pngj.ImageInfo; +import jogamp.opengl.util.pngj.ImageLine; +import jogamp.opengl.util.pngj.ImageLineHelper; +import jogamp.opengl.util.pngj.PngReader; +import jogamp.opengl.util.pngj.PngWriter; +import jogamp.opengl.util.pngj.chunks.PngChunkPLTE; +import jogamp.opengl.util.pngj.chunks.PngChunkTRNS; +import jogamp.opengl.util.pngj.chunks.PngChunkTextVar; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; + +public class PNGPixelRect extends PixelRectangle.GenericPixelRect { + private static final boolean DEBUG = Debug.debug("PNG"); + + /** + * Reads a PNG image from the specified InputStream. + * <p> + * Implicitly converts the image to match the desired: + * <ul> + * <li>{@link PixelFormat}, see {@link #getPixelformat()}</li> + * <li><code>destStrideInBytes</code>, see {@link #getStride()}</li> + * <li><code>destIsGLOriented</code>, see {@link #isGLOriented()}</li> + * </ul> + * </p> + * + * @param in input stream + * @param destFmt desired destination {@link PixelFormat} incl. conversion, maybe <code>null</code> to use source {@link PixelFormat} + * @param destDirectBuffer if true, using a direct NIO buffer, otherwise an array backed buffer + * @param destMinStrideInBytes used if greater than PNG's stride, otherwise using PNG's stride. Stride is width * bytes-per-pixel. + * @param destIsGLOriented + * @return the newly created PNGPixelRect instance + * @throws IOException + */ + public static PNGPixelRect read(final InputStream in, + final PixelFormat ddestFmt, final boolean destDirectBuffer, final int destMinStrideInBytes, + final boolean destIsGLOriented) throws IOException { + final PngReader pngr = new PngReader(new BufferedInputStream(in), null); + final ImageInfo imgInfo = pngr.imgInfo; + final PngChunkPLTE plte = pngr.getMetadata().getPLTE(); + final PngChunkTRNS trns = pngr.getMetadata().getTRNS(); + final boolean indexed = imgInfo.indexed; + final boolean hasAlpha = indexed ? ( trns != null ) : imgInfo.alpha ; + + if(DEBUG) { + System.err.println("PNGPixelRect: "+imgInfo); + } + final int channels = indexed ? ( hasAlpha ? 4 : 3 ) : imgInfo.channels ; + final boolean isGrayAlpha = 2 == channels && imgInfo.greyscale && imgInfo.alpha; + if ( ! ( 1 == channels || 3 == channels || 4 == channels || isGrayAlpha ) ) { + throw new RuntimeException("PNGPixelRect can only handle Lum/RGB/RGBA [1/3/4 channels] or Lum+A (GA) images for now. Channels "+channels + " Paletted: " + indexed); + } + final int bytesPerPixel = indexed ? channels : imgInfo.bytesPixel ; + if ( ! ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel || isGrayAlpha ) ) { + throw new RuntimeException("PNGPixelRect can only handle Lum/RGB/RGBA [1/3/4 bpp] images for now. BytesPerPixel "+bytesPerPixel); + } + if( channels != bytesPerPixel ) { + throw new RuntimeException("PNGPixelRect currently only handles Channels [1/3/4] == BytePerPixel [1/3/4], channels: "+channels+", bytesPerPixel "+bytesPerPixel); + } + final int width = imgInfo.cols; + final int height = imgInfo.rows; + final double dpiX, dpiY; + { + final double[] dpi = pngr.getMetadata().getDpi(); + dpiX = dpi[0]; + dpiY = dpi[1]; + } + final PixelFormat srcFmt; + if ( indexed ) { + if ( hasAlpha ) { + srcFmt = PixelFormat.RGBA8888; + } else { + srcFmt = PixelFormat.RGB888; + } + } else { + switch( channels ) { + case 1: srcFmt = PixelFormat.LUMINANCE; break; + case 2: srcFmt = isGrayAlpha ? PixelFormat.LUMINANCE : null; break; + case 3: srcFmt = PixelFormat.RGB888; break; + case 4: srcFmt = PixelFormat.RGBA8888; break; + default: srcFmt = null; + } + if( null == srcFmt ) { + throw new InternalError("XXX: channels: "+channels+", bytesPerPixel "+bytesPerPixel); + } + } + final PixelFormat destFmt; + if( null == ddestFmt ) { + if( isGrayAlpha ) { + destFmt = PixelFormat.BGRA8888; // save alpha value on gray-alpha + } else { + destFmt = srcFmt; // 1:1 + } + } else { + destFmt = ddestFmt; // user choice + } + final int destStrideInBytes = Math.max(destMinStrideInBytes, destFmt.bytesPerPixel() * width); + final ByteBuffer destPixels = destDirectBuffer ? Buffers.newDirectByteBuffer(destStrideInBytes * height) : + ByteBuffer.allocate(destStrideInBytes * height); + { + final int reqBytes = destStrideInBytes * height; + if( destPixels.limit() < reqBytes ) { + throw new IndexOutOfBoundsException("Dest buffer has insufficient bytes left, needs "+reqBytes+": "+destPixels); + } + } + final boolean vert_flip = destIsGLOriented; + + int[] rgbaScanline = indexed ? new int[width * channels] : null; + if(DEBUG) { + System.err.println("PNGPixelRect: indexed "+indexed+", alpha "+hasAlpha+", grayscale "+imgInfo.greyscale+", channels "+channels+"/"+imgInfo.channels+ + ", bytesPerPixel "+bytesPerPixel+"/"+imgInfo.bytesPixel+ + ", grayAlpha "+isGrayAlpha+", pixels "+width+"x"+height+", dpi "+dpiX+"x"+dpiY+", format "+srcFmt); + System.err.println("PNGPixelRect: destFormat "+destFmt+" ("+ddestFmt+", bytesPerPixel "+destFmt.bytesPerPixel()+", fast-path "+(destFmt==srcFmt)+"), destDirectBuffer "+destDirectBuffer+", destIsGLOriented (flip) "+destIsGLOriented); + System.err.println("PNGPixelRect: destStrideInBytes "+destStrideInBytes+" (destMinStrideInBytes "+destMinStrideInBytes+")"); + } + + for (int row = 0; row < height; row++) { + final ImageLine l1 = pngr.readRow(row); + int lineOff = 0; + int dataOff = vert_flip ? ( height - 1 - row ) * destStrideInBytes : row * destStrideInBytes; + if( indexed ) { + for (int j = width - 1; j >= 0; j--) { + rgbaScanline = ImageLineHelper.palette2rgb(l1, plte, trns, rgbaScanline); // reuse rgbaScanline and update if resized + dataOff = getPixelRGBA8ToAny(destFmt, destPixels, dataOff, rgbaScanline, lineOff, hasAlpha); + lineOff += bytesPerPixel; + } + } else if( 1 == channels ) { + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelLUMToAny(destFmt, destPixels, dataOff, (byte)l1.scanline[lineOff++], (byte)0xff); // Luminance, 1 bytesPerPixel + } + } else if( isGrayAlpha ) { + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelLUMToAny(destFmt, destPixels, dataOff, (byte)l1.scanline[lineOff++], (byte)l1.scanline[lineOff++]); // Luminance+Alpha, 2 bytesPerPixel + } + } else if( srcFmt == destFmt ) { // fast-path + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelRGBSame(destPixels, dataOff, l1.scanline, lineOff, bytesPerPixel); + lineOff += bytesPerPixel; + } + } else { + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelRGBA8ToAny(destFmt, destPixels, dataOff, l1.scanline, lineOff, hasAlpha); + lineOff += bytesPerPixel; + } + } + } + pngr.end(); + + return new PNGPixelRect(destFmt, new Dimension(width, height), destStrideInBytes, destIsGLOriented, destPixels, dpiX, dpiY); + } + + private static final int getPixelLUMToAny(PixelFormat dest_fmt, ByteBuffer d, int dOff, byte lum, byte alpha) { + switch(dest_fmt) { + case LUMINANCE: + d.put(dOff++, lum); + break; + case BGR888: + case RGB888: + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, lum); + break; + case ABGR8888: + case ARGB8888: + d.put(dOff++, alpha); // A + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, lum); + break; + case BGRA8888: + case RGBA8888: + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, alpha); // A + break; + default: + throw new InternalError("Unhandled format "+dest_fmt); + } + return dOff; + } + private static final int getPixelRGBA8ToAny(final PixelFormat dest_fmt, final ByteBuffer d, int dOff, final int[] scanline, final int lineOff, final boolean srcHasAlpha) { + final int p = PixelFormatUtil.convertToInt32(dest_fmt, (byte)scanline[lineOff], // R + (byte)scanline[lineOff+1], // G + (byte)scanline[lineOff+2], // B + srcHasAlpha ? (byte)scanline[lineOff+3] : (byte)0xff); // A + final int dbpp = dest_fmt.bytesPerPixel(); + d.put(dOff++, (byte) ( p )); // 1 + if( 1 < dbpp ) { + d.put(dOff++, (byte) ( p >>> 8 )); // 2 + d.put(dOff++, (byte) ( p >>> 16 )); // 3 + if( 4 == dbpp ) { + d.put(dOff++, (byte) ( p >>> 24 )); // 4 + } + } + return dOff; + } + private static final int getPixelRGBSame(final ByteBuffer d, int dOff, final int[] scanline, final int lineOff, final int bpp) { + d.put(dOff++, (byte)scanline[lineOff]); // R + if( 1 < bpp ) { + d.put(dOff++, (byte)scanline[lineOff + 1]); // G + d.put(dOff++, (byte)scanline[lineOff + 2]); // B + if( 4 == bpp ) { + d.put(dOff++, (byte)scanline[lineOff + 3]); // A + } + } + return dOff; + } + private int setPixelRGBA8(final ImageLine line, final int lineOff, final ByteBuffer d, final int dOff, final int bytesPerPixel, final boolean hasAlpha) { + final int b = hasAlpha ? 4-1 : 3-1; + if( d.limit() <= dOff + b ) { + throw new IndexOutOfBoundsException("Buffer has unsufficient bytes left, needs ["+dOff+".."+(dOff+b)+"]: "+d); + } + final int p = PixelFormatUtil.convertToInt32(hasAlpha ? PixelFormat.RGBA8888 : PixelFormat.RGB888, pixelformat, d, dOff); + line.scanline[lineOff ] = 0xff & p; // R + line.scanline[lineOff + 1] = 0xff & ( p >>> 8 ); // G + line.scanline[lineOff + 2] = 0xff & ( p >>> 16 ); // B + if(hasAlpha) { + line.scanline[lineOff + 3] = 0xff & ( p >>> 24 ); // A + } + return dOff + pixelformat.bytesPerPixel(); + } + + /** + * Creates a PNGPixelRect from data supplied by the end user. Shares + * data with the passed ByteBuffer. + * + * @param pixelformat + * @param size + * @param strideInBytes + * @param isGLOriented see {@link #isGLOriented()}. + * @param pixels + * @param dpiX + * @param dpiY + */ + public PNGPixelRect(final PixelFormat pixelformat, final DimensionImmutable size, + final int strideInBytes, final boolean isGLOriented, final ByteBuffer pixels, + final double dpiX, final double dpiY) { + super(pixelformat, size, strideInBytes, isGLOriented, pixels); + this.dpi = new double[] { dpiX, dpiY }; + } + public PNGPixelRect(final PixelRectangle src, final double dpiX, final double dpiY) { + super(src); + this.dpi = new double[] { dpiX, dpiY }; + } + private final double[] dpi; + + /** Returns the dpi of the image. */ + public double[] getDpi() { return dpi; } + + public void write(final OutputStream outstream, final boolean closeOutstream) throws IOException { + final int width = size.getWidth(); + final int height = size.getHeight(); + final int bytesPerPixel = pixelformat.bytesPerPixel(); + final ImageInfo imi = new ImageInfo(width, height, 8 /* bitdepth */, + (4 == bytesPerPixel) ? true : false /* alpha */, + (1 == bytesPerPixel) ? true : false /* grayscale */, + false /* indexed */); + + // open image for writing to a output stream + try { + final PngWriter png = new PngWriter(outstream, imi); + // add some optional metadata (chunks) + png.getMetadata().setDpi(dpi[0], dpi[1]); + png.getMetadata().setTimeNow(0); // 0 seconds fron now = now + png.getMetadata().setText(PngChunkTextVar.KEY_Title, "JogAmp PNGPixelRect"); + // png.getMetadata().setText("my key", "my text"); + final boolean hasAlpha = 4 == bytesPerPixel; + + final ImageLine l1 = new ImageLine(imi); + for (int row = 0; row < height; row++) { + int dataOff = isGLOriented ? ( height - 1 - row ) * strideInBytes : row * strideInBytes; + int lineOff = 0; + if(1 == bytesPerPixel) { + for (int j = width - 1; j >= 0; j--) { + l1.scanline[lineOff++] = pixels.get(dataOff++); // // Luminance, 1 bytesPerPixel + } + } else { + for (int j = width - 1; j >= 0; j--) { + dataOff = setPixelRGBA8(l1, lineOff, pixels, dataOff, bytesPerPixel, hasAlpha); + lineOff += bytesPerPixel; + } + } + png.writeRow(l1, row); + } + png.end(); + } finally { + if( closeOutstream ) { + IOUtil.close(outstream, false); + } + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java index 67ab5176d..0cde24db4 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java @@ -41,10 +41,12 @@ package com.jogamp.opengl.util.texture; import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -52,9 +54,10 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2GL3; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; @@ -63,11 +66,11 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.texture.spi.DDSImage; import com.jogamp.opengl.util.texture.spi.JPEGImage; import com.jogamp.opengl.util.texture.spi.NetPbmTextureWriter; -import com.jogamp.opengl.util.texture.spi.PNGImage; import com.jogamp.opengl.util.texture.spi.SGIImage; import com.jogamp.opengl.util.texture.spi.TGAImage; import com.jogamp.opengl.util.texture.spi.TextureProvider; @@ -1166,27 +1169,29 @@ public class TextureIO { boolean mipmap, String fileSuffix) throws IOException { if (PNG.equals(fileSuffix)) { - PNGImage image = PNGImage.read(/*glp, */ stream); - if (pixelFormat == 0) { - pixelFormat = image.getGLFormat(); - } - if (internalFormat == 0) { + final PNGPixelRect image = PNGPixelRect.read(stream, null, true /* directBuffer */, 0 /* destMinStrideInBytes */, true /* destIsGLOriented */); + final GLPixelAttributes glpa = GLPixelAttributes.convert(image.getPixelformat(), glp); + if ( 0 == pixelFormat ) { + pixelFormat = glpa.format; + } // else FIXME: Actually not supported w/ preset pixelFormat! + if ( 0 == internalFormat ) { + final boolean hasAlpha = 4 == glpa.bytesPerPixel; if(glp.isGL2ES3()) { - internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8; + internalFormat = hasAlpha ? GL.GL_RGBA8 : GL.GL_RGB8; } else { - internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB; + internalFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB; } } return new TextureData(glp, internalFormat, - image.getWidth(), - image.getHeight(), + image.getSize().getWidth(), + image.getSize().getHeight(), 0, pixelFormat, - image.getGLType(), + glpa.type, mipmap, false, false, - image.getData(), + image.getPixels(), null); } @@ -1392,29 +1397,7 @@ public class TextureIO { final int pixelFormat = pixelAttribs.format; final int pixelType = pixelAttribs.type; final int bytesPerPixel = pixelAttribs.bytesPerPixel; - final boolean reversedChannels; - switch(pixelFormat) { - case GL.GL_ALPHA: - case GL.GL_LUMINANCE: - case GL2ES2.GL_RED: - reversedChannels=false; - break; - case GL.GL_RGB: - reversedChannels=false; - break; - case GL.GL_RGBA: - reversedChannels=false; - break; - case GL2.GL_BGR: - reversedChannels=true; - break; - case GL.GL_BGRA: - reversedChannels=true; - break; - default: - reversedChannels=false; - break; - } + final PixelFormat pixFmt = pixelAttribs.getPixelFormat(); if ( ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel) && ( pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { ByteBuffer buf = (ByteBuffer) data.getBuffer(); @@ -1423,9 +1406,11 @@ public class TextureIO { } buf.rewind(); - PNGImage image = PNGImage.createFromData(data.getWidth(), data.getHeight(), -1f, -1f, - bytesPerPixel, reversedChannels, !data.getMustFlipVertically(), buf); - image.write(file, true); + final PNGPixelRect image = new PNGPixelRect(pixFmt, new Dimension(data.getWidth(), data.getHeight()), + 0 /* stride */, true /* isGLOriented */, buf /* pixels */, + -1f, -1f); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(file, true /* allowOverwrite */)); + image.write(outs, true /* close */); return true; } throw new IOException("PNG writer doesn't support this pixel format 0x"+Integer.toHexString(pixelFormat)+ diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java deleted file mode 100644 index 71cbbf97e..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright 2012 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.opengl.util.texture.spi; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; - -import javax.media.opengl.GL; - -import jogamp.opengl.Debug; -import jogamp.opengl.util.pngj.ImageInfo; -import jogamp.opengl.util.pngj.ImageLine; -import jogamp.opengl.util.pngj.ImageLineHelper; -import jogamp.opengl.util.pngj.PngReader; -import jogamp.opengl.util.pngj.PngWriter; -import jogamp.opengl.util.pngj.chunks.PngChunkPLTE; -import jogamp.opengl.util.pngj.chunks.PngChunkTRNS; -import jogamp.opengl.util.pngj.chunks.PngChunkTextVar; - -import com.jogamp.common.nio.Buffers; -import com.jogamp.common.util.IOUtil; - -public class PNGImage { - private static final boolean DEBUG = Debug.debug("PNGImage"); - - /** - * Creates a PNGImage from data supplied by the end user. Shares - * data with the passed ByteBuffer. Assumes the data is already in - * the correct byte order for writing to disk, i.e., LUMINANCE, RGB or RGBA. - * Orientation is <i>bottom-to-top</i> (OpenGL coord. default) - * or <i>top-to-bottom</i> depending on <code>isGLOriented</code>. - * - * @param width - * @param height - * @param dpiX - * @param dpiY - * @param bytesPerPixel - * @param reversedChannels - * @param isGLOriented see {@link #isGLOriented()}. - * @param data - * @return - */ - public static PNGImage createFromData(int width, int height, double dpiX, double dpiY, - int bytesPerPixel, boolean reversedChannels, boolean isGLOriented, ByteBuffer data) { - return new PNGImage(width, height, dpiX, dpiY, bytesPerPixel, reversedChannels, isGLOriented, data); - } - - /** - * Reads a PNG image from the specified InputStream. - * <p> - * Implicitly flip image to GL orientation, see {@link #isGLOriented()}. - * </p> - */ - public static PNGImage read(InputStream in) throws IOException { - return new PNGImage(in); - } - - /** Reverse read and store, implicitly flip image to GL orientation, see {@link #isGLOriented()}. */ - private static final int getPixelRGBA8(ByteBuffer d, int dOff, int[] scanline, int lineOff, boolean hasAlpha) { - final int b = hasAlpha ? 4-1 : 3-1; - if( d.limit() <= dOff || dOff - b < 0 ) { - throw new IndexOutOfBoundsException("Buffer has unsufficient bytes left, needs ["+(dOff-b)+".."+dOff+"]: "+d); - } - if(hasAlpha) { - d.put(dOff--, (byte)scanline[lineOff + 3]); // A - } - d.put(dOff--, (byte)scanline[lineOff + 2]); // B - d.put(dOff--, (byte)scanline[lineOff + 1]); // G - d.put(dOff--, (byte)scanline[lineOff ]); // R - return dOff; - } - - /** Reverse write and store, implicitly flip image from current orientation, see {@link #isGLOriented()}. Handle reversed channels (BGR[A]). */ - private int setPixelRGBA8(ImageLine line, int lineOff, ByteBuffer d, int dOff, boolean hasAlpha) { - final int b = hasAlpha ? 4-1 : 3-1; - if( d.limit() <= dOff + b ) { - throw new IndexOutOfBoundsException("Buffer has unsufficient bytes left, needs ["+dOff+".."+(dOff+b)+"]: "+d); - } - if( reversedChannels ) { - if(hasAlpha) { - line.scanline[lineOff + 3] = d.get(dOff++); // A - } - line.scanline[lineOff + 2] = d.get(dOff++); // R - line.scanline[lineOff + 1] = d.get(dOff++); // G - line.scanline[lineOff ] = d.get(dOff++); // B - } else { - line.scanline[lineOff ] = d.get(dOff++); // R - line.scanline[lineOff + 1] = d.get(dOff++); // G - line.scanline[lineOff + 2] = d.get(dOff++); // B - if(hasAlpha) { - line.scanline[lineOff + 3] = d.get(dOff++); // A - } - } - return isGLOriented ? dOff - bytesPerPixel - bytesPerPixel : dOff; - } - - private PNGImage(int width, int height, double dpiX, double dpiY, int bytesPerPixel, boolean reversedChannels, boolean isGLOriented, ByteBuffer data) { - pixelWidth=width; - pixelHeight=height; - dpi = new double[] { dpiX, dpiY }; - if(4 == bytesPerPixel) { - glFormat = GL.GL_RGBA; - } else if (3 == bytesPerPixel) { - glFormat = GL.GL_RGB; - } else { - throw new InternalError("XXX: bytesPerPixel "+bytesPerPixel); - } - this.bytesPerPixel = bytesPerPixel; - this.reversedChannels = reversedChannels; - this.isGLOriented = isGLOriented; - this.data = data; - } - - private PNGImage(InputStream in) { - final PngReader pngr = new PngReader(new BufferedInputStream(in), null); - final ImageInfo imgInfo = pngr.imgInfo; - final PngChunkPLTE plte = pngr.getMetadata().getPLTE(); - final PngChunkTRNS trns = pngr.getMetadata().getTRNS(); - final boolean indexed = imgInfo.indexed; - final boolean hasAlpha = indexed ? ( trns != null ) : imgInfo.alpha ; - - final int channels = indexed ? ( hasAlpha ? 4 : 3 ) : imgInfo.channels ; - if ( ! ( 1 == channels || 3 == channels || 4 == channels ) ) { - throw new RuntimeException("PNGImage can only handle Lum/RGB/RGBA [1/3/4 channels] images for now. Channels "+channels + " Paletted: " + indexed); - } - - bytesPerPixel = indexed ? channels : imgInfo.bytesPixel ; - if ( ! ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel ) ) { - throw new RuntimeException("PNGImage can only handle Lum/RGB/RGBA [1/3/4 bpp] images for now. BytesPerPixel "+bytesPerPixel); - } - if( channels != bytesPerPixel ) { - throw new RuntimeException("PNGImage currently only handles Channels [1/3/4] == BytePerPixel [1/3/4], channels: "+channels+", bytesPerPixel "+bytesPerPixel); - } - pixelWidth = imgInfo.cols; - pixelHeight = imgInfo.rows; - dpi = new double[2]; - { - final double[] dpi2 = pngr.getMetadata().getDpi(); - dpi[0]=dpi2[0]; - dpi[1]=dpi2[1]; - } - if ( indexed ) { - if ( hasAlpha ) { - glFormat = GL.GL_RGBA; - } else { - glFormat = GL.GL_RGB; - } - } else { - switch( channels ) { - case 1: glFormat = GL.GL_LUMINANCE; break; - case 3: glFormat = GL.GL_RGB; break; - case 4: glFormat = GL.GL_RGBA; break; - default: throw new InternalError("XXX: channels: "+channels+", bytesPerPixel "+bytesPerPixel); - } - } - if(DEBUG) { - System.err.println("PNGImage: "+imgInfo); - System.err.println("PNGImage: indexed "+indexed+", alpha "+hasAlpha+", channels "+channels+"/"+imgInfo.channels+ - ", bytesPerPixel "+bytesPerPixel+"/"+imgInfo.bytesPixel+ - ", pixels "+pixelWidth+"x"+pixelHeight+", dpi "+dpi[0]+"x"+dpi[1]+", glFormat 0x"+Integer.toHexString(glFormat)); - } - - data = Buffers.newDirectByteBuffer(bytesPerPixel * pixelWidth * pixelHeight); - reversedChannels = false; // RGB[A] - isGLOriented = true; - int dataOff = bytesPerPixel * pixelWidth * pixelHeight - 1; // start at end-of-buffer, reverse store - - int[] rgbaScanline = indexed ? new int[imgInfo.cols * channels] : null; - - for (int row = 0; row < pixelHeight; row++) { - final ImageLine l1 = pngr.readRow(row); - int lineOff = ( pixelWidth - 1 ) * bytesPerPixel ; // start w/ last pixel in line, reverse read (PNG top-left -> OpenGL bottom-left origin) - if( indexed ) { - for (int j = pixelWidth - 1; j >= 0; j--) { - rgbaScanline = ImageLineHelper.palette2rgb(l1, plte, trns, rgbaScanline); // reuse rgbaScanline and update if resized - dataOff = getPixelRGBA8(data, dataOff, rgbaScanline, lineOff, hasAlpha); - lineOff -= bytesPerPixel; - } - } else if( 1 == channels ) { - for (int j = pixelWidth - 1; j >= 0; j--) { - data.put(dataOff--, (byte)l1.scanline[lineOff--]); // Luminance, 1 bytesPerPixel - } - } else { - for (int j = pixelWidth - 1; j >= 0; j--) { - dataOff = getPixelRGBA8(data, dataOff, l1.scanline, lineOff, hasAlpha); - lineOff -= bytesPerPixel; - } - } - } - pngr.end(); - } - private final int pixelWidth, pixelHeight, glFormat, bytesPerPixel; - private final boolean reversedChannels; - private final boolean isGLOriented; - private final double[] dpi; - private final ByteBuffer data; - - /** Returns the width of the image. */ - public int getWidth() { return pixelWidth; } - - /** Returns the height of the image. */ - public int getHeight() { return pixelHeight; } - - /** Returns true if data has the channels reversed to BGR or BGRA, otherwise RGB or RGBA is expected. */ - public boolean getHasReversedChannels() { return reversedChannels; } - - /** - * Returns <code>true</code> if the drawable is rendered in - * OpenGL's coordinate system, <i>origin at bottom left</i>. - * Otherwise returns <code>false</code>, i.e. <i>origin at top left</i>. - * <p> - * Default impl. is <code>true</code>, i.e. OpenGL coordinate system. - * </p> - */ - public boolean isGLOriented() { return isGLOriented; } - - /** Returns the dpi of the image. */ - public double[] getDpi() { return dpi; } - - /** Returns the OpenGL format for this texture; e.g. GL.GL_LUMINANCE, GL.GL_RGB or GL.GL_RGBA. */ - public int getGLFormat() { return glFormat; } - - /** Returns the OpenGL data type: GL.GL_UNSIGNED_BYTE. */ - public int getGLType() { return GL.GL_UNSIGNED_BYTE; } - - /** Returns the bytes per pixel */ - public int getBytesPerPixel() { return bytesPerPixel; } - - /** Returns the raw data for this texture in the correct - (bottom-to-top) order for calls to glTexImage2D. */ - public ByteBuffer getData() { return data; } - - public void write(File out, boolean allowOverwrite) throws IOException { - final ImageInfo imi = new ImageInfo(pixelWidth, pixelHeight, 8, (4 == bytesPerPixel) ? true : false); // 8 bits per channel, no alpha - // open image for writing to a output stream - final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out, allowOverwrite)); - try { - final PngWriter png = new PngWriter(outs, imi); - // add some optional metadata (chunks) - png.getMetadata().setDpi(dpi[0], dpi[1]); - png.getMetadata().setTimeNow(0); // 0 seconds fron now = now - png.getMetadata().setText(PngChunkTextVar.KEY_Title, "JogAmp PNGImage"); - // png.getMetadata().setText("my key", "my text"); - final boolean hasAlpha = 4 == bytesPerPixel; - final ImageLine l1 = new ImageLine(imi); - if( isGLOriented ) { - // start at last pixel at end-of-buffer, reverse read (OpenGL bottom-left -> PNG top-left origin) - int dataOff = ( pixelWidth * bytesPerPixel * ( pixelHeight - 1 ) ) + // full lines - 1 line - ( ( pixelWidth - 1 ) * bytesPerPixel ); // one line - 1 pixel - for (int row = 0; row < pixelHeight; row++) { - int lineOff = ( pixelWidth - 1 ) * bytesPerPixel ; // start w/ last pixel in line, reverse store (OpenGL bottom-left -> PNG top-left origin) - if(1 == bytesPerPixel) { - for (int j = pixelWidth - 1; j >= 0; j--) { - l1.scanline[lineOff--] = data.get(dataOff--); // // Luminance, 1 bytesPerPixel - } - } else { - for (int j = pixelWidth - 1; j >= 0; j--) { - dataOff = setPixelRGBA8(l1, lineOff, data, dataOff, hasAlpha); - lineOff -= bytesPerPixel; - } - } - png.writeRow(l1, row); - } - } else { - int dataOff = 0; // start at first pixel at start-of-buffer, normal read (same origin: top-left) - for (int row = 0; row < pixelHeight; row++) { - int lineOff = 0; // start w/ first pixel in line, normal store (same origin: top-left) - if(1 == bytesPerPixel) { - for (int j = pixelWidth - 1; j >= 0; j--) { - l1.scanline[lineOff++] = data.get(dataOff++); // // Luminance, 1 bytesPerPixel - } - } else { - for (int j = pixelWidth - 1; j >= 0; j--) { - dataOff = setPixelRGBA8(l1, lineOff, data, dataOff, hasAlpha); - lineOff += bytesPerPixel; - } - } - png.writeRow(l1, row); - } - } - png.end(); - } finally { - IOUtil.close(outs, false); - } - } - - @Override - public String toString() { return "PNGImage["+pixelWidth+"x"+pixelHeight+", dpi "+dpi[0]+" x "+dpi[1]+", bytesPerPixel "+bytesPerPixel+", reversedChannels "+reversedChannels+", "+data+"]"; } -} diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java index b947505cb..ddf513180 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java @@ -38,7 +38,6 @@ package com.jogamp.nativewindow.awt; import com.jogamp.common.os.Platform; -import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.awt.AWTEDTExecutor; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; @@ -53,7 +52,6 @@ import java.awt.event.ComponentListener; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.applet.Applet; -import java.io.IOException; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -66,6 +64,7 @@ import javax.media.nativewindow.OffscreenLayerSurface; import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.PixelRectangle; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.Rectangle; @@ -433,19 +432,19 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } @Override - public final boolean setCursor(final IOUtil.ClassResources resources, final PointImmutable hotSpot) throws IOException { + public final boolean setCursor(final PixelRectangle pixelrect, final PointImmutable hotSpot) { AWTEDTExecutor.singleton.invoke(false, new Runnable() { public void run() { Cursor c = null; - if( null == resources || null == hotSpot ) { + if( null == pixelrect || null == hotSpot ) { c = Cursor.getDefaultCursor(); } else { final java.awt.Point awtHotspot = new java.awt.Point(hotSpot.getX(), hotSpot.getY()); try { - c = AWTMisc.getCursor(resources, awtHotspot); - } catch (IOException e) { - e.printStackTrace(); - } + c = AWTMisc.getCursor(pixelrect, awtHotspot); + } catch (Exception e) { + e.printStackTrace(); + } } if( null != c ) { component.setCursor(c); @@ -455,7 +454,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } @Override - public boolean hideCursor() { + public final boolean hideCursor() { AWTEDTExecutor.singleton.invoke(false, new Runnable() { public void run() { component.setCursor(AWTMisc.getNullCursor()); diff --git a/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java b/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java index 81983a7c2..cf8cf89d2 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java +++ b/src/nativewindow/classes/javax/media/nativewindow/OffscreenLayerSurface.java @@ -27,11 +27,9 @@ */ package javax.media.nativewindow; -import java.io.IOException; - +import javax.media.nativewindow.util.PixelRectangle; import javax.media.nativewindow.util.PointImmutable; -import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.locks.RecursiveLock; /** @@ -73,12 +71,12 @@ public interface OffscreenLayerSurface { /** * Optional method setting cursor in the corresponding on-screen surface/window, if exists. * - * @param resources maybe null for default cursor + * @param pixelrect cursor pixels, maybe null for default cursor * @param hotSpot maybe null for default cursor * @return true if successful, i.e. on-screen surface/window w/ cursor capabilities exists. Otherwise false. - * @throws IOException */ - public boolean setCursor(IOUtil.ClassResources resources, PointImmutable hotSpot) throws IOException; + public boolean setCursor(PixelRectangle pixelrect, PointImmutable hotSpot); + /** * Optional method hiding the cursor in the corresponding on-screen surface/window, if exists. * diff --git a/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java b/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java index b690aff4d..069cffeb8 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java +++ b/src/nativewindow/classes/jogamp/nativewindow/awt/AWTMisc.java @@ -37,21 +37,19 @@ import java.awt.Component; import java.awt.Container; import java.awt.Frame; import java.awt.image.BufferedImage; -import java.io.IOException; -import java.net.URLConnection; import java.util.HashMap; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JRootPane; import javax.swing.WindowConstants; -import javax.imageio.ImageIO; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelFormatUtil; import javax.swing.MenuSelectionManager; -import com.jogamp.common.util.IOUtil; - public class AWTMisc { public static JFrame getJFrame(Component c) { @@ -173,7 +171,7 @@ public class AWTMisc { MenuSelectionManager.defaultManager().clearSelectedPath(); } - static final HashMap<String, Cursor> cursorMap = new HashMap<String, Cursor>(); + static final HashMap<Integer, Cursor> cursorMap = new HashMap<Integer, Cursor>(); static final Cursor nulCursor; static { final Toolkit toolkit = Toolkit.getDefaultToolkit(); @@ -183,21 +181,43 @@ public class AWTMisc { public static synchronized Cursor getNullCursor() { return nulCursor; } - public static synchronized Cursor getCursor(IOUtil.ClassResources resources, Point hotSpot) throws IOException { - final String key = resources.getClass().getName()+":"+resources.resourcePaths[0]; + public static synchronized Cursor getCursor(PixelRectangle pixelrect, Point hotSpot) { + // 31 * x == (x << 5) - x + int hash = 31 + pixelrect.hashCode(); + hash = ((hash << 5) - hash) + hotSpot.hashCode(); + final Integer key = Integer.valueOf(hash); + Cursor cursor = cursorMap.get(key); if( null == cursor ) { - cursor = createAWTCursor(resources, hotSpot); + cursor = createCursor(pixelrect, hotSpot); cursorMap.put(key, cursor); } return cursor; } - private static synchronized Cursor createAWTCursor(IOUtil.ClassResources resources, Point hotSpot) throws IOException { - final URLConnection urlConn = resources.resolve(0); - final BufferedImage img = ImageIO.read(urlConn.getInputStream()); - + private static synchronized Cursor createCursor(PixelRectangle pixelrect, 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(int x, int y, 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 Toolkit toolkit = Toolkit.getDefaultToolkit(); - return toolkit.createCustomCursor(img, hotSpot, resources.resourcePaths[0]); + return toolkit.createCustomCursor(img, hotSpot, pixelrect.toString()); } public static WindowClosingProtocol.WindowClosingMode AWT2NWClosingOperation(int awtClosingOperation) { diff --git a/src/newt/classes/com/jogamp/newt/Display.java b/src/newt/classes/com/jogamp/newt/Display.java index 8d1445f80..4b38fcca5 100644 --- a/src/newt/classes/com/jogamp/newt/Display.java +++ b/src/newt/classes/com/jogamp/newt/Display.java @@ -30,14 +30,14 @@ package com.jogamp.newt; import java.io.IOException; import java.lang.ref.WeakReference; -import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormat; import javax.media.nativewindow.util.PointImmutable; import jogamp.newt.Debug; @@ -47,6 +47,7 @@ import com.jogamp.newt.util.EDTUtil; public abstract class Display { public static final boolean DEBUG = Debug.debug("Display"); + protected static final boolean DEBUG_POINTER_ICON = Debug.debug("Display.PointerIcon"); /** return precomputed hashCode from FQN {@link #getFQName()} */ @Override @@ -66,7 +67,9 @@ public abstract class Display { /** * Native PointerIcon handle. * <p> - * Instances can be created via {@link Display}'s {@link Display#createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int) createPointerIcon(..)}. + * Instances can be created via {@link Display}'s + * {@link Display#createPointerIcon(com.jogamp.common.util.IOUtil.ClassResources, int, int) createPointerIcon(pngResource, ..)} + * or {@link Display#createPointerIcon(PixelRectangle, int, int) createPointerIcon(pixelrect, ..)}. * </p> * <p> * Instance is {@link #destroy()}'ed automatically if it's {@link #getDisplay() associated Display} is destroyed. @@ -75,23 +78,65 @@ public abstract class Display { * Instance can be re-validated after destruction via {@link #validate()}. * </p> * <p> + * {@link PointerIcon} must not be {@link #destroy() destroyed} while in use! + * </p> + * <p> * {@link PointerIcon} may be {@link #destroy() destroyed} manually after use, * i.e. when no {@link Window} {@link Window#setPointerIcon(PointerIcon) uses them} anymore. + * However, this is not required. * </p> * <p> * PointerIcons can be used via {@link Window#setPointerIcon(PointerIcon)}. * </p> */ - public static interface PointerIcon { + public static interface PointerIcon extends PixelRectangle { /** - * @return the associated Display + * Always neatly packed, i.e. width * bytes_per_pixel. + * <p> + * {@inheritDoc} + * </p> */ - Display getDisplay(); + @Override + int getStride(); + + /** + * Always false, i.e. origin is TOP-LEFT. + * <p> + * {@inheritDoc} + * </p> + */ + boolean isGLOriented(); + + /** + * Computes a hash code over: + * <ul> + * <li>display</li> + * <li>pixelformat</li> + * <li>size</li> + * <li>stride</li> + * <li>isGLOriented</li> + * <li>pixels</li> + * <li>hotspot</li> + * </ul> + * Dismissing the native handle! + * <p> + * The hashCode shall be computed only once with first call + * and stored for later retrieval to enhance performance. + * </p> + * <p> + * {@inheritDoc} + * </p> + */ + @Override + int hashCode(); /** - * @return the single {@link IOUtil.ClassResources}. + * @return the associated Display */ - IOUtil.ClassResources getResource(); + Display getDisplay(); + + /** Returns the hotspot. */ + PointImmutable getHotspot(); /** * Returns true if valid, otherwise false. @@ -116,35 +161,69 @@ public abstract class Display { * </p> */ void destroy(); + } - /** Returns the size, i.e. width and height. */ - DimensionImmutable getSize(); + /** + * Returns the native platform's {@link PointerIcon.PixelFormat} for pointer-icon pixel data. + * <p> + * Using this value will avoid conversion within {@link #createPointerIcon(PixelRectangle, int, int)}. + * </p> + */ + public abstract PixelFormat getNativePointerIconPixelFormat(); - /** Returns the hotspot. */ - PointImmutable getHotspot(); + /** + * Returns the native platform's direct NIO buffer requirement pointer-icon pixel data. + * <p> + * Using this value will avoid conversion within {@link #createPointerIcon(PixelRectangle, int, int)}. + * </p> + */ + public abstract boolean getNativePointerIconForceDirectNIO(); - @Override - String toString(); - } + /** + * Returns the newly created {@link PointerIcon} or <code>null</code> if not implemented on platform. + * <p> + * See {@link PointerIcon} for lifecycle semantics. + * </p> + * + * @param pngResource single PNG resource for the {@link PointerIcon}. Only the first entry of {@link IOUtil.ClassResources#resourcePaths} is used. + * @param hotX pointer hotspot x-coord, origin is upper-left corner + * @param hotY pointer hotspot y-coord, origin is upper-left corner + * + * @throws IllegalArgumentException if pngResource is null or invalid + * @throws IllegalStateException if this Display instance is not {@link #isNativeValid() valid yet}. + * @throws IOException if the <code>pngResource</code> could not be {@link IOUtil.ClassResources#resolve(int) resolved} + * or via the PNG parser processing the input stream. + * + * @see PointerIcon + * @see Window#setPointerIcon(PointerIcon) + */ + public abstract PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException, IOException; /** * Returns the newly created {@link PointerIcon} or <code>null</code> if not implemented on platform. * <p> * See {@link PointerIcon} for lifecycle semantics. * </p> + * <p> + * In case {@link #getNativePointerIconPixelFormat()} or {@link #getNativePointerIconForceDirectNIO()} + * is not matched by the given <code>pixelrect</code>, the <code>pixelrect</code> is converted + * into the required {@link PixelFormat} and NIO type. + * </p> * - * @param pngResource single PNG resource, only the first entry of {@link IOUtil.ClassResources#resourcePaths} is used. + * @param pixelrect {@link PixelRectangle} source for the {@link PointerIcon} * @param hotX pointer hotspot x-coord, origin is upper-left corner * @param hotY pointer hotspot y-coord, origin is upper-left corner + * + * @throws IllegalArgumentException if pixelrect is null. * @throws IllegalStateException if this Display instance is not {@link #isNativeValid() valid yet}. - * @throws MalformedURLException - * @throws InterruptedException - * @throws IOException * * @see PointerIcon * @see Window#setPointerIcon(PointerIcon) + * @see #getNativePointerIconPixelFormat() + * @see #getNativePointerIconForceDirectNIO() */ - public abstract PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws IllegalStateException, MalformedURLException, InterruptedException, IOException; + public abstract PointerIcon createPointerIcon(final PixelRectangle pixelrect, final int hotX, final int hotY) throws IllegalArgumentException, IllegalStateException; /** * Manual trigger the native creation, if it is not done yet.<br> diff --git a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java index 7ffbc2e83..472672fd9 100644 --- a/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java +++ b/src/newt/classes/com/jogamp/newt/awt/applet/JOGLNewtAppletBase.java @@ -235,10 +235,10 @@ public class JOGLNewtAppletBase implements KeyListener, GLEventListener { glWindow.setVisible(true); glWindow.sendWindowEvent(WindowEvent.EVENT_WINDOW_RESIZED); if( null == pointerIconTest ) { - final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/jogamp-32x32.png" } ); + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/crosshair-lumina-trans-32x32.png" } ); final Display disp = glWindow.getScreen().getDisplay(); try { - pointerIconTest = disp.createPointerIcon(res, 16, 0); + pointerIconTest = disp.createPointerIcon(res, 16, 16); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index 346c03b15..4c7169ec4 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -34,7 +34,9 @@ package jogamp.newt; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.ReflectionUtil; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.event.NEWTEvent; @@ -43,17 +45,25 @@ import com.jogamp.newt.event.NEWTEventConsumer; import jogamp.newt.event.NEWTEventTask; import com.jogamp.newt.util.EDTUtil; +import com.jogamp.opengl.util.PNGPixelRect; import java.io.IOException; -import java.net.MalformedURLException; +import java.net.URLConnection; +import java.nio.ByteBuffer; import java.util.ArrayList; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.util.PixelFormatUtil; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PointImmutable; public abstract class DisplayImpl extends Display { private static int serialno = 1; + private static final boolean pngUtilAvail; static { NativeWindowFactory.addCustomShutdownHook(true /* head */, new Runnable() { @@ -64,8 +74,14 @@ public abstract class DisplayImpl extends Display { DisplayImpl.shutdownAll(); } }); + + final ClassLoader cl = DisplayImpl.class.getClassLoader(); + pngUtilAvail = ReflectionUtil.isClassAvailable("jogamp.opengl.util.pngj.PngReader", cl) && + ReflectionUtil.isClassAvailable("com.jogamp.opengl.util.PNGPixelRect", cl); } + public static final boolean isPNGUtilAvailable() { return pngUtilAvail; } + final ArrayList<PointerIconImpl> pointerIconList = new ArrayList<PointerIconImpl>(); /** Executed from EDT! */ @@ -86,13 +102,94 @@ public abstract class DisplayImpl extends Display { } @Override - public final PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { - return createPointerIcon(false /* isTemp */, pngResource, hotX, hotY); + public PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.BGRA8888; } + @Override + public boolean getNativePointerIconForceDirectNIO() { return false; } + + @Override + public final PointerIcon createPointerIcon(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException, IOException + { + if( !isNativeValid() ) { + throw new IllegalStateException("Display.createPointerIcon(1): Display invalid "+this); + } + if( null == pngResource || 0 >= pngResource.resourceCount() ) { + throw new IllegalArgumentException("Null or invalid pngResource "+pngResource); + } + if( !pngUtilAvail ) { + return null; + } + final PointerIconImpl[] res = { null }; + runOnEDTIfAvail(true, new Runnable() { + public void run() { + try { + if( !DisplayImpl.this.isNativeValid() ) { + throw new IllegalStateException("Display.createPointerIcon(2): Display invalid "+DisplayImpl.this); + } + final URLConnection urlConn = pngResource.resolve(0); + if( null == urlConn ) { + throw new IOException("Could not resolve "+pngResource.resourcePaths[0]); + } + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), + getNativePointerIconPixelFormat(), + getNativePointerIconForceDirectNIO(), + 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + final long handle = createPointerIconImplChecked(image.getPixelformat(), image.getSize().getWidth(), image.getSize().getHeight(), + image.getPixels(), hotX, hotY); + final PointImmutable hotspot = new Point(hotX, hotY); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconPNG.0: "+image+", handle: "+toHexString(handle)+", hot "+hotspot); + } + if( 0 != handle ) { + res[0] = new PointerIconImpl(DisplayImpl.this, image, hotspot, handle); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconPNG.0: "+res[0]); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } } ); + if( null != res[0] ) { + synchronized(pointerIconList) { + pointerIconList.add(res[0]); + } + } + return res[0]; } - PointerIcon createPointerIcon(final boolean isTemp, final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { + + @Override + public final PointerIcon createPointerIcon(final PixelRectangle pixelrect, final int hotX, final int hotY) + throws IllegalArgumentException, IllegalStateException + { if( !isNativeValid() ) { throw new IllegalStateException("Display.createPointerIcon(1): Display invalid "+this); } + if( null == pixelrect ) { + throw new IllegalArgumentException("Null or pixelrect"); + } + final PixelRectangle fpixelrect; + if( getNativePointerIconPixelFormat() != pixelrect.getPixelformat() || pixelrect.isGLOriented() ) { + // conversion ! + fpixelrect = PixelFormatUtil.convert32(pixelrect, getNativePointerIconPixelFormat(), + 0 /* ddestStride */, false /* isGLOriented */, getNativePointerIconForceDirectNIO() ); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconRES.0: Conversion-FMT "+pixelrect+" -> "+fpixelrect); + } + } else if( getNativePointerIconForceDirectNIO() && !Buffers.isDirect(pixelrect.getPixels()) ) { + // transfer to direct NIO + final ByteBuffer sBB = pixelrect.getPixels(); + final ByteBuffer dBB = Buffers.newDirectByteBuffer(sBB.array(), sBB.arrayOffset()); + fpixelrect = new PixelRectangle.GenericPixelRect(pixelrect.getPixelformat(), pixelrect.getSize(), pixelrect.getStride(), pixelrect.isGLOriented(), dBB); + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconRES.0: Conversion-NIO "+pixelrect+" -> "+fpixelrect); + } + } else { + fpixelrect = pixelrect; + if( DEBUG_POINTER_ICON ) { + System.err.println("createPointerIconRES.0: No conversion "+fpixelrect); + } + } final PointerIconImpl[] res = { null }; runOnEDTIfAvail(true, new Runnable() { public void run() { @@ -100,22 +197,63 @@ public abstract class DisplayImpl extends Display { if( !DisplayImpl.this.isNativeValid() ) { throw new IllegalStateException("Display.createPointerIcon(2): Display invalid "+DisplayImpl.this); } - res[0] = createPointerIconImpl(pngResource, hotX, hotY); + if( null != fpixelrect ) { + final long handle = createPointerIconImplChecked(fpixelrect.getPixelformat(), + fpixelrect.getSize().getWidth(), + fpixelrect.getSize().getHeight(), + fpixelrect.getPixels(), hotX, hotY); + if( 0 != handle ) { + res[0] = new PointerIconImpl(DisplayImpl.this, fpixelrect, new Point(hotX, hotY), handle); + } + } } catch (Exception e) { e.printStackTrace(); } } } ); - if( !isTemp ) { + if( null != res[0] ) { synchronized(pointerIconList) { pointerIconList.add(res[0]); } } return res[0]; } - /** Executed from EDT! */ - protected PointerIconImpl createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { - return null; + + /** + * Executed from EDT! + * + * @param pixelformat the <code>pixels</code>'s format + * @param width the <code>pixels</code>'s width + * @param height the <code>pixels</code>'s height + * @param pixels the <code>pixels</code> + * @param hotX the PointerIcon's hot-spot x-coord + * @param hotY the PointerIcon's hot-spot x-coord + * @return if successful a valid handle (not null), otherwise null. + */ + protected final long createPointerIconImplChecked(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + if( getNativePointerIconPixelFormat() != pixelformat ) { + throw new IllegalArgumentException("Pixelformat no "+getNativePointerIconPixelFormat()+", but "+pixelformat); + } + if( getNativePointerIconForceDirectNIO() && !Buffers.isDirect(pixels) ) { + throw new IllegalArgumentException("pixel buffer is not direct "+pixels); + } + return createPointerIconImpl(pixelformat, width, height, pixels, hotX, hotY); + } + + /** + * Executed from EDT! + * + * @param pixelformat the <code>pixels</code>'s format + * @param width the <code>pixels</code>'s width + * @param height the <code>pixels</code>'s height + * @param pixels the <code>pixels</code> + * @param hotX the PointerIcon's hot-spot x-coord + * @param hotY the PointerIcon's hot-spot x-coord + * @return if successful a valid handle (not null), otherwise null. + */ + protected long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return 0; } + /** Executed from EDT! */ protected void destroyPointerIconImpl(final long displayHandle, long piHandle) { } diff --git a/src/newt/classes/jogamp/newt/PointerIconImpl.java b/src/newt/classes/jogamp/newt/PointerIconImpl.java index e2388be67..7c8535f20 100644 --- a/src/newt/classes/jogamp/newt/PointerIconImpl.java +++ b/src/newt/classes/jogamp/newt/PointerIconImpl.java @@ -27,27 +27,63 @@ */ package jogamp.newt; +import java.nio.ByteBuffer; + import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelRectangle; import javax.media.nativewindow.util.PointImmutable; -import com.jogamp.common.util.IOUtil; -import com.jogamp.common.util.IOUtil.ClassResources; import com.jogamp.newt.Display; import com.jogamp.newt.Display.PointerIcon; public class PointerIconImpl implements PointerIcon { private final DisplayImpl display; - private final IOUtil.ClassResources resource; + private final PixelFormat pixelformat; private final DimensionImmutable size; + private final ByteBuffer pixels; private final PointImmutable hotspot; private long handle; - public PointerIconImpl(DisplayImpl display, ClassResources resource, final DimensionImmutable size, final PointImmutable hotspot, final long handle) { + private int hashCode = 0; + private volatile boolean hashCodeComputed = false; + + public PointerIconImpl(final DisplayImpl display, final PixelFormat pixelformat, final DimensionImmutable size, final ByteBuffer pixels, final PointImmutable hotspot, final long handle) { this.display = display; - this.resource = resource; + this.pixelformat = pixelformat; this.size = size; + this.pixels = pixels; this.hotspot = hotspot; + this.handle=handle; } + public PointerIconImpl(final DisplayImpl display, final PixelRectangle pixelrect, final PointImmutable hotspot, final long handle) { + this.display = display; + this.pixelformat = pixelrect.getPixelformat(); + this.size = pixelrect.getSize(); + this.pixels = pixelrect.getPixels(); + this.hotspot = hotspot; + this.handle=handle; + } + + @Override + public int hashCode() { + if( !hashCodeComputed ) { // DBL CHECKED OK VOLATILE + synchronized (this) { + if( !hashCodeComputed ) { + // 31 * x == (x << 5) - x + int hash = 31 + display.getFQName().hashCode(); + hash = ((hash << 5) - hash) + pixelformat.hashCode(); + hash = ((hash << 5) - hash) + size.hashCode(); + hash = ((hash << 5) - hash) + getStride(); + hash = ((hash << 5) - hash) + ( isGLOriented() ? 1 : 0); + hash = ((hash << 5) - hash) + pixels.hashCode(); + hashCode = ((hash << 5) - hash) + hotspot.hashCode(); + } + } + } + return hashCode; + } + public synchronized final long getHandle() { return handle; } public synchronized final long validatedHandle() { synchronized(display.pointerIconList) { @@ -57,8 +93,7 @@ public class PointerIconImpl implements PointerIcon { } if( 0 == handle ) { try { - final PointerIconImpl temp = (PointerIconImpl) display.createPointerIcon(true /* isTemp */, resource, hotspot.getX(), hotspot.getY()); - handle = temp.handle; + handle = display.createPointerIconImpl(pixelformat, size.getWidth(), size.getHeight(), pixels, hotspot.getX(), hotspot.getY()); return handle; } catch (Exception e) { e.printStackTrace(); @@ -71,7 +106,9 @@ public class PointerIconImpl implements PointerIcon { @Override public final Display getDisplay() { return display; } @Override - public final IOUtil.ClassResources getResource() { return resource; } + public final PixelFormat getPixelformat() { return pixelformat; } + @Override + public final ByteBuffer getPixels() { return pixels; } @Override public synchronized final boolean isValid() { return 0 != handle; } @Override @@ -116,11 +153,19 @@ public class PointerIconImpl implements PointerIcon { return size; } @Override + public final int getStride() { + return size.getWidth() * pixelformat.bytesPerPixel(); + } + @Override + public final boolean isGLOriented() { + return false; + } + @Override public final PointImmutable getHotspot() { return hotspot; } @Override public final String toString() { - return "PointerIcon["+DisplayImpl.toHexString(super.hashCode())+", "+display.getFQName()+", "+resource.resourcePaths[0]+", 0x"+Long.toHexString(handle)+", "+size+", "+hotspot+"]"; + return "PointerIcon[obj 0x"+Integer.toHexString(super.hashCode())+", "+display.getFQName()+", 0x"+Long.toHexString(handle)+", "+pixelformat+", "+size+", "+hotspot+", pixels "+pixels+"]"; } }
\ No newline at end of file diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 50f30f04d..dc9affd63 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -53,6 +53,7 @@ import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.nativewindow.util.PixelRectangle; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.PointImmutable; import javax.media.nativewindow.util.Rectangle; @@ -61,7 +62,6 @@ import javax.media.nativewindow.util.RectangleImmutable; import jogamp.nativewindow.SurfaceUpdatedHelper; import com.jogamp.common.util.ArrayHashSet; -import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.IntBitfield; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.locks.LockFactory; @@ -1700,7 +1700,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } /** * Helper method to delegate {@link #setPointerVisibleImpl(boolean)} to - * {@link OffscreenLayerSurface#hideCursor()} or {@link OffscreenLayerSurface#setCursor(IOUtil.ClassResources, PointImmutable)}. + * {@link OffscreenLayerSurface#hideCursor()} or {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)}. * <p> * Note: JAWTWindow is an OffscreenLayerSurface. * </p> @@ -1755,7 +1755,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } /** * Helper method to delegate {@link #setPointerIconIntern(PointerIconImpl)} to - * {@link OffscreenLayerSurface#setCursor(IOUtil.ClassResources, PointImmutable)}. + * {@link OffscreenLayerSurface#setCursor(PixelRectangle, PointImmutable)} * <p> * Note: JAWTWindow is an OffscreenLayerSurface. * </p> @@ -1777,7 +1777,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer final OffscreenLayerSurface ols = (OffscreenLayerSurface) parent; try { if( null != pi ) { - return ols.setCursor(pi.getResource(), pi.getHotspot()); + return ols.setCursor(pi, pi.getHotspot()); } else { return ols.setCursor(null, null); // default } diff --git a/src/newt/classes/jogamp/newt/driver/PNGIcon.java b/src/newt/classes/jogamp/newt/driver/PNGIcon.java index ffa62978f..967acd413 100644 --- a/src/newt/classes/jogamp/newt/driver/PNGIcon.java +++ b/src/newt/classes/jogamp/newt/driver/PNGIcon.java @@ -32,6 +32,7 @@ import java.net.MalformedURLException; import java.nio.ByteBuffer; import jogamp.newt.Debug; +import jogamp.newt.DisplayImpl; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.ReflectionUtil; @@ -45,8 +46,7 @@ public class PNGIcon { Debug.initSingleton(); final ClassLoader cl = PNGIcon.class.getClassLoader(); - avail = ReflectionUtil.isClassAvailable("jogamp.newt.driver.opengl.JoglUtilPNGIcon", cl) && - ReflectionUtil.isClassAvailable("com.jogamp.opengl.util.texture.spi.PNGImage", cl); + avail = DisplayImpl.isPNGUtilAvailable() && ReflectionUtil.isClassAvailable("jogamp.newt.driver.opengl.JoglUtilPNGIcon", cl); } /** Returns true if PNG decoder is available. */ @@ -55,7 +55,11 @@ public class PNGIcon { } /** - * Implemented for X11. + * Special implementation for X11 Window Icons + * <p> + * The returned byte buffer is a direct buffer! + * </p> + * * @param resources * @param data_size * @param elem_bytesize @@ -73,29 +77,4 @@ public class PNGIcon { } throw new UnsupportedOperationException(err0); } - - /** - * Implemented for Windows. - * @param resources - * @param toBGRA if true, arranges stores in BGRA888 order, otherwise RGBA888 - * @param width - * @param height - * @param data_size - * @param resourcesIdx - * @return pixels with origin at upper-left corner. - * If storing RGBA8888, component R is located on the lowest 8-bit. - * If storing BGRA8888, component B is located on the lowest 8-bit. - * Component A is located on the highest 8-bit. - * - * @throws UnsupportedOperationException if not implemented - * @throws InterruptedException - * @throws IOException - * @throws MalformedURLException - */ - public static ByteBuffer singleToRGBAImage(IOUtil.ClassResources resources, int resourceIdx, boolean toBGRA, int[] width, int[] height, int[] data_size) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { - if( avail ) { - return jogamp.newt.driver.opengl.JoglUtilPNGIcon.singleToRGBAImage(resources, resourceIdx, toBGRA, width, height, data_size); - } - throw new UnsupportedOperationException(err0); - } } diff --git a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index 30583c48c..d850a18af 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java @@ -34,28 +34,25 @@ package jogamp.newt.driver.macosx; -import java.io.IOException; -import java.net.MalformedURLException; +import java.net.URLConnection; import java.nio.Buffer; import java.nio.ByteBuffer; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PixelFormat; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; import com.jogamp.newt.NewtFactory; +import com.jogamp.opengl.util.PNGPixelRect; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; -import jogamp.newt.PointerIconImpl; -import jogamp.newt.driver.PNGIcon; public class DisplayDriver extends DisplayImpl { - private static final int defaultIconWidth, defaultIconHeight; - private static final Buffer defaultIconData; + private static final PNGPixelRect defaultIconData; static { NEWTJNILibLoader.loadNEWT(); @@ -67,21 +64,23 @@ public class DisplayDriver extends DisplayImpl { throw new NativeWindowException("Failed to initialize jmethodIDs"); } { - final int[] width = { 0 }, height = { 0 }, data_size = { 0 }; - Buffer data=null; - if( PNGIcon.isAvailable() ) { + PNGPixelRect image=null; + if( DisplayImpl.isPNGUtilAvailable() ) { try { + // NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); - data = PNGIcon.singleToRGBAImage(iconRes, iconRes.resourceCount()-1, false /* toBGRA */, width, height, data_size); + final URLConnection urlConn = iconRes.resolve(iconRes.resourceCount()-1); + image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, true /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); } catch (Exception e) { e.printStackTrace(); } } - defaultIconWidth = width[0]; - defaultIconHeight = height[0]; - defaultIconData = data; + defaultIconData = image; if( null != defaultIconData ) { - DisplayDriver.setAppIcon0(defaultIconData, defaultIconWidth, defaultIconHeight); + final Buffer pixels = defaultIconData.getPixels(); + DisplayDriver.setAppIcon0( + pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */, + defaultIconData.getSize().getWidth(), defaultIconData.getSize().getHeight()); } } @@ -112,17 +111,20 @@ public class DisplayDriver extends DisplayImpl { aDevice.close(); } + /** + * {@inheritDoc} + * <p> + * NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! + * </p> + */ @Override - protected PointerIconImpl createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { - if( PNGIcon.isAvailable() ) { - final int[] width = { 0 }, height = { 0 }, data_size = { 0 }; - if( null != pngResource && 0 < pngResource.resourceCount() ) { - final ByteBuffer data = PNGIcon.singleToRGBAImage(pngResource, 0, true /* toBGRA */, width, height, data_size); - return new PointerIconImpl( this, pngResource, new Dimension(width[0], height[0]), - new Point(hotX, hotY), createPointerIcon0(data, width[0], height[0], hotX, hotY)); - } - } - return null; + public final boolean getNativePointerIconForceDirectNIO() { return true; } + + @Override + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return createPointerIcon0( + pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */, + width, height, hotX, hotY); } @Override @@ -140,8 +142,8 @@ public class DisplayDriver extends DisplayImpl { private static native boolean initNSApplication0(); private static native void runNSApplication0(); private static native void stopNSApplication0(); - /* pp */ static native void setAppIcon0(Object iconData, int iconWidth, int iconHeight); - private static native long createPointerIcon0(Object iconData, int iconWidth, int iconHeight, int hotX, int hotY); + /* pp */ static native void setAppIcon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height); + private static native long createPointerIcon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, int hotX, int hotY); private static native long destroyPointerIcon0(long handle); } diff --git a/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java index 216a0cb20..551929b8d 100644 --- a/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java +++ b/src/newt/classes/jogamp/newt/driver/opengl/JoglUtilPNGIcon.java @@ -32,20 +32,22 @@ import java.net.MalformedURLException; import java.net.URLConnection; import java.nio.ByteBuffer; +import javax.media.nativewindow.util.PixelFormat; + import com.jogamp.common.nio.Buffers; import com.jogamp.common.os.Platform; import com.jogamp.common.util.IOUtil; -import com.jogamp.opengl.util.texture.spi.PNGImage; +import com.jogamp.opengl.util.PNGPixelRect; public class JoglUtilPNGIcon { public static ByteBuffer arrayToX11BGRAImages(IOUtil.ClassResources resources, int[] data_size, int[] elem_bytesize) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { - final PNGImage[] images = new PNGImage[resources.resourceCount()]; + final PNGPixelRect[] images = new PNGPixelRect[resources.resourceCount()]; data_size[0] = 0; for(int i=0; i<resources.resourceCount(); i++) { final URLConnection urlConn = resources.resolve(i); - final PNGImage image = PNGImage.read(urlConn.getInputStream()); - data_size[0] += 2 + image.getWidth() * image.getHeight(); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + data_size[0] += 2 + image.getSize().getWidth() * image.getSize().getHeight(); images[i] = image; } final boolean is64Bit = Platform.is64Bit(); @@ -53,31 +55,26 @@ public class JoglUtilPNGIcon { final ByteBuffer buffer = Buffers.newDirectByteBuffer( data_size[0] * elem_bytesize[0] ); for(int i=0; i<images.length; i++) { - final PNGImage image1 = images[i]; + final PNGPixelRect image1 = images[i]; + final int width = image1.getSize().getWidth(); + final int height = image1.getSize().getHeight(); if( is64Bit ) { - buffer.putLong(image1.getWidth()); - buffer.putLong(image1.getHeight()); + buffer.putLong(width); + buffer.putLong(height); } else { - buffer.putInt(image1.getWidth()); - buffer.putInt(image1.getHeight()); + buffer.putInt(width); + buffer.putInt(height); } - final ByteBuffer bb = image1.getData(); - final int bpp = image1.getBytesPerPixel(); - final int stride = image1.getWidth() * bpp; - for(int y=0; y<image1.getHeight(); y++) { - int bbOff = image1.isGLOriented() ? ( image1.getHeight() - 1 - y ) * stride : y * stride; - for(int x=0; x<image1.getWidth(); x++) { - // Source: R G B A - // Dest: B G R A + final ByteBuffer bb = image1.getPixels(); + final int stride = image1.getStride(); + for(int y=0; y<height; y++) { + int bbOff = y * stride; + for(int x=0; x<width; x++) { long pixel; - pixel = ( 0xffL & bb.get(bbOff++) ) << 16; // R + pixel = ( 0xffL & bb.get(bbOff++) ); // B pixel |= ( 0xffL & bb.get(bbOff++) ) << 8; // G - pixel |= ( 0xffL & bb.get(bbOff++) ); // B - if( 4 == bpp ) { - pixel |= ( 0xffL & bb.get(bbOff++) ) << 24; - } else { - pixel |= 0x00000000ff000000L; - } + pixel |= ( 0xffL & bb.get(bbOff++) ) << 16; // R + pixel |= ( 0xffL & bb.get(bbOff++) ) << 24; // A if( is64Bit ) { buffer.putLong(pixel); } else { @@ -89,51 +86,4 @@ public class JoglUtilPNGIcon { buffer.rewind(); return buffer; } - - public static ByteBuffer singleToRGBAImage(IOUtil.ClassResources resources, int resourceIdx, boolean toBGRA, int[] width, int[] height, int[] data_size) throws UnsupportedOperationException, InterruptedException, IOException, MalformedURLException { - width[0] = 0; - height[0] = 0; - data_size[0] = 0; - final URLConnection urlConn = resources.resolve(resourceIdx); - final PNGImage image = PNGImage.read(urlConn.getInputStream()); - width[0] = image.getWidth(); - height[0] = image.getHeight(); - data_size[0] = image.getWidth() * image.getHeight(); - - final int elem_bytesize = 4; // BGRA - final ByteBuffer buffer = Buffers.newDirectByteBuffer( data_size[0] * elem_bytesize ); - - final ByteBuffer bb = image.getData(); - final int bpp = image.getBytesPerPixel(); - final int stride = image.getWidth() * bpp; - for(int y=0; y<image.getHeight(); y++) { - int bbOff = image.isGLOriented() ? ( image.getHeight() - 1 - y ) * stride : y * stride; - for(int x=0; x<image.getWidth(); x++) { - // Source: R G B A - final byte r, g, b, a; - r = bb.get(bbOff++); // R - g = bb.get(bbOff++); // G - b = bb.get(bbOff++); // B - if( 4 == bpp ) { - a = bb.get(bbOff++); // A - } else { - a = (byte)0xff; // A - } - if( toBGRA ) { - // Dest: B G R A - buffer.put(b); - buffer.put(g); - buffer.put(r); - } else { - // Dest: R G B A - buffer.put(r); - buffer.put(g); - buffer.put(b); - } - buffer.put(a); - } - } - buffer.rewind(); - return buffer; - } } diff --git a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java index a4db50165..a30431788 100644 --- a/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/windows/DisplayDriver.java @@ -34,8 +34,7 @@ package jogamp.newt.driver.windows; -import java.io.IOException; -import java.net.MalformedURLException; +import java.net.URLConnection; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -43,18 +42,16 @@ import jogamp.nativewindow.windows.RegisteredClass; import jogamp.nativewindow.windows.RegisteredClassFactory; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; -import jogamp.newt.PointerIconImpl; -import jogamp.newt.driver.PNGIcon; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PixelFormat; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; import com.jogamp.newt.NewtFactory; +import com.jogamp.opengl.util.PNGPixelRect; public class DisplayDriver extends DisplayImpl { @@ -66,17 +63,18 @@ public class DisplayDriver extends DisplayImpl { NEWTJNILibLoader.loadNEWT(); { long[] _defaultIconHandle = { 0, 0 }; - if( PNGIcon.isAvailable() ) { + if( DisplayImpl.isPNGUtilAvailable() ) { try { - final int[] width = { 0 }, height = { 0 }, data_size = { 0 }; final IOUtil.ClassResources iconRes = NewtFactory.getWindowIcons(); { - final ByteBuffer icon_data_small = PNGIcon.singleToRGBAImage(iconRes, 0, true /* toBGRA */, width, height, data_size); - _defaultIconHandle[0] = DisplayDriver.createBGRA8888Icon0(icon_data_small, width[0], height[0], false, 0, 0); + final URLConnection urlConn = iconRes.resolve(0); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + _defaultIconHandle[0] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); } { - final ByteBuffer icon_data_big = PNGIcon.singleToRGBAImage(iconRes, iconRes.resourceCount()-1, true /* toBGRA */, width, height, data_size); - _defaultIconHandle[1] = DisplayDriver.createBGRA8888Icon0(icon_data_big, width[0], height[0], false, 0, 0); + final URLConnection urlConn = iconRes.resolve(iconRes.resourceCount()-1); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), PixelFormat.BGRA8888, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + _defaultIconHandle[1] = DisplayDriver.createBGRA8888Icon0(image.getPixels(), image.getSize().getWidth(), image.getSize().getHeight(), false, 0, 0); } } catch (Exception e) { e.printStackTrace(); @@ -127,16 +125,8 @@ public class DisplayDriver extends DisplayImpl { } @Override - protected PointerIconImpl createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { - if( PNGIcon.isAvailable() ) { - final int[] width = { 0 }, height = { 0 }, data_size = { 0 }; - if( null != pngResource && 0 < pngResource.resourceCount() ) { - final ByteBuffer data = PNGIcon.singleToRGBAImage(pngResource, 0, true /* toBGRA */, width, height, data_size); - return new PointerIconImpl( this, pngResource, new Dimension(width[0], height[0]), - new Point(hotX, hotY), createBGRA8888Icon0(data, width[0], height[0], true, hotX, hotY)); - } - } - return null; + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return createBGRA8888Icon0(pixels, width, height, true, hotX, hotY); } @Override @@ -149,16 +139,18 @@ public class DisplayDriver extends DisplayImpl { // private static native void DispatchMessages0(); - static long createBGRA8888Icon0(Buffer data, int width, int height, boolean isCursor, int hotX, int hotY) { - if( null == data ) { + static long createBGRA8888Icon0(Buffer pixels, int width, int height, boolean isCursor, int hotX, int hotY) { + if( null == pixels ) { throw new IllegalArgumentException("data buffer/size"); } - if( !Buffers.isDirect(data) ) { - throw new IllegalArgumentException("data buffer is not direct "+data); - } - return createBGRA8888Icon0(data, Buffers.getDirectBufferByteOffset(data), width, height, isCursor, hotX, hotY); + final boolean pixels_is_direct = Buffers.isDirect(pixels); + return createBGRA8888Icon0( + pixels_is_direct ? pixels : Buffers.getArray(pixels), + pixels_is_direct ? Buffers.getDirectBufferByteOffset(pixels) : Buffers.getIndirectBufferByteOffset(pixels), + pixels_is_direct, + width, height, isCursor, hotX, hotY); } - private static native long createBGRA8888Icon0(Object data, int data_offset, int width, int height, boolean isCursor, int hotX, int hotY); + private static native long createBGRA8888Icon0(Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, boolean isCursor, int hotX, int hotY); private static native void destroyIcon0(long handle); } diff --git a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java index 150337df4..4d1388eec 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/DisplayDriver.java @@ -34,25 +34,19 @@ package jogamp.newt.driver.x11; -import java.io.IOException; -import java.net.MalformedURLException; import java.nio.Buffer; import java.nio.ByteBuffer; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowException; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.Point; +import javax.media.nativewindow.util.PixelFormat; import com.jogamp.common.nio.Buffers; -import com.jogamp.common.util.IOUtil; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import jogamp.nativewindow.x11.X11Util; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; -import jogamp.newt.PointerIconImpl; -import jogamp.newt.driver.PNGIcon; public class DisplayDriver extends DisplayImpl { @@ -132,16 +126,11 @@ public class DisplayDriver extends DisplayImpl { protected Boolean isXineramaEnabled() { return isNativeValid() ? Boolean.valueOf(((X11GraphicsDevice)aDevice).isXineramaEnabled()) : null; } @Override - protected PointerIconImpl createPointerIconImpl(final IOUtil.ClassResources pngResource, final int hotX, final int hotY) throws MalformedURLException, InterruptedException, IOException { - if( PNGIcon.isAvailable() ) { - final int[] width = { 0 }, height = { 0 }, data_size = { 0 }; - if( null != pngResource && 0 < pngResource.resourceCount() ) { - final ByteBuffer data = PNGIcon.singleToRGBAImage(pngResource, 0, false /* toBGRA */, width, height, data_size); - final long handle = createPointerIcon0(getHandle(), data, width[0], height[0], hotX, hotY); - return new PointerIconImpl(DisplayDriver.this, pngResource, new Dimension(width[0], height[0]), new Point(hotX, hotY), handle); - } - } - return null; + public final PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.RGBA8888; } + + @Override + protected final long createPointerIconImpl(PixelFormat pixelformat, int width, int height, final ByteBuffer pixels, final int hotX, final int hotY) { + return createPointerIcon(getHandle(), pixels, width, height, hotX, hotY); } @Override @@ -166,13 +155,16 @@ public class DisplayDriver extends DisplayImpl { private native void DispatchMessages0(long display, long javaObjectAtom, long windowDeleteAtom /* , long kbdHandle */); // XKB disabled for now - static long createPointerIcon0(long display, Buffer data, int width, int height, int hotX, int hotY) { - if( !Buffers.isDirect(data) ) { - throw new IllegalArgumentException("data buffer is not direct "+data); - } - return createPointerIcon0(display, data, Buffers.getDirectBufferByteOffset(data), width, height, hotX, hotY); + private static long createPointerIcon(long display, Buffer pixels, int width, int height, int hotX, int hotY) { + final boolean pixels_is_direct = Buffers.isDirect(pixels); + return createPointerIcon0(display, + pixels_is_direct ? pixels : Buffers.getArray(pixels), + pixels_is_direct ? Buffers.getDirectBufferByteOffset(pixels) : Buffers.getIndirectBufferByteOffset(pixels), + pixels_is_direct, + width, height, hotX, hotY); } - private static native long createPointerIcon0(long display, Object data, int data_offset, int width, int height, int hotX, int hotY); + private static native long createPointerIcon0(long display, Object pixels, int pixels_byte_offset, boolean pixels_is_direct, int width, int height, int hotX, int hotY); + static native void destroyPointerIcon0(long display, long handle); /** X11 Window delete atom marker used on EDT */ diff --git a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java index ad1744f2e..0eda37eac 100644 --- a/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java +++ b/src/newt/classes/jogamp/newt/driver/x11/WindowDriver.java @@ -50,6 +50,7 @@ import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; +import com.jogamp.common.nio.Buffers; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; import com.jogamp.newt.NewtFactory; @@ -74,6 +75,7 @@ public class WindowDriver extends WindowImpl { Buffer _icon_data=null; if( PNGIcon.isAvailable() ) { try { + // NOTE: MUST BE DIRECT BUFFER, since _NET_WM_ICON Atom uses buffer directly! final int[] data_size = { 0 }, elem_bytesize = { 0 }; _icon_data = PNGIcon.arrayToX11BGRAImages(NewtFactory.getWindowIcons(), data_size, elem_bytesize); _icon_data_size = data_size[0]; @@ -124,7 +126,7 @@ public class WindowDriver extends WindowImpl { ( FLAG_IS_ALWAYSONTOP | FLAG_IS_UNDECORATED ) ; edtDevice.lock(); try { - setWindowHandle(CreateWindow0(getParentWindowHandle(), + setWindowHandle(CreateWindow(getParentWindowHandle(), edtDevice.getHandle(), screen.getIndex(), visualID, display.getJavaObjectAtom(), display.getWindowDeleteAtom(), getX(), getY(), getWidth(), getHeight(), autoPosition(), flags, @@ -430,10 +432,24 @@ public class WindowDriver extends WindowImpl { protected static native boolean initIDs0(); + private long CreateWindow(long parentWindowHandle, long display, int screen_index, + int visualID, long javaObjectAtom, long windowDeleteAtom, + int x, int y, int width, int height, boolean autoPosition, int flags, + int pixelDataSize, Buffer pixels) { + // NOTE: MUST BE DIRECT BUFFER, since _NET_WM_ICON Atom uses buffer directly! + if( !Buffers.isDirect(pixels) ) { + throw new IllegalArgumentException("data buffer is not direct "+pixels); + } + return CreateWindow0(parentWindowHandle, display, screen_index, + visualID, javaObjectAtom, windowDeleteAtom, + x, y, width, height, autoPosition, flags, + pixelDataSize, + pixels, Buffers.getDirectBufferByteOffset(pixels), true /* pixels_is_direct */); + } private native long CreateWindow0(long parentWindowHandle, long display, int screen_index, int visualID, long javaObjectAtom, long windowDeleteAtom, int x, int y, int width, int height, boolean autoPosition, int flags, - int iconDataSize, Object iconData); + int pixelDataSize, Object pixels, int pixels_byte_offset, boolean pixels_is_direct); private native void CloseWindow0(long display, long windowHandle, long javaObjectAtom, long windowDeleteAtom /*, long kbdHandle*/ ); // XKB disabled for now private native void reconfigureWindow0(long display, int screen_index, long parentWindowHandle, long windowHandle, long windowDeleteAtom, int x, int y, int width, int height, int flags); diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m index 359b67b39..c6fd805a5 100644 --- a/src/newt/native/MacWindow.m +++ b/src/newt/native/MacWindow.m @@ -287,9 +287,8 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_stopNSApplic [pool release]; } -static NSImage * createNSImageFromData(JNIEnv *env, jobject jiconData, jint jiconWidth, jint jiconHeight) { - if( NULL != jiconData ) { - unsigned char * iconData = (unsigned char *) (*env)->GetDirectBufferAddress(env, jiconData); +static NSImage * createNSImageFromData(JNIEnv *env, unsigned char * iconData, jint jiconWidth, jint jiconHeight) { + if( NULL != iconData ) { NSInteger iconWidth = (NSInteger) jiconWidth; NSInteger iconHeight = (NSInteger) jiconHeight; const NSInteger bpc = 8 /* bits per component */, spp=4 /* RGBA */, bpp = bpc * spp; @@ -319,28 +318,47 @@ static NSImage * createNSImageFromData(JNIEnv *env, jobject jiconData, jint jico * Method: setAppIcon0 */ JNIEXPORT void JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_setAppIcon0 - (JNIEnv *env, jobject unused, jobject jiconData, jint jiconWidth, jint jiconHeight) + (JNIEnv *env, jobject unused, jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct, jint width, jint height) { + if( 0 == pixels ) { + return; + } NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSImage * nsImage = createNSImageFromData(env, jiconData, jiconWidth, jiconHeight); + // NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! + unsigned char * pixelPtr = (unsigned char *) ( JNI_TRUE == pixels_is_direct ? + (*env)->GetDirectBufferAddress(env, pixels) : + (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) ); + NSImage * nsImage = createNSImageFromData(env, pixelPtr + pixels_byte_offset, width, height); if( NULL != nsImage ) { [nsImage autorelease]; [NSApp setApplicationIconImage: nsImage]; } + if ( JNI_FALSE == pixels_is_direct ) { + (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT); + } [pool release]; } JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_macosx_DisplayDriver_createPointerIcon0 - (JNIEnv *env, jobject unused, jobject jiconData, jint jiconWidth, jint jiconHeight, jint hotX, jint hotY) + (JNIEnv *env, jobject unused, jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct, jint width, jint height, jint hotX, jint hotY) { + if( 0 == pixels ) { + return 0; + } NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSImage * nsImage = createNSImageFromData(env, jiconData, jiconWidth, jiconHeight); + unsigned char * pixelPtr = (unsigned char *) ( JNI_TRUE == pixels_is_direct ? + (*env)->GetDirectBufferAddress(env, pixels) : + (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) ); + NSImage * nsImage = createNSImageFromData(env, pixelPtr + pixels_byte_offset, width, height); NSCursor * res = NULL; if( NULL != nsImage ) { [nsImage autorelease]; NSPoint hotP = { hotX, hotY }; res = [[NSCursor alloc] initWithImage: nsImage hotSpot: hotP]; } + if ( JNI_FALSE == pixels_is_direct ) { + (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT); + } [pool release]; DBG_PRINT( "createPointerIcon0 %p\n", res); return (jlong) (intptr_t) res; diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c index 1fd8d0039..c20e156c1 100644 --- a/src/newt/native/WindowsWindow.c +++ b/src/newt/native/WindowsWindow.c @@ -2368,9 +2368,15 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_windows_WindowDriver_warpPointer0 JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_windows_DisplayDriver_createBGRA8888Icon0(JNIEnv *env, jobject _unused, - jobject data, jint data_offset, jint width, jint height, jboolean isCursor, jint hotX, jint hotY) { + jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct, jint width, jint height, jboolean isCursor, jint hotX, jint hotY) { - const unsigned char * data_ptr = (const unsigned char *) (*env)->GetDirectBufferAddress(env, data) + data_offset; + if( 0 == pixels ) { + return 0; + } + + const unsigned char * pixelPtr = (const unsigned char *) ( JNI_TRUE == pixels_is_direct ? + (*env)->GetDirectBufferAddress(env, pixels) : + (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) ); const int bytes = 4 * width * height; // BGRA8888 DWORD dwWidth, dwHeight; @@ -2403,10 +2409,14 @@ Java_jogamp_newt_driver_windows_DisplayDriver_createBGRA8888Icon0(JNIEnv *env, j hBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&lpBits, NULL, (DWORD)0); - memcpy(lpBits, data_ptr, bytes); + memcpy(lpBits, pixelPtr + pixels_byte_offset, bytes); ReleaseDC(NULL,hdc); + if ( JNI_FALSE == pixels_is_direct ) { + (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT); + } + // Create an empty mask bitmap. HBITMAP hMonoBitmap = CreateBitmap(dwWidth,dwHeight,1,1,NULL); diff --git a/src/newt/native/X11Display.c b/src/newt/native/X11Display.c index 19e733111..b62a9b234 100644 --- a/src/newt/native/X11Display.c +++ b/src/newt/native/X11Display.c @@ -678,13 +678,15 @@ JNIEXPORT void JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_DispatchMessage * Signature: (JJILjava/lang/Object;I)V */ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_createPointerIcon0 - (JNIEnv *env, jclass clazz, jlong display, jobject data, jint data_offset, jint width, jint height, jint hotX, jint hotY) + (JNIEnv *env, jclass clazz, jlong display, jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct, jint width, jint height, jint hotX, jint hotY) { Cursor c; - if( 0 != data ) { + if( 0 != pixels ) { Display * dpy = (Display *) (intptr_t) display; - char * data_ptr = (char *) (*env)->GetDirectBufferAddress(env, data) + data_offset; + const unsigned char * pixelPtr = (const unsigned char *) ( JNI_TRUE == pixels_is_direct ? + (*env)->GetDirectBufferAddress(env, pixels) : + (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) ); XcursorImage ci; ci.version = 1; // XCURSOR_IMAGE_VERSION; ci.size = width; // nominal size (assume square ..) @@ -693,11 +695,14 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_DisplayDriver_createPointerI ci.xhot = hotX; ci.yhot = hotY; ci.delay = 0; - ci.pixels = (XcursorPixel *)(intptr_t)data_ptr; + ci.pixels = (XcursorPixel *)(intptr_t)(pixelPtr + pixels_byte_offset); c = XcursorImageLoadCursor (dpy, &ci); - DBG_PRINT( "X11: createPointerIcon0: %p %dx%d %d/%d -> %p\n", data_ptr, width, height, hotX, hotY, (void *)c); + if ( JNI_FALSE == pixels_is_direct ) { + (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT); + } + DBG_PRINT( "X11: createPointerIcon0: %p %dx%d %d/%d -> %p\n", (pixelPtr+pixels_byte_offset), width, height, hotX, hotY, (void *)c); } else { c = 0; diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c index da2778004..54b85c243 100644 --- a/src/newt/native/X11Window.c +++ b/src/newt/native/X11Window.c @@ -512,7 +512,7 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_WindowDriver_CreateWindow0 jint visualID, jlong javaObjectAtom, jlong windowDeleteAtom, jint x, jint y, jint width, jint height, jboolean autoPosition, int flags, - jint iconDataSize, jobject iconData) + jint pixelDataSize, jobject pixels, jint pixels_byte_offset, jboolean pixels_is_direct) { Display * dpy = (Display *)(intptr_t)display; Atom wm_delete_atom = (Atom)windowDeleteAtom; @@ -632,10 +632,16 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_WindowDriver_CreateWindow0 { XEvent event; int left=0, right=0, top=0, bottom=0; - - if( 0 < iconDataSize && NULL != iconData ) { - const unsigned char * iconDataPtr = (const unsigned char *) (*env)->GetDirectBufferAddress(env, iconData); - NewtWindows_setIcon(dpy, window, (int)iconDataSize, iconDataPtr); + const unsigned char * pixelPtr = NULL; + + // NOTE: MUST BE DIRECT BUFFER, since _NET_WM_ICON Atom uses buffer directly! + DBG_PRINT("X11: CreateWindow icon: size %d, pixels %p, offset %d, direct %d\n", pixelDataSize, (void*)pixels, pixels_byte_offset, pixels_is_direct); + if( 0 < pixelDataSize && NULL != pixels ) { + pixelPtr = (const unsigned char *) ( JNI_TRUE == pixels_is_direct ? + (*env)->GetDirectBufferAddress(env, pixels) : + (*env)->GetPrimitiveArrayCritical(env, pixels, NULL) ); + DBG_PRINT("X11: CreateWindow icon: NIO %p\n", pixelPtr); + NewtWindows_setIcon(dpy, window, (int)pixelDataSize, pixelPtr+pixels_byte_offset); } XMapWindow(dpy, window); @@ -643,6 +649,10 @@ JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_x11_WindowDriver_CreateWindow0 XSync(dpy, False); + if( JNI_FALSE == pixels_is_direct && NULL != pixelPtr ) { + (*env)->ReleasePrimitiveArrayCritical(env, pixels, (void*)pixelPtr, JNI_ABORT); + } + // send insets before visibility, allowing java code a proper sync point! NewtWindows_updateInsets(env, jwindow, dpy, window, &left, &right, &top, &bottom); (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_FALSE, JNI_TRUE); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java index 8cc676291..780338492 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java @@ -29,6 +29,7 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.newt; import java.io.IOException; +import java.net.URLConnection; import com.jogamp.common.util.IOUtil; import com.jogamp.newt.Display; @@ -47,6 +48,7 @@ import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.test.junit.util.QuitAdapter; import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import javax.media.nativewindow.NativeWindowFactory; @@ -171,25 +173,55 @@ public class TestGearsES2NEWT extends UITestCase { } }); - final PointerIcon pointerIconOne; + final PointerIcon[] pointerIcons = { null, null, null }; { - PointerIcon _pointerIconOne = null; - final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "jogamp-pointer-64x64.png" } ); final Display disp = glWindow.getScreen().getDisplay(); disp.createNative(); - try { - _pointerIconOne = disp.createPointerIcon(res, 32, 0); - } catch (Exception e) { - e.printStackTrace(); + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/crosshair-lumina-trans-32x32.png" } ); + try { + _pointerIcon = disp.createPointerIcon(res, 16, 16); + System.err.println("Create PointerIcon #01: "+_pointerIcon); + } catch (Exception e) { + e.printStackTrace(); + } + pointerIcons[0] = _pointerIcon; + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "jogamp-pointer-64x64.png" } ); + try { + _pointerIcon = disp.createPointerIcon(res, 32, 0); + System.err.println("Create PointerIcon #02: "+_pointerIcon); + } catch (Exception e) { + e.printStackTrace(); + } + pointerIcons[1] = _pointerIcon; + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "crosshair-lumina-trans-64x64.png" } ); + try { + final URLConnection urlConn = res.resolve(0); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), null, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + System.err.println("Create PointerIcon #03: "+image); + _pointerIcon = disp.createPointerIcon(image, 32, 32); + System.err.println("Create PointerIcon #03: "+_pointerIcon); + } catch (Exception e) { + e.printStackTrace(); + } + pointerIcons[2] = _pointerIcon; } - pointerIconOne = _pointerIconOne; } if( setPointerIcon ) { - glWindow.setPointerIcon(pointerIconOne); + glWindow.setPointerIcon(pointerIcons[0]); System.err.println("Set PointerIcon: "+glWindow.getPointerIcon()); } glWindow.addKeyListener(new KeyAdapter() { + int pointerIconIdx = 0; + @Override public void keyPressed(final KeyEvent e) { if( e.isAutoRepeat() ) { @@ -246,7 +278,14 @@ public class TestGearsES2NEWT extends UITestCase { final Thread t = glWindow.setExclusiveContextThread(null); System.err.println("[set pointer-icon pre]"); final PointerIcon currentPI = glWindow.getPointerIcon(); - glWindow.setPointerIcon( currentPI == pointerIconOne ? null : pointerIconOne); + final PointerIcon newPI; + if( pointerIconIdx >= pointerIcons.length ) { + newPI=null; + pointerIconIdx=0; + } else { + newPI=pointerIcons[pointerIconIdx++]; + } + glWindow.setPointerIcon( newPI ); System.err.println("[set pointer-icon post] "+currentPI+" -> "+glWindow.getPointerIcon()); glWindow.setExclusiveContextThread(t); } }.start(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/PNGTstFiles.java b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/PNGTstFiles.java new file mode 100644 index 000000000..bb9bc42a8 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/PNGTstFiles.java @@ -0,0 +1,54 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.util.texture; + +public class PNGTstFiles { + static public final String[] allBasenames = { + "test-ntscN_3-01-160x90", + "test-ntscN_4-01-160x90", + "test-ntscNG4-01-160x90", + "test-ntscI_3-01-160x90", + "test-ntscI_4-01-160x90", + "test-ntscIG3-01-160x90", + "test-ntscIG4-01-160x90", + "test-ntscP_3-01-160x90", + "test-ntscP_4-01-160x90", + "grayscale_texture", + "bug724-transparent-grey_orig", + "bug724-transparent-grey_gimpexp", + "crosshair-lumina-trans-32x32", + "crosshair-lumina-trans-64x64", + }; + static public final String[] greyBasenames = { + "grayscale_texture", + "bug724-transparent-grey_orig", + "bug724-transparent-grey_gimpexp", + "crosshair-lumina-trans-32x32", + "crosshair-lumina-trans-64x64", + }; +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGImage00NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGImage00NEWT.java deleted file mode 100644 index 3ace5ab6a..000000000 --- a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGImage00NEWT.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.opengl.test.junit.jogl.util.texture; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URLConnection; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.FixMethodOrder; -import org.junit.runners.MethodSorters; - -import com.jogamp.common.util.IOUtil; -import com.jogamp.opengl.test.junit.util.UITestCase; -import com.jogamp.opengl.util.texture.spi.PNGImage; - -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestPNGImage00NEWT extends UITestCase { - @Test - public void testPNGReadWriteAndCompare() throws InterruptedException, IOException, MalformedURLException { - final File out1_f=new File(getSimpleTestName(".")+"-PNGImageTest1.png"); - final File out2_f=new File(getSimpleTestName(".")+"-PNGImageTest2.png"); - final File out2F_f=new File(getSimpleTestName(".")+"-PNGImageTest2Flipped.png"); - final File out2R_f=new File(getSimpleTestName(".")+"-PNGImageTest2Reversed.png"); - final File out2RF_f=new File(getSimpleTestName(".")+"-PNGImageTest2ReversedFlipped.png"); - final String url_s="jogl/util/data/av/test-ntsc01-57x32.png"; - URLConnection urlConn = IOUtil.getResource(url_s, this.getClass().getClassLoader()); - PNGImage image1 = PNGImage.read(urlConn.getInputStream()); - System.err.println("PNGImage - Orig: "+image1); - image1.write(out1_f, true); - { - Assert.assertEquals(image1.getData(), PNGImage.read(out1_f.toURI().toURL().openStream()).getData()); - } - - final PNGImage image2 = PNGImage.createFromData(image1.getWidth(), image1.getHeight(), - image1.getDpi()[0], image1.getDpi()[1], - image1.getBytesPerPixel(), false /* reverseChannels */, image1.isGLOriented(), image1.getData()); - image2.write(out2_f, true); - { - Assert.assertEquals(image1.getData(), PNGImage.read(out2_f.toURI().toURL().openStream()).getData()); - } - - // flipped - final PNGImage image2F = PNGImage.createFromData(image1.getWidth(), image1.getHeight(), - image1.getDpi()[0], image1.getDpi()[1], - image1.getBytesPerPixel(), false /* reverseChannels */, !image1.isGLOriented(), image1.getData()); - image2F.write(out2F_f, true); - - // reversed channels - final PNGImage image2R = PNGImage.createFromData(image1.getWidth(), image1.getHeight(), - image1.getDpi()[0], image1.getDpi()[1], - image1.getBytesPerPixel(), true /* reverseChannels */, image1.isGLOriented(), image1.getData()); - image2R.write(out2R_f, true); - - // reversed channels and flipped - final PNGImage image2RF = PNGImage.createFromData(image1.getWidth(), image1.getHeight(), - image1.getDpi()[0], image1.getDpi()[1], - image1.getBytesPerPixel(), true /* reverseChannels */, !image1.isGLOriented(), image1.getData()); - image2RF.write(out2RF_f, true); - } - - public static void main(String args[]) { - org.junit.runner.JUnitCore.main(TestPNGImage00NEWT.class.getName()); - } -} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGPixelRect00NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGPixelRect00NEWT.java new file mode 100644 index 000000000..fc97c56fa --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGPixelRect00NEWT.java @@ -0,0 +1,224 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.util.texture; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URLConnection; + +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelFormatUtil; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.PNGPixelRect; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestPNGPixelRect00NEWT extends UITestCase { + @Test + public void testPNGRead01_All() throws InterruptedException, IOException, MalformedURLException { + for(int i=0; i<PNGTstFiles.allBasenames.length; i++) { + final String basename = PNGTstFiles.allBasenames[i]; + final String pathname=""; + testPNG01Impl(pathname, basename, null, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + } + + @Test + public void testPNGRead02_RGB888_to_RGBA8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_3-01-160x90"; + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.RGBA8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + @Test + public void testPNGRead03_RGB888_to_RGBA8888_stride1000() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_3-01-160x90"; // 640 bytes = 4 * 160 + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.RGBA8888, 1000 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + @Test + public void testPNGRead04_RGB888_to_RGBA8888_stride999() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_3-01-160x90"; // 640 bytes = 4 * 160 + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.RGBA8888, 999 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + @Test + public void testPNGRead11_RGBA8888_to_LUMINA() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG02Impl(pathname, basename, PixelFormat.LUMINANCE, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + @Test + public void testPNGRead12_RGBA8888_to_RGB888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG02Impl(pathname, basename, PixelFormat.RGB888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + @Test + public void testPNGRead13_RGBA8888_to_BGR888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG02Impl(pathname, basename, PixelFormat.BGR888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + @Test + public void testPNGRead14_RGBA8888_to_BGRA8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG02Impl(pathname, basename, PixelFormat.BGRA8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testPNGRead15_RGBA8888_to_ARGB8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG02Impl(pathname, basename, PixelFormat.ARGB8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testPNGRead16_RGBA8888_to_ABGR8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG02Impl(pathname, basename, PixelFormat.ABGR8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + private void testPNG01Impl(final String pathname, final String basename, + final PixelFormat destFmt, final int destMinStrideInBytes, final boolean destIsGLOriented) + throws InterruptedException, IOException, MalformedURLException + { + System.err.println("Test01: "+pathname+basename+".png, destFmt "+destFmt+", destMinStrideInBytes "+destMinStrideInBytes+", destIsGLOriented "+destIsGLOriented); + + final File out1_f=new File(getSimpleTestName(".")+"-01-"+basename+"-orig.png"); + final File out2F_f=new File(getSimpleTestName(".")+"-02-"+basename+"-flipped.png"); + final File out2R_f=new File(getSimpleTestName(".")+"-03-"+basename+"-reversed.png"); + final File out2RF_f=new File(getSimpleTestName(".")+"-04-"+basename+"-reversed_flipped.png"); + URLConnection urlConn = IOUtil.getResource(this.getClass(), pathname+basename+".png"); + + final PNGPixelRect image1 = PNGPixelRect.read(urlConn.getInputStream(), destFmt, false /* directBuffer */, destMinStrideInBytes, destIsGLOriented); + System.err.println("PNGPixelRect - Orig: "+image1); + { + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out1_f, true /* allowOverwrite */)); + image1.write(outs, true /* close */); + { + final PNGPixelRect image1_R = PNGPixelRect.read(out1_f.toURI().toURL().openStream(), image1.getPixelformat(), false /* directBuffer */, destMinStrideInBytes, destIsGLOriented); + System.err.println("PNGPixelRect - Orig (Read Back): "+image1_R); + Assert.assertEquals(image1.getPixels(), image1_R.getPixels()); + } + } + + // + // Flipped Orientation + // + { + final PNGPixelRect image2F = new PNGPixelRect(image1.getPixelformat(), image1.getSize(), + image1.getStride(), !image1.isGLOriented(), image1.getPixels(), + image1.getDpi()[0], image1.getDpi()[1]); + System.err.println("PNGPixelRect - Flip : "+image2F); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out2F_f, true /* allowOverwrite */)); + image2F.write(outs, true /* close */); + { + // flip again .. to compare w/ original + final PNGPixelRect image2F_R = PNGPixelRect.read(out2F_f.toURI().toURL().openStream(), image1.getPixelformat(), false /* directBuffer */, destMinStrideInBytes, !destIsGLOriented); + System.err.println("PNGPixelRect - Flip (Read Back): "+image2F_R); + Assert.assertEquals(image1.getPixels(), image2F_R.getPixels()); + } + } + + // + // Reversed Components + // + final PixelFormat revFmt = PixelFormatUtil.getReversed(image1.getPixelformat()); + { + final PNGPixelRect image2R = new PNGPixelRect(revFmt, image1.getSize(), + image1.getStride(), image1.isGLOriented(), image1.getPixels(), + image1.getDpi()[0], image1.getDpi()[1]); + System.err.println("PNGPixelRect - Reversed : "+image2R); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out2R_f, true /* allowOverwrite */)); + image2R.write(outs, true /* close */); + { + // reverse again .. to compare w/ original + final PNGPixelRect image2R_R = PNGPixelRect.read(out2R_f.toURI().toURL().openStream(), revFmt, false /* directBuffer */, destMinStrideInBytes, destIsGLOriented); + System.err.println("PNGPixelRect - Reversed (Read Back): "+image2R_R); + Assert.assertEquals(image1.getPixels(), image2R_R.getPixels()); + } + } + + // reversed channels and flipped + { + final PNGPixelRect image2RF = new PNGPixelRect(revFmt, image1.getSize(), + image1.getStride(), !image1.isGLOriented(), image1.getPixels(), + image1.getDpi()[0], image1.getDpi()[1]); + System.err.println("PNGPixelRect - Reversed+Flipped : "+image2RF); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out2RF_f, true /* allowOverwrite */)); + image2RF.write(outs, true /* close */); + { + // reverse+flip again .. to compare w/ original + final PNGPixelRect image2RF_R = PNGPixelRect.read(out2RF_f.toURI().toURL().openStream(), revFmt, false /* directBuffer */, destMinStrideInBytes, !destIsGLOriented); + System.err.println("PNGPixelRect - Reversed+FLipped (Read Back): "+image2RF_R); + Assert.assertEquals(image1.getPixels(), image2RF_R.getPixels()); + } + } + } + + private void testPNG02Impl(final String pathname, final String basename, + final PixelFormat destFmt, final int destMinStrideInBytes, final boolean destIsGLOriented) + throws InterruptedException, IOException, MalformedURLException + { + System.err.println("Test02: "+pathname+basename+".png, destFmt "+destFmt+", destMinStrideInBytes "+destMinStrideInBytes+", destIsGLOriented "+destIsGLOriented); + + final File out1_f=new File(getSimpleTestName(".")+"-"+basename+"-orig.png"); + URLConnection urlConn = IOUtil.getResource(this.getClass(), pathname+basename+".png"); + + final PNGPixelRect image1 = PNGPixelRect.read(urlConn.getInputStream(), destFmt, false /* directBuffer */, destMinStrideInBytes, destIsGLOriented); + System.err.println("PNGPixelRect - Orig: "+image1); + { + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out1_f, true /* allowOverwrite */)); + image1.write(outs, true /* close */); + { + final PNGPixelRect image1_R = PNGPixelRect.read(out1_f.toURI().toURL().openStream(), image1.getPixelformat(), false /* directBuffer */, destMinStrideInBytes, destIsGLOriented); + System.err.println("PNGPixelRect - Orig (Read Back): "+image1_R); + Assert.assertEquals(image1.getPixels(), image1_R.getPixels()); + } + } + } + + public static void main(String args[]) { + org.junit.runner.JUnitCore.main(TestPNGPixelRect00NEWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGImage01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGPixelRect01NEWT.java index 29e041908..0e92db48e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGImage01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPNGPixelRect01NEWT.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -32,6 +32,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URLConnection; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; @@ -52,10 +53,10 @@ import com.jogamp.opengl.test.junit.util.QuitAdapter; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLReadBufferUtil; +import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.texture.TextureData; import com.jogamp.opengl.util.texture.TextureIO; -import com.jogamp.opengl.util.texture.spi.PNGImage; /** * Test reading and displaying a PNG image. @@ -64,71 +65,71 @@ import com.jogamp.opengl.util.texture.spi.PNGImage; * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestPNGImage01NEWT extends UITestCase { - +public class TestPNGPixelRect01NEWT extends UITestCase { static boolean showFPS = false; static long duration = 200; // ms - - public void testImpl(final InputStream istream) throws InterruptedException, IOException { - final PNGImage image = PNGImage.read(istream); + + public void testImpl(final int num, final String basename, final InputStream istream, final PixelFormat destFmt) throws InterruptedException, IOException { + final GLProfile glp = GLProfile.getGL2ES2(); + final PNGPixelRect image = PNGPixelRect.read(istream, destFmt, true /* directBuffer */, 0 /* destMinStrideInBytes */, true /* destIsGLOriented */); Assert.assertNotNull(image); - final boolean hasAlpha = 4 == image.getBytesPerPixel(); - System.err.println("PNGImage: "+image); - + final GLPixelAttributes glpa = GLPixelAttributes.convert(image.getPixelformat(), glp); + final boolean hasAlpha = 4 == glpa.bytesPerPixel; + System.err.println("PNGPixelRect: "+basename+", "+image+", glpa "+glpa); + final GLReadBufferUtil screenshot = new GLReadBufferUtil(true, false); - final GLProfile glp = GLProfile.getGL2ES2(); final GLCapabilities caps = new GLCapabilities(glp); if( hasAlpha ) { caps.setAlphaBits(1); } - + final int internalFormat; if(glp.isGL2ES3()) { internalFormat = hasAlpha ? GL.GL_RGBA8 : GL.GL_RGB8; } else { internalFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB; - } + } final TextureData texData = new TextureData(glp, internalFormat, - image.getWidth(), - image.getHeight(), + image.getSize().getWidth(), + image.getSize().getHeight(), 0, - new GLPixelAttributes(image.getGLFormat(), image.getGLType()), + glpa, false /* mipmap */, false /* compressed */, false /* must flip-vert */, - image.getData(), + image.getPixels(), null); - + // final TextureData texData = TextureIO.newTextureData(glp, istream, false /* mipmap */, TextureIO.PNG); - System.err.println("TextureData: "+texData); - + System.err.println("TextureData: "+texData); + final GLWindow glad = GLWindow.create(caps); - glad.setTitle("TestPNGImage01NEWT"); + glad.setTitle(this.getSimpleTestName(".")); // Size OpenGL to Video Surface glad.setSize(texData.getWidth(), texData.getHeight()); - + // load texture from file inside current GL context to match the way // the bug submitter was doing it final TextureDraw01ES2Listener gle = new TextureDraw01ES2Listener( texData, 0 ) ; // gle.setClearColor(new float[] { 1.0f, 0.0f, 0.0f, 1.0f } ); glad.addGLEventListener(gle); - glad.addGLEventListener(new GLEventListener() { + glad.addGLEventListener(new GLEventListener() { boolean shot = false; - + @Override public void init(GLAutoDrawable drawable) { System.err.println("Chosen Caps: " + drawable.getChosenGLCapabilities()); System.err.println("GL ctx: " + drawable.getGL().getContext()); } - + @Override public void display(GLAutoDrawable drawable) { // 1 snapshot if(null!=((TextureDraw01Accessor)gle).getTexture() && !shot) { shot = true; - snapshot(0, null, drawable.getGL(), screenshot, TextureIO.PNG, null); + snapshot(num, basename, drawable.getGL(), screenshot, TextureIO.PNG, null); } } - + @Override public void dispose(GLAutoDrawable drawable) { } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } }); @@ -144,28 +145,43 @@ public class TestPNGImage01NEWT extends UITestCase { while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) { Thread.sleep(100); } - + animator.stop(); glad.destroy(); } - + @Test - public void testRead01_RGBn_exp() throws InterruptedException, IOException, MalformedURLException { - final String fname = null == _fname ? "bug724-transparent-grey_gimpexp.png" : _fname; - final URLConnection urlConn = IOUtil.getResource(this.getClass(), fname); - testImpl(urlConn.getInputStream()); + public void testRead00_Manual() throws InterruptedException, IOException, MalformedURLException { + if( null == _fname ) { + return; + } + final URLConnection urlConn = IOUtil.getResource(this.getClass(), _fname); + testImpl(0, _fname, urlConn.getInputStream(), null); } @Test - public void testRead02_RGBn_orig() throws InterruptedException, IOException, MalformedURLException { + public void testRead01_All() throws InterruptedException, IOException, MalformedURLException { + if( null != _fname ) { + return; + } + for(int i=0; i<PNGTstFiles.allBasenames.length; i++) { + final String basename = PNGTstFiles.allBasenames[i]; + final URLConnection urlConn = IOUtil.getResource(this.getClass(), basename+".png"); + testImpl(i, basename, urlConn.getInputStream(), null); + } + } + @Test + public void testRead02_Gray2RGBA() throws InterruptedException, IOException, MalformedURLException { if( null != _fname ) { return; } - final String fname = "bug724-transparent-grey_orig.png"; - final URLConnection urlConn = IOUtil.getResource(this.getClass(), fname); - testImpl(urlConn.getInputStream()); + for(int i=0; i<PNGTstFiles.greyBasenames.length; i++) { + final String basename = PNGTstFiles.greyBasenames[i]; + final URLConnection urlConn = IOUtil.getResource(this.getClass(), basename+".png"); + testImpl(i, basename, urlConn.getInputStream(), PixelFormat.RGBA8888); + } } - + static String _fname = null; public static void main(String args[]) { for(int i=0; i<args.length; i++) { @@ -177,6 +193,6 @@ public class TestPNGImage01NEWT extends UITestCase { _fname = args[i]; } } - org.junit.runner.JUnitCore.main(TestPNGImage01NEWT.class.getName()); + org.junit.runner.JUnitCore.main(TestPNGPixelRect01NEWT.class.getName()); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPixelFormatUtil01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPixelFormatUtil01NEWT.java new file mode 100644 index 000000000..f54861371 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPixelFormatUtil01NEWT.java @@ -0,0 +1,105 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.util.texture; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URLConnection; + +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelFormatUtil; +import javax.media.nativewindow.util.PixelRectangle; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.PNGPixelRect; + +/** + * Testing PixelFormatUtil's Conversion using PNG test data + * including strides, endian-order and PixelFormat conversions: + * { PixelFormat.RGBA8888, PixelFormat.ABGR8888, PixelFormat.BGRA8888, PixelFormat.ARGB8888 } + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestPixelFormatUtil01NEWT extends UITestCase { + @Test + public void testPNGRead11_fromRGBA8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.RGBA8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testPNGRead12_fromABGR8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.ABGR8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testPNGRead13_fromBGRA8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.BGRA8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testPNGRead14_fromARGB8888() throws InterruptedException, IOException, MalformedURLException { + final String basename ="test-ntscN_4-01-160x90"; + final String pathname=""; + testPNG01Impl(pathname, basename, PixelFormat.ARGB8888, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + private void testPNG01Impl(final String pathname, final String basename, final PixelFormat srcFmt, + final int destMinStrideInBytes, final boolean destIsGLOriented) + throws InterruptedException, IOException, MalformedURLException + { + System.err.println("Test01: "+pathname+basename+".png, srcFmt "+srcFmt+", destMinStrideInBytes "+destMinStrideInBytes+", destIsGLOriented "+destIsGLOriented); + + URLConnection urlConn = IOUtil.getResource(this.getClass(), pathname+basename+".png"); + + final PNGPixelRect image1 = PNGPixelRect.read(urlConn.getInputStream(), srcFmt, false /* directBuffer */, destMinStrideInBytes, false /* isGLOriented */); + System.err.println("PNGPixelRect - Orig: "+image1); + + final PixelFormat[] formats = new PixelFormat[] { PixelFormat.RGBA8888, PixelFormat.ABGR8888, PixelFormat.BGRA8888, PixelFormat.ARGB8888 }; + for(int i=0; i<formats.length; i++) { + final PixelFormat destFmt = formats[i]; + System.err.println("CONVERT["+i+"]: "+srcFmt+" -> "+destFmt); + final PixelRectangle imageConv1 = PixelFormatUtil.convert32(image1, destFmt, destMinStrideInBytes, destIsGLOriented, false /* nio */); + System.err.println("PNGPixelRect - Conv1: "+imageConv1); + final PixelRectangle imageConv2 = PixelFormatUtil.convert32(imageConv1, image1.getPixelformat(), image1.getStride(), image1.isGLOriented(), false /* nio */); + System.err.println("PNGPixelRect - Conv2: "+imageConv2); + Assert.assertEquals(image1.getPixels(), imageConv2.getPixels()); + } + } + + public static void main(String args[]) { + org.junit.runner.JUnitCore.main(TestPixelFormatUtil01NEWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/crosshair-lumina-trans-32x32.png b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/crosshair-lumina-trans-32x32.png Binary files differnew file mode 100644 index 000000000..92279640b --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/crosshair-lumina-trans-32x32.png diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/crosshair-lumina-trans-64x64.png b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/crosshair-lumina-trans-64x64.png Binary files differnew file mode 100644 index 000000000..9be285335 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/crosshair-lumina-trans-64x64.png diff --git a/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java b/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java index 69874df6b..70d01ae66 100644 --- a/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java +++ b/src/test/com/jogamp/opengl/test/junit/newt/parenting/NewtAWTReparentingKeyAdapter.java @@ -28,25 +28,27 @@ package com.jogamp.opengl.test.junit.newt.parenting; import java.awt.Frame; +import java.net.URLConnection; import javax.media.nativewindow.util.InsetsImmutable; import com.jogamp.common.util.IOUtil; import com.jogamp.newt.Display; -import com.jogamp.newt.Window; import com.jogamp.newt.Display.PointerIcon; import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.util.QuitAdapter; +import com.jogamp.opengl.util.PNGPixelRect; public class NewtAWTReparentingKeyAdapter extends KeyAdapter { final Frame frame; final NewtCanvasAWT newtCanvasAWT; final GLWindow glWindow; final QuitAdapter quitAdapter; - PointerIcon pointerIconTest = null; + PointerIcon[] pointerIcons = null; + int pointerIconIdx = 0; public NewtAWTReparentingKeyAdapter(Frame frame, NewtCanvasAWT newtCanvasAWT, GLWindow glWindow, QuitAdapter quitAdapter) { this.frame = frame; @@ -144,13 +146,46 @@ public class NewtAWTReparentingKeyAdapter extends KeyAdapter { } } }.start(); } else if(e.getKeySymbol() == KeyEvent.VK_C ) { - if( null == pointerIconTest ) { - final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/jogamp-32x32.png" } ); - final Display disp = glWindow.getScreen().getDisplay(); - try { - pointerIconTest = disp.createPointerIcon(res, 16, 0); - } catch (Exception err) { - err.printStackTrace(); + if( null == pointerIcons ) { + { + pointerIcons = new PointerIcon[3]; + final Display disp = glWindow.getScreen().getDisplay(); + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "newt/data/crosshair-lumina-trans-32x32.png" } ); + try { + _pointerIcon = disp.createPointerIcon(res, 16, 16); + System.err.println("Create PointerIcon #01: "+_pointerIcon); + } catch (Exception ex) { + ex.printStackTrace(); + } + pointerIcons[0] = _pointerIcon; + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "jogamp-pointer-64x64.png" } ); + try { + _pointerIcon = disp.createPointerIcon(res, 32, 0); + System.err.println("Create PointerIcon #02: "+_pointerIcon); + } catch (Exception ex) { + ex.printStackTrace(); + } + pointerIcons[1] = _pointerIcon; + } + { + PointerIcon _pointerIcon = null; + final IOUtil.ClassResources res = new IOUtil.ClassResources(glWindow.getClass(), new String[] { "crosshair-lumina-trans-64x64.png" } ); + try { + final URLConnection urlConn = res.resolve(0); + final PNGPixelRect image = PNGPixelRect.read(urlConn.getInputStream(), null, false /* directBuffer */, 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + System.err.println("Create PointerIcon #03: "+image); + _pointerIcon = disp.createPointerIcon(image, 32, 32); + System.err.println("Create PointerIcon #03: "+_pointerIcon); + } catch (Exception ex) { + ex.printStackTrace(); + } + pointerIcons[2] = _pointerIcon; + } } } new Thread() { @@ -158,7 +193,14 @@ public class NewtAWTReparentingKeyAdapter extends KeyAdapter { final Thread t = glWindow.setExclusiveContextThread(null); System.err.println("[set pointer-icon pre]"); final PointerIcon currentPI = glWindow.getPointerIcon(); - glWindow.setPointerIcon( currentPI == pointerIconTest ? null : pointerIconTest); + final PointerIcon newPI; + if( pointerIconIdx >= pointerIcons.length ) { + newPI=null; + pointerIconIdx=0; + } else { + newPI=pointerIcons[pointerIconIdx++]; + } + glWindow.setPointerIcon( newPI ); System.err.println("[set pointer-icon post] "+currentPI+" -> "+glWindow.getPointerIcon()); glWindow.setExclusiveContextThread(t); } }.start(); |