diff options
5 files changed, 1227 insertions, 0 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java index 50124c349..a921a2818 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java @@ -30,11 +30,15 @@ package com.jogamp.opengl.util; import java.nio.Buffer; import java.nio.ByteBuffer; +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.GL2ES3; +import javax.media.opengl.GL2GL3; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.texture.TextureData; @@ -202,6 +206,56 @@ public class GLPixelBuffer { public GLPixelAttributes(int componentCount, int dataFormat, int dataType) { this(componentCount, dataFormat, dataType, true); } + + /** + * Returns the matching {@link GLPixelAttributes} for the given {@link PixelFormat} and {@link GLProfile} if exists, + * otherwise returns <code>null</code>. + */ + public static final GLPixelAttributes convert(PixelFormat pixFmt, GLProfile glp) { + int df = 0; // format + int dt = GL.GL_UNSIGNED_BYTE; // data type + switch(pixFmt) { + case LUMINANCE: + if( glp.isGL3ES3() ) { + // RED is supported on ES3 and >= GL3 [core]; ALPHA/LUMINANCE is deprecated on core + df = GL2ES2.GL_RED; + } else { + // ALPHA/LUMINANCE is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility + df = GL2ES2.GL_LUMINANCE; + } + break; + case BGR888: + if( glp.isGL2GL3() ) { + df = GL2GL3.GL_BGR; + } + break; + case RGB888: + df = GL.GL_RGB; + break; + case RGBA8888: + df = GL.GL_RGBA; + break; + case ABGR8888: + if( glp.isGL2GL3() ) { + df = GL.GL_RGBA; dt = GL2GL3.GL_UNSIGNED_INT_8_8_8_8; + } + break; + case BGRA8888: + df = GL.GL_BGRA; + break; + case ARGB8888: + if( glp.isGL2GL3() ) { + df = GL.GL_BGRA; dt = GL2GL3.GL_UNSIGNED_INT_8_8_8_8; + } + break; + default: + break; + } + if( 0 != df ) { + return new GLPixelAttributes(pixFmt.componentCount, df, dt, true); + } + return null; + } private GLPixelAttributes(int componentCount, int dataFormat, int dataType, boolean checkArgs) { this.componentCount = componentCount; this.format = dataFormat; @@ -216,6 +270,52 @@ public class GLPixelBuffer { } } } + + /** + * Returns the matching {@link PixelFormat} of this {@link GLPixelAttributes} if exists, + * otherwise returns <code>null</code>. + */ + public final PixelFormat getPixelFormat() { + final PixelFormat pixFmt; + // FIXME: Take 'type' into consideration and complete mapping! + switch(format) { + case GL.GL_ALPHA: + case GL.GL_LUMINANCE: + case GL2ES2.GL_RED: + pixFmt = PixelFormat.LUMINANCE; + break; + case GL.GL_RGB: + pixFmt = PixelFormat.RGB888; + break; + case GL.GL_RGBA: + pixFmt = PixelFormat.RGBA8888; + break; + case GL2.GL_BGR: + pixFmt = PixelFormat.BGR888; + break; + case GL.GL_BGRA: + pixFmt = PixelFormat.BGRA8888; + break; + default: + switch( bytesPerPixel ) { + case 1: + pixFmt = PixelFormat.LUMINANCE; + break; + case 3: + pixFmt = PixelFormat.RGB888; + break; + case 4: + pixFmt = PixelFormat.RGBA8888; + break; + default: + pixFmt = null; + break; + } + break; + } + return pixFmt; + } + @Override public String toString() { return "PixelAttributes[comp "+componentCount+", fmt 0x"+Integer.toHexString(format)+", type 0x"+Integer.toHexString(type)+", bytesPerPixel "+bytesPerPixel+"]"; 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+"]"; + } + } +} + diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPixelFormatUtil00NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPixelFormatUtil00NEWT.java new file mode 100644 index 000000000..bfcf9c2d7 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/TestPixelFormatUtil00NEWT.java @@ -0,0 +1,364 @@ +/** + * 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.nio.ByteBuffer; +import java.nio.ByteOrder; + +import javax.media.nativewindow.util.Dimension; +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.opengl.test.junit.util.UITestCase; + +/** + * Testing PixelFormatUtil's Conversion using synthetic test data + * including strides, endian-order and all PixelFormat conversions. + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestPixelFormatUtil00NEWT extends UITestCase { + @Test + public void testConversion01_srcS000_BE_TL_destS000_TL() throws InterruptedException, IOException, MalformedURLException { + testPNG00Impl(0 /* srcMinStrideInBytes */, ByteOrder.BIG_ENDIAN, false /* srcIsGLOriented */, + 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testConversion02_srcS000_LE_TL_destS000_TL() throws InterruptedException, IOException, MalformedURLException { + testPNG00Impl(0 /* srcMinStrideInBytes */, ByteOrder.LITTLE_ENDIAN, false /* srcIsGLOriented */, + 0 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testConversion03_srcS000_BE_TL_destS259_TL() throws InterruptedException, IOException, MalformedURLException { + testPNG00Impl(0 /* srcMinStrideInBytes */, ByteOrder.BIG_ENDIAN, false /* srcIsGLOriented */, + 259 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testConversion04_srcS259_BE_TL_destS259_TL() throws InterruptedException, IOException, MalformedURLException { + testPNG00Impl(259 /* srcMinStrideInBytes */, ByteOrder.BIG_ENDIAN, false /* srcIsGLOriented */, + 259 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + @Test + public void testConversion05_srcS301_BE_TL_destS259_TL() throws InterruptedException, IOException, MalformedURLException { + testPNG00Impl(301 /* srcMinStrideInBytes */, ByteOrder.BIG_ENDIAN, false /* srcIsGLOriented */, + 259 /* destMinStrideInBytes */, false /* destIsGLOriented */); + } + + static final byte red___val = (byte)0x01; + static final byte green_val = (byte)0x02; + static final byte blue__val = (byte)0x03; + static final byte alpha_val = (byte)0x04; + static final byte undef_val = (byte)0xff; + + static final void getComponents(final int srcComps, final PixelFormat fmt, final byte[] components) { + final byte b1, b2, b3, b4; + if( 1 == srcComps ) { + // LUM -> Fmt Conversion + switch(fmt) { + case LUMINANCE: + b1 = red___val; + b2 = undef_val; + b3 = undef_val; + b4 = undef_val; + break; + case RGB888: + b1 = red___val; + b2 = red___val; + b3 = red___val; + b4 = undef_val; + break; + case BGR888: + b1 = red___val; + b2 = red___val; + b3 = red___val; + b4 = undef_val; + break; + case RGBA8888: + b1 = red___val; + b2 = red___val; + b3 = red___val; + b4 = undef_val; + break; + case ABGR8888: + b1 = undef_val; + b2 = red___val; + b3 = red___val; + b4 = red___val; + break; + case BGRA8888: + b1 = red___val; + b2 = red___val; + b3 = red___val; + b4 = undef_val; + break; + case ARGB8888: + b1 = undef_val; + b2 = red___val; + b3 = red___val; + b4 = red___val; + break; + default: + throw new InternalError("Unhandled format "+fmt); + } + } else { + // 1:1 values + switch(fmt) { + case LUMINANCE: + if( srcComps > 1 ) { + b1 = ( red___val + green_val+ blue__val ) / 3; + b2 = undef_val; + b3 = undef_val; + b4 = undef_val; + } else { + b1 = red___val; + b2 = undef_val; + b3 = undef_val; + b4 = undef_val; + } + break; + case RGB888: + b1 = red___val; + b2 = green_val; + b3 = blue__val; + b4 = undef_val; + break; + case BGR888: + b1 = blue__val; + b2 = green_val; + b3 = red___val; + b4 = undef_val; + break; + case RGBA8888: + b1 = red___val; + b2 = green_val; + b3 = blue__val; + b4 = srcComps > 3 ? alpha_val : undef_val; + break; + case ABGR8888: + b1 = srcComps > 3 ? alpha_val : undef_val; + b2 = blue__val; + b3 = green_val; + b4 = red___val; + break; + case BGRA8888: + b1 = blue__val; + b2 = green_val; + b3 = red___val; + b4 = srcComps > 3 ? alpha_val : undef_val; + break; + case ARGB8888: + b1 = srcComps > 3 ? alpha_val : undef_val; + b2 = red___val; + b3 = green_val; + b4 = blue__val; + break; + default: + throw new InternalError("Unhandled format "+fmt); + } + } + components[0] = b1; + components[1] = b2; + components[2] = b3; + components[3] = b4; + } + private void testPNG00Impl(final int srcMinStrideInBytes, final ByteOrder srcByteOrder, final boolean srcIsGLOriented, + final int destMinStrideInBytes, final boolean destIsGLOriented) + throws InterruptedException, IOException, MalformedURLException + { + System.err.println("Test00: srcMinStrideInBytes "+srcMinStrideInBytes+", srcByteOrder "+srcByteOrder+", srcIsGLOriented "+srcIsGLOriented+ + ", destMinStrideInBytes "+destMinStrideInBytes+", destIsGLOriented "+destIsGLOriented); + + final PixelFormat[] formats = PixelFormat.values(); + final int width = 64, height = 64; + + for(int i=0; i<formats.length; i++) { + final PixelFormat srcFmt = formats[i]; + final int srcBpp = srcFmt.bytesPerPixel(); + final int srcStrideBytes = Math.max(srcMinStrideInBytes, width*srcBpp); + final ByteBuffer srcPixels = ByteBuffer.allocate(height*srcStrideBytes).order(srcByteOrder); + final byte[] srcComponents = new byte[4]; + getComponents(srcFmt.componentCount, srcFmt, srcComponents); + for(int y=0; y<height; y++) { + int o = y*srcStrideBytes; + for(int x=0; x<width; x++) { + switch(srcFmt) { + case LUMINANCE: + srcPixels.put(o++, srcComponents[0]); + break; + case RGB888: + case BGR888: + srcPixels.put(o++, srcComponents[0]); + srcPixels.put(o++, srcComponents[1]); + srcPixels.put(o++, srcComponents[2]); + break; + case RGBA8888: + case ABGR8888: + case BGRA8888: + case ARGB8888: + srcPixels.put(o++, srcComponents[0]); + srcPixels.put(o++, srcComponents[1]); + srcPixels.put(o++, srcComponents[2]); + srcPixels.put(o++, srcComponents[3]); + break; + default: + throw new InternalError("Unhandled format "+srcFmt); + } + } + } + final PixelRectangle imageSrc = new PixelRectangle.GenericPixelRect(srcFmt, new Dimension(width, height), srcStrideBytes, srcIsGLOriented, srcPixels); + System.err.println("CONVERT["+i+"][*]: Image0 - Orig: "+imageSrc); + testComponents(imageSrc, 0, 0, srcComponents); + testComponents(imageSrc, width-1, height-1, srcComponents); + + for(int j=0; j<formats.length; j++) { + final PixelFormat destFmt = formats[j]; + System.err.println("CONVERT["+i+"]["+j+"]: "+srcFmt+" -> "+destFmt); + + final int destStrideBytes = Math.max(destMinStrideInBytes, width*destFmt.bytesPerPixel()); + final byte[] destComponents = new byte[4]; + getComponents(srcFmt.componentCount, destFmt, destComponents); + final PixelRectangle imageConv1 = PixelFormatUtil.convert32(imageSrc, destFmt, destStrideBytes, destIsGLOriented, false /* nio */); + System.err.println("CONVERT["+i+"]["+j+"]: Conv1: "+imageConv1); + testComponents(imageConv1, 0, 0, destComponents); + testComponents(imageConv1, width-1, height-1, destComponents); + if( PixelFormat.LUMINANCE != srcFmt && PixelFormat.LUMINANCE == destFmt ) { + // Cannot convert: RGB* -> LUM -> RGB* + System.err.println("CONVERT["+i+"]["+j+"]: Conv2: Dropped due to RGB* -> LUM"); + } else if( srcFmt.componentCount > destFmt.componentCount ) { + // Cannot convert back if: src.componentCount > dest.componentCount + System.err.println("CONVERT["+i+"]["+j+"]: Conv2: Dropped due to src.componentCount > dest.componentCount"); + } else { + final PixelRectangle imageConv2 = PixelFormatUtil.convert32(imageConv1, imageSrc.getPixelformat(), imageSrc.getStride(), imageSrc.isGLOriented(), false /* nio */); + System.err.println("CONVERT["+i+"]["+j+"]: Conv2: "+imageConv2); + testComponents(imageConv2, 0, 0, srcComponents); + testComponents(imageConv2, width-1, height-1, srcComponents); + if( imageSrc.getStride() == imageConv1.getStride() ) { + Assert.assertEquals(imageSrc.getPixels(), imageConv2.getPixels()); + } + } + } + } + } + private void dumpComponents(PixelRectangle image, int x1, int y1, int w, int h) { + if( x1 + w >= image.getSize().getWidth() ) { + x1 = image.getSize().getWidth() - w; + } + if( y1 + h >= image.getSize().getHeight() ) { + y1 = image.getSize().getHeight() - h; + } + System.err.print("PixelsBytes "+x1+"/"+y1+" "+w+"x"+h+":"); + final ByteBuffer bb = image.getPixels(); + final int bpp = image.getPixelformat().bytesPerPixel(); + for(int y = y1; y< y1+h; y++) { + System.err.printf("%n[%3d][%3d] ", x1, y); + int o = y * image.getStride()+x1*bpp; + for(int x = x1; x< x1+w; x++) { + switch(bpp) { + case 1: { + final byte a = bb.get(o++); + System.err.printf(" 0x%02X", a); + } + break; + case 2: { + final byte a = bb.get(o++), b = bb.get(o++); + System.err.printf(" 0x%02X%02X", b, a); + } + break; + case 3: { + final byte a = bb.get(o++), b = bb.get(o++), c = bb.get(o++); + System.err.printf(" 0x%02X%02X%02X", c, b, a); + } + break; + case 4: { + final byte a = bb.get(o++), b = bb.get(o++), c = bb.get(o++), d = bb.get(o++); + System.err.printf(" 0x%02X%02X%02X%02X", d, c, b, a); + } + break; + } + } + } + System.err.println(); + } + private void testComponents(PixelRectangle image, int x, int y, byte[] components) { + dumpComponents(image, x, y, 3, 3); + final ByteBuffer bb = image.getPixels(); + final int bpp = image.getPixelformat().bytesPerPixel(); + int o = y * image.getStride()+x*bpp; + switch(bpp) { + case 1: { + final byte c1 = bb.get(o++); + final boolean equal = c1==components[0]; + System.err.printf("Test [%3d][%3d] exp 0x%02X == has 0x%02X : %b%n", + x, y, components[0], c1, equal ); + Assert.assertEquals(components[0], c1); + } + break; + case 2: { + final byte c1 = bb.get(o++), c2 = bb.get(o++); + final boolean equal = c1==components[0] && c2==components[1]; + System.err.printf("Test [%3d][%3d] exp 0x%02X%02X == has 0x%02X%02X : %b%n", + x, components[1], components[0], c2, c1, equal ); + Assert.assertEquals(components[0], c1); + Assert.assertEquals(components[1], c2); + } + break; + case 3: { + final byte c1 = bb.get(o++), c2 = bb.get(o++), c3 = bb.get(o++); + final boolean equal = c1==components[0] && c2==components[1] && c3==components[2]; + System.err.printf("Test [%3d][%3d] exp 0x%02X%02X%02X == has 0x%02X%02X%02X : %b%n", + x, y, components[2], components[1], components[0], c3, c2, c1, equal ); + Assert.assertEquals(components[0], c1); + Assert.assertEquals(components[1], c2); + Assert.assertEquals(components[2], c3); + } + break; + case 4: { + final byte c1 = bb.get(o++), c2 = bb.get(o++), c3 = bb.get(o++), c4 = bb.get(o++); + final boolean equal = c1==components[0] && c2==components[1] && c3==components[2] && c4==components[3]; + System.err.printf("Test [%3d][%3d] exp 0x%02X%02X%02X%02X == has 0x%02X%02X%02X%02X : %b%n", + x, y, components[3], components[2], components[1], components[0], c4, c3, c2, c1, equal ); + Assert.assertEquals(components[0], c1); + Assert.assertEquals(components[1], c2); + Assert.assertEquals(components[2], c3); + Assert.assertEquals(components[3], c4); + } + break; + } + } + + public static void main(String args[]) { + org.junit.runner.JUnitCore.main(TestPixelFormatUtil00NEWT.class.getName()); + } +} |