diff options
Diffstat (limited to 'src/nativewindow/classes/javax/media')
4 files changed, 767 insertions, 6 deletions
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/javax/media/nativewindow/util/PixelFormat.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormat.java new file mode 100644 index 000000000..823496a92 --- /dev/null +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormat.java @@ -0,0 +1,196 @@ +/** + * Copyright (c) 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 javax.media.nativewindow.util; + +/** + * Basic pixel formats + * <p> + * Notation follows OpenGL notation, i.e. + * name consist of all it's component names + * followed by their bit size. + * </p> + * <p> + * Order of component names is from lowest-bit to highest-bit. + * </p> + * <p> + * In case component-size is 1 byte (e.g. OpenGL data-type GL_UNSIGNED_BYTE), + * component names are ordered from lowest-byte to highest-byte. + * Note that OpenGL applies special interpretation if + * data-type is e.g. GL_UNSIGNED_8_8_8_8_REV or GL_UNSIGNED_8_8_8_8_REV. + * </p> + * <p> + * PixelFormat can be converted to OpenGL GLPixelAttributes + * via + * <pre> + * GLPixelAttributes glpa = GLPixelAttributes.convert(PixelFormat pixFmt, GLProfile glp); + * </pre> + * </p> + * <p> + * See OpenGL Specification 4.3 - February 14, 2013, Core Profile, + * Section 8.4.4 Transfer of Pixel Rectangles, p. 161-174. + * </ul> + * + * </p> + */ +public enum PixelFormat { + /** + * Pixel size is 1 bytes (8 bits) with one component of size 1 byte (8 bits). + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_ALPHA (< GL3), GL_RED (>= GL3), data-type GL_UNSIGNED_BYTE</li> + * <li>AWT: <i>none</i></li> + * </ul> + * </p> + */ + LUMINANCE(1, 8), + + /** + * Pixel size is 3 bytes (24 bits) with each component of size 1 byte (8 bits). + * <p> + * The components are interleaved in the order: + * <ul> + * <li>Low to High: R, G, B</li> + * </ul> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_RGB, data-type GL_UNSIGNED_BYTE</li> + * <li>AWT: <i>None</i></li> + * </ul> + * </p> + */ + RGB888(3, 24), + + /** + * Pixel size is 3 bytes (24 bits) with each component of size 1 byte (8 bits). + * <p> + * The components are interleaved in the order: + * <ul> + * <li>Low to High: B, G, R</li> + * </ul> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_BGR (>= GL2), data-type GL_UNSIGNED_BYTE</li> + * <li>AWT: {@link java.awt.image.BufferedImage#TYPE_3BYTE_BGR TYPE_3BYTE_BGR}</li> + * </ul> + * </p> + */ + BGR888(3, 24), + + /** + * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * <p> + * The components are interleaved in the order: + * <ul> + * <li>Low to High: R, G, B, A</li> + * </ul> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_BYTE</li> + * <li>AWT: <i>None</i></li> + * <li>PointerIcon: X11 (XCURSOR)</li> + * <li>PNGJ: Scanlines</li> + * </ul> + * </p> + */ + RGBA8888(4, 32), + + /** + * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * <p> + * The components are interleaved in the order: + * <ul> + * <li>Low to High: A, B, G, R</li> + * </ul> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_RGBA, data-type GL_UNSIGNED_8_8_8_8</li> + * <li>AWT: {@link java.awt.image.BufferedImage#TYPE_4BYTE_ABGR TYPE_4BYTE_ABGR}</li> + * </ul> + * </p> + */ + ABGR8888(4, 32), + + /** + * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * <p> + * The components are interleaved in the order: + * <ul> + * <li>Low to High: A, R, G, B</li> + * </ul> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_BGRA, data-type GL_UNSIGNED_INT_8_8_8_8</li> + * <li>AWT: <i>None</i></li> + * </ul> + * </p> + */ + ARGB8888(4, 32), + + /** + * Pixel size is 4 bytes (32 bits) with each component of size 1 byte (8 bits). + * <p> + * The components are interleaved in the order: + * <ul> + * <li>Low to High: B, G, R, A</li> + * </ul> + * </p> + * <p> + * Compatible with: + * <ul> + * <li>OpenGL: data-format GL_BGRA, data-type GL_UNSIGNED_BYTE</li> + * <li>AWT: {@link java.awt.image.BufferedImage#TYPE_INT_ARGB TYPE_INT_ARGB}</li> + * <li>PointerIcon: Win32, OSX (NSBitmapImageRep), AWT</li> + * <li>Window Icon: X11, Win32, OSX (NSBitmapImageRep)</li> + * </ul> + * </p> + */ + BGRA8888(4, 32); + + /** Number of components per pixel, e.g. 4 for RGBA. */ + public final int componentCount; + /** Number of bits per pixel, e.g. 32 for RGBA. */ + public final int bitsPerPixel; + /** Number of bytes per pixel, e.g. 4 for RGBA. */ + public final int bytesPerPixel() { return (7+bitsPerPixel)/8; } + + private PixelFormat(int componentCount, int bpp) { + this.componentCount = componentCount; + this.bitsPerPixel = bpp; + } +} diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java new file mode 100644 index 000000000..361d03446 --- /dev/null +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java @@ -0,0 +1,374 @@ +/** + * Copyright (c) 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 javax.media.nativewindow.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.jogamp.common.nio.Buffers; + +/** + * Pixel Rectangle Utilities. + * <p> + * All conversion methods are endian independent. + * </p> + */ +public class PixelFormatUtil { + public static interface PixelSink { + /** Return the sink's destination pixelformat. */ + PixelFormat getPixelformat(); + + /** + * Returns stride in byte-size, i.e. byte count from one line to the next. + * <p> + * Must be >= {@link #getPixelformat()}.{@link PixelFormat#bytesPerPixel() bytesPerPixel()} * {@link #getSize()}.{@link DimensionImmutable#getWidth() getWidth()}. + * </p> + */ + int getStride(); + + /** + * Returns <code>true</code> if the sink's memory is laid out in + * OpenGL's coordinate system, <i>origin at bottom left</i>. + * Otherwise returns <code>false</code>, i.e. <i>origin at top left</i>. + */ + boolean isGLOriented(); + } + /** + * Pixel sink for up-to 32bit. + */ + public static interface PixelSink32 extends PixelSink { + /** + * Will be invoked over all rows top-to down + * and all columns left-to-right. + * <p> + * Shall consider dest pixelformat and only store as much components + * as defined, up to 32bit. + * </p> + * <p> + * Implementation may better write single bytes from low-to-high bits, + * e.g. {@link ByteOrder#LITTLE_ENDIAN} order. + * Otherwise a possible endian conversion must be taken into consideration. + * </p> + * @param x + * @param y + * @param pixel + */ + void store(int x, int y, int pixel); + } + + /** + * Returns the {@link PixelFormat} with reversed components of <code>fmt</code>. + * If no reversed {@link PixelFormat} is available, returns <code>fmt</code>. + */ + public static PixelFormat getReversed(PixelFormat fmt) { + switch(fmt) { + case LUMINANCE: + return PixelFormat.LUMINANCE; + case RGB888: + return PixelFormat.BGR888; + case BGR888: + return PixelFormat.RGB888; + case RGBA8888: + return PixelFormat.ABGR8888; + case ABGR8888: + return PixelFormat.RGBA8888; + case ARGB8888: + return PixelFormat.BGRA8888; + case BGRA8888: + return PixelFormat.ABGR8888; + default: + throw new InternalError("Unhandled format "+fmt); + } + } + + public static int getValue32(PixelFormat src_fmt, ByteBuffer src, int srcOff) { + switch(src_fmt) { + case LUMINANCE: { + final byte c1 = src.get(srcOff++); + return ( 0xff ) << 24 | ( 0xff & c1 ) << 16 | ( 0xff & c1 ) << 8 | ( 0xff & c1 ); + } + case RGB888: + case BGR888: { + final byte c1 = src.get(srcOff++); + final byte c2 = src.get(srcOff++); + final byte c3 = src.get(srcOff++); + return ( 0xff ) << 24 | ( 0xff & c3 ) << 16 | ( 0xff & c2 ) << 8 | ( 0xff & c1 ); + } + case RGBA8888: + case ABGR8888: + case ARGB8888: + case BGRA8888: { + final byte c1 = src.get(srcOff++); + final byte c2 = src.get(srcOff++); + final byte c3 = src.get(srcOff++); + final byte c4 = src.get(srcOff++); + return ( 0xff & c4 ) << 24 | ( 0xff & c3 ) << 16 | ( 0xff & c2 ) << 8 | ( 0xff & c1 ); + } + default: + throw new InternalError("Unhandled format "+src_fmt); + } + } + + public static int convertToInt32(PixelFormat dest_fmt, final byte r, final byte g, final byte b, final byte a) { + switch(dest_fmt) { + case LUMINANCE: { + final byte l = ( byte) ( ( ( ( 0xff & r ) + ( 0xff & g ) + ( 0xff & b ) ) / 3 ) ); + return ( 0xff ) << 24 | ( 0xff & l ) << 16 | ( 0xff & l ) << 8 | ( 0xff & l ); + } + case RGB888: + return ( 0xff ) << 24 | ( 0xff & b ) << 16 | ( 0xff & g ) << 8 | ( 0xff & r ); + case BGR888: + return ( 0xff ) << 24 | ( 0xff & r ) << 16 | ( 0xff & g ) << 8 | ( 0xff & b ); + case RGBA8888: + return ( 0xff & a ) << 24 | ( 0xff & b ) << 16 | ( 0xff & g ) << 8 | ( 0xff & r ); + case ABGR8888: + return ( 0xff & r ) << 24 | ( 0xff & g ) << 16 | ( 0xff & b ) << 8 | ( 0xff & a ); + case ARGB8888: + return ( 0xff & b ) << 24 | ( 0xff & g ) << 16 | ( 0xff & r ) << 8 | ( 0xff & a ); + case BGRA8888: + return ( 0xff & a ) << 24 | ( 0xff & r ) << 16 | ( 0xff & g ) << 8 | ( 0xff & b ); + default: + throw new InternalError("Unhandled format "+dest_fmt); + } + } + + public static int convertToInt32(PixelFormat dest_fmt, PixelFormat src_fmt, ByteBuffer src, int srcOff) { + final byte r, g, b, a; + switch(src_fmt) { + case LUMINANCE: + r = src.get(srcOff++); // R + g = r; // G + b = r; // B + a = (byte) 0xff; // A + break; + case RGB888: + r = src.get(srcOff++); // R + g = src.get(srcOff++); // G + b = src.get(srcOff++); // B + a = (byte) 0xff; // A + break; + case BGR888: + b = src.get(srcOff++); // B + g = src.get(srcOff++); // G + r = src.get(srcOff++); // R + a = (byte) 0xff; // A + break; + case RGBA8888: + r = src.get(srcOff++); // R + g = src.get(srcOff++); // G + b = src.get(srcOff++); // B + a = src.get(srcOff++); // A + break; + case ABGR8888: + a = src.get(srcOff++); // A + b = src.get(srcOff++); // B + g = src.get(srcOff++); // G + r = src.get(srcOff++); // R + break; + case ARGB8888: + a = src.get(srcOff++); // A + r = src.get(srcOff++); // R + g = src.get(srcOff++); // G + b = src.get(srcOff++); // B + break; + case BGRA8888: + b = src.get(srcOff++); // B + g = src.get(srcOff++); // G + r = src.get(srcOff++); // R + a = src.get(srcOff++); // A + break; + default: + throw new InternalError("Unhandled format "+src_fmt); + } + return convertToInt32(dest_fmt, r, g, b, a); + } + + /** + public static int convertToInt32(PixelFormat dest_fmt, PixelFormat src_fmt, final int src_pixel) { + final byte r, g, b, a; + switch(src_fmt) { + case LUMINANCE: + r = (byte) ( src_pixel ); // R + g = r; // G + b = r; // B + a = (byte) 0xff; // A + break; + case RGB888: + r = (byte) ( src_pixel ); // R + g = (byte) ( src_pixel >>> 8 ); // G + b = (byte) ( src_pixel >>> 16 ); // B + a = (byte) 0xff; // A + break; + case BGR888: + b = (byte) ( src_pixel ); // B + g = (byte) ( src_pixel >>> 8 ); // G + r = (byte) ( src_pixel >>> 16 ); // R + a = (byte) 0xff; // A + break; + case RGBA8888: + r = (byte) ( src_pixel ); // R + g = (byte) ( src_pixel >>> 8 ); // G + b = (byte) ( src_pixel >>> 16 ); // B + a = (byte) ( src_pixel >>> 24 ); // A + break; + case ABGR8888: + a = (byte) ( src_pixel ); // A + b = (byte) ( src_pixel >>> 8 ); // B + g = (byte) ( src_pixel >>> 16 ); // G + r = (byte) ( src_pixel >>> 24 ); // R + break; + case ARGB8888: + a = (byte) ( src_pixel ); // A + r = (byte) ( src_pixel >>> 8 ); // R + g = (byte) ( src_pixel >>> 16 ); // G + b = (byte) ( src_pixel >>> 24 ); // B + break; + case BGRA8888: + b = (byte) ( src_pixel ); // B + g = (byte) ( src_pixel >>> 8 ); // G + r = (byte) ( src_pixel >>> 16 ); // R + a = (byte) ( src_pixel >>> 24 ); // A + break; + default: + throw new InternalError("Unhandled format "+src_fmt); + } + return convertToInt32(dest_fmt, r, g, b, a); + } */ + + public static PixelRectangle convert32(final PixelRectangle src, + final PixelFormat destFmt, int ddestStride, final boolean isGLOriented, + final boolean destIsDirect) { + final int width = src.getSize().getWidth(); + final int height = src.getSize().getHeight(); + final int bpp = destFmt.bytesPerPixel(); + final int destStride; + if( 0 != ddestStride ) { + destStride = ddestStride; + if( destStride < bpp * width ) { + throw new IllegalArgumentException("Invalid stride "+destStride+", must be greater than bytesPerPixel "+bpp+" * width "+width); + } + } else { + destStride = bpp * width; + } + final int capacity = destStride*height; + final ByteBuffer bb = destIsDirect ? Buffers.newDirectByteBuffer(capacity) : ByteBuffer.allocate(capacity).order(src.getPixels().order()); + + // System.err.println("XXX: SOURCE "+src); + // System.err.println("XXX: DEST fmt "+destFmt+", stride "+destStride+" ("+ddestStride+"), isGL "+isGLOriented+", "+width+"x"+height+", capacity "+capacity+", "+bb); + + final PixelFormatUtil.PixelSink32 imgSink = new PixelFormatUtil.PixelSink32() { + public void store(int x, int y, int pixel) { + int o = destStride*y+x*bpp; + bb.put(o++, (byte) ( pixel )); // 1 + if( 3 <= bpp ) { + bb.put(o++, (byte) ( pixel >>> 8 )); // 2 + bb.put(o++, (byte) ( pixel >>> 16 )); // 3 + if( 4 <= bpp ) { + bb.put(o++, (byte) ( pixel >>> 24 )); // 4 + } + } + } + @Override + public final PixelFormat getPixelformat() { + return destFmt; + } + @Override + public final int getStride() { + return destStride; + } + @Override + public final boolean isGLOriented() { + return isGLOriented; + } + }; + convert32(imgSink, src); + return new PixelRectangle.GenericPixelRect(destFmt, src.getSize(), destStride, isGLOriented, bb); + } + + public static void convert32(PixelSink32 destInt32, final PixelRectangle src) { + convert32(destInt32, + src.getPixels(), src.getPixelformat(), + src.isGLOriented(), + src.getSize().getWidth(), src.getSize().getHeight(), + src.getStride()); + } + + /** + * + * @param dest32 32bit pixel sink + * @param src_bb + * @param src_fmt + * @param src_glOriented if true, the source memory is laid out in OpenGL's coordinate system, <i>origin at bottom left</i>, + * otherwise <i>origin at top left</i>. + * @param width + * @param height + * @param strideInBytes stride in byte-size, i.e. byte count from one line to the next. + * If zero, stride is set to <code>width * bytes-per-pixel</code>. + * If not zero, value must be >= <code>width * bytes-per-pixel</code>. + * @param stride_bytes stride in byte-size, i.e. byte count from one line to the next. + * Must be >= {@link PixelFormat#bytesPerPixel() src_fmt.bytesPerPixel()} * width. + * @throws IllegalArgumentException if <code>strideInBytes</code> is invalid + */ + public static void convert32(PixelSink32 dest32, + final ByteBuffer src_bb, final PixelFormat src_fmt, final boolean src_glOriented, final int width, final int height, int stride_bytes) { + final int src_bpp = src_fmt.bytesPerPixel(); + if( 0 != stride_bytes ) { + if( stride_bytes < src_bpp * width ) { + throw new IllegalArgumentException("Invalid stride "+stride_bytes+", must be greater than bytesPerPixel "+src_bpp+" * width "+width); + } + } else { + stride_bytes = src_bpp * width; + } + final PixelFormat dest_fmt = dest32.getPixelformat(); + final boolean vert_flip = src_glOriented != dest32.isGLOriented(); + final boolean fast_copy = src_fmt == dest_fmt && dest_fmt.bytesPerPixel() == 4 ; + // System.err.println("XXX: SRC fmt "+src_fmt+", stride "+stride_bytes+", isGL "+src_glOriented+", "+width+"x"+height); + // System.err.println("XXX: DST fmt "+dest_fmt+", fast_copy "+fast_copy); + + if( fast_copy ) { + // Fast copy + for(int y=0; y<height; y++) { + int o = vert_flip ? ( height - 1 - y ) * stride_bytes : y * stride_bytes; + for(int x=0; x<width; x++) { + dest32.store(x, y, getValue32(src_fmt, src_bb, o)); + o += src_bpp; + } + } + } else { + // Conversion + for(int y=0; y<height; y++) { + int o = vert_flip ? ( height - 1 - y ) * stride_bytes : y * stride_bytes; + for(int x=0; x<width; x++) { + dest32.store( x, y, convertToInt32( dest_fmt, src_fmt, src_bb, o)); + o += src_bpp; + } + } + } + } +} + diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java new file mode 100644 index 000000000..493c712bb --- /dev/null +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelRectangle.java @@ -0,0 +1,193 @@ +/** + * Copyright (c) 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 javax.media.nativewindow.util; + +import java.nio.ByteBuffer; + +/** + * Pixel Rectangle identified by it's {@link #hashCode()}. + * <p> + * The {@link #getPixels()} are assumed to be immutable. + * </p> + */ +public interface PixelRectangle { + /** + * <p> + * Computes a hash code over: + * <ul> + * <li>pixelformat</li> + * <li>size</li> + * <li>stride</li> + * <li>isGLOriented</li> + * <li>pixels</li> + * </ul> + * </p> + * <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(); + + /** Returns the {@link PixelFormat}. */ + PixelFormat getPixelformat(); + + /** Returns the size, i.e. width and height. */ + DimensionImmutable getSize(); + + /** + * Returns stride in byte-size, i.e. byte count from one line to the next. + * <p> + * Must be >= {@link #getPixelformat()}.{@link PixelFormat#bytesPerPixel() bytesPerPixel()} * {@link #getSize()}.{@link DimensionImmutable#getWidth() getWidth()}. + * </p> + */ + int getStride(); + + /** + * Returns <code>true</code> if the memory is laid out in + * OpenGL's coordinate system, <i>origin at bottom left</i>. + * Otherwise returns <code>false</code>, i.e. <i>origin at top left</i>. + */ + public boolean isGLOriented(); + + /** Returns the pixels. */ + ByteBuffer getPixels(); + + @Override + String toString(); + + /** + * Generic PixelRectangle implementation + */ + public static class GenericPixelRect implements PixelRectangle { + protected final PixelFormat pixelformat; + protected final DimensionImmutable size; + protected final int strideInBytes; + protected final boolean isGLOriented; + protected final ByteBuffer pixels; + private int hashCode = 0; + private volatile boolean hashCodeComputed = false; + + /** + * + * @param pixelformat + * @param size + * @param strideInBytes stride in byte-size, i.e. byte count from one line to the next. + * If not zero, value must be >= <code>width * bytes-per-pixel</code>. + * If zero, stride is set to <code>width * bytes-per-pixel</code>. + * @param isGLOriented + * @param pixels + * @throws IllegalArgumentException if <code>strideInBytes</code> is invalid. + * @throws IndexOutOfBoundsException if <code>pixels</code> has insufficient bytes left + */ + public GenericPixelRect(final PixelFormat pixelformat, final DimensionImmutable size, int strideInBytes, final boolean isGLOriented, final ByteBuffer pixels) + throws IllegalArgumentException, IndexOutOfBoundsException + { + if( 0 != strideInBytes ) { + if( strideInBytes < pixelformat.bytesPerPixel() * size.getWidth()) { + throw new IllegalArgumentException("Invalid stride "+strideInBytes+", must be greater than bytesPerPixel "+pixelformat.bytesPerPixel()+" * width "+size.getWidth()); + } + } else { + strideInBytes = pixelformat.bytesPerPixel() * size.getWidth(); + } + final int reqBytes = strideInBytes * size.getHeight(); + if( pixels.limit() < reqBytes ) { + throw new IndexOutOfBoundsException("Dest buffer has insufficient bytes left, needs "+reqBytes+": "+pixels); + } + this.pixelformat = pixelformat; + this.size = size; + this.strideInBytes = strideInBytes; + this.isGLOriented = isGLOriented; + this.pixels = pixels; + } + + /** + * Copy ctor validating src. + * @param src + * @throws IllegalArgumentException if <code>strideInBytes</code> is invalid. + * @throws IndexOutOfBoundsException if <code>pixels</code> has insufficient bytes left + */ + public GenericPixelRect(final PixelRectangle src) + throws IllegalArgumentException, IndexOutOfBoundsException + { + this(src.getPixelformat(), src.getSize(), src.getStride(), src.isGLOriented(), src.getPixels()); + } + + @Override + public int hashCode() { + if( !hashCodeComputed ) { // DBL CHECKED OK VOLATILE + synchronized (this) { + if( !hashCodeComputed ) { + // 31 * x == (x << 5) - x + int hash = 31 + pixelformat.hashCode(); + hash = ((hash << 5) - hash) + size.hashCode(); + hash = ((hash << 5) - hash) + strideInBytes; + hash = ((hash << 5) - hash) + ( isGLOriented ? 1 : 0); + hashCode = ((hash << 5) - hash) + pixels.hashCode(); + } + } + } + return hashCode; + } + + @Override + public PixelFormat getPixelformat() { + return pixelformat; + } + + @Override + public DimensionImmutable getSize() { + return size; + } + + @Override + public int getStride() { + return strideInBytes; + } + + @Override + public boolean isGLOriented() { + return isGLOriented; + } + + @Override + public ByteBuffer getPixels() { + return pixels; + } + + @Override + public final String toString() { + return "PixelRect[obj 0x"+Integer.toHexString(super.hashCode())+", "+pixelformat+", "+size+", stride "+strideInBytes+", isGLOrient "+isGLOriented+", pixels "+pixels+"]"; + } + } +} + |