From 21b68a5e73a672da34b37b5641b779aa1e3fa41d Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 13 Feb 2014 00:17:09 +0100 Subject: TextureIO: Support PNGTextureWriter w/ TextureData IntBuffer (via PNGPixelRect and PixelFormatUtil) TextureData IntBuffer could be caused by AWT read-pixels but is not seamlessly supported via PNGPixelRect since the latter uses a hardcoded ByteBuffer. Add static PNGPixelRect.write(..) supporting IntBuffer to support this case for now. PNGPixelRect instances do not support any Buffer type to avoid a bloated implementation. PixelFormatUtil adds support for int32 pixel format conversion. --- .../com/jogamp/opengl/util/PNGPixelRect.java | 80 ++++++++++++++++++++-- .../com/jogamp/opengl/util/texture/TextureIO.java | 40 ++++++++--- .../media/nativewindow/util/PixelFormatUtil.java | 3 +- 3 files changed, 103 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java index 1bbc12f32..fd8f54152 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java +++ b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import javax.media.nativewindow.util.Dimension; import javax.media.nativewindow.util.DimensionImmutable; @@ -248,19 +249,29 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { } return dOff; } - private int setPixelRGBA8(final ImageLine line, final int lineOff, final ByteBuffer d, final int dOff, final int bytesPerPixel, final boolean hasAlpha) { + private int setPixelRGBA8(final ImageLine line, final int lineOff, final ByteBuffer src, final int srcOff, 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); + if( src.limit() <= srcOff + b ) { + throw new IndexOutOfBoundsException("Buffer has unsufficient bytes left, needs ["+srcOff+".."+(srcOff+b)+"]: "+src); } - final int p = PixelFormatUtil.convertToInt32(hasAlpha ? PixelFormat.RGBA8888 : PixelFormat.RGB888, pixelformat, d, dOff); + final int p = PixelFormatUtil.convertToInt32(hasAlpha ? PixelFormat.RGBA8888 : PixelFormat.RGB888, pixelformat, src, srcOff); + 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 srcOff + pixelformat.bytesPerPixel(); + } + + private static void setPixelRGBA8(final PixelFormat pixelformat, final ImageLine line, final int lineOff, final int srcPix, final int bytesPerPixel, final boolean hasAlpha) { + final int p = PixelFormatUtil.convertToInt32(hasAlpha ? PixelFormat.RGBA8888 : PixelFormat.RGB888, pixelformat, srcPix); 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(); } /** @@ -304,9 +315,8 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { 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().setTimeNow(0); // 0 seconds from 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); @@ -332,4 +342,60 @@ public class PNGPixelRect extends PixelRectangle.GenericPixelRect { } } } + + public static void write(final PixelFormat pixelformat, final DimensionImmutable size, + int strideInPixels, final boolean isGLOriented, final IntBuffer pixels, + final double dpiX, final double dpiY, + 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 */); + if( 0 != strideInPixels ) { + if( strideInPixels < size.getWidth()) { + throw new IllegalArgumentException("Invalid stride "+bytesPerPixel+", must be greater than width "+size.getWidth()); + } + } else { + strideInPixels = size.getWidth(); + } + final int reqPixels = strideInPixels * size.getHeight(); + if( pixels.limit() < reqPixels ) { + throw new IndexOutOfBoundsException("Dest buffer has insufficient pixels left, needs "+reqPixels+": "+pixels); + } + + // open image for writing to a output stream + try { + final PngWriter png = new PngWriter(outstream, imi); + // add some optional metadata (chunks) + png.getMetadata().setDpi(dpiX, dpiY); + png.getMetadata().setTimeNow(0); // 0 seconds from now = now + png.getMetadata().setText(PngChunkTextVar.KEY_Title, "JogAmp PNGPixelRect"); + final boolean hasAlpha = 4 == bytesPerPixel; + + final ImageLine l1 = new ImageLine(imi); + for (int row = 0; row < height; row++) { + int dataOff = isGLOriented ? ( height - 1 - row ) * strideInPixels : row * strideInPixels; + 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--) { + setPixelRGBA8(pixelformat, l1, lineOff, pixels.get(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 0cde24db4..c29bd9af2 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java @@ -50,11 +50,13 @@ import java.io.OutputStream; import java.net.URL; import java.nio.Buffer; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2; @@ -1400,18 +1402,34 @@ public class TextureIO { 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(); - if (null == buf) { - buf = (ByteBuffer) data.getMipmapData()[0]; + Buffer buf0 = data.getBuffer(); + if (null == buf0) { + buf0 = data.getMipmapData()[0]; + } + if( null == buf0 ) { + throw new IOException("Pixel storage buffer is null"); + } + final DimensionImmutable size = new Dimension(data.getWidth(), data.getHeight()); + if( buf0 instanceof ByteBuffer ) { + final ByteBuffer buf = (ByteBuffer) buf0; + buf.rewind(); + final PNGPixelRect image = new PNGPixelRect(pixFmt, size, + 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; + } else if( buf0 instanceof IntBuffer ) { + final IntBuffer buf = (IntBuffer) buf0; + buf.rewind(); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(file, true /* allowOverwrite */)); + PNGPixelRect.write(pixFmt, size, + 0 /* stride */, true /* isGLOriented */, buf /* pixels */, + -1f, -1f, outs, true /* closeOutstream */); + return true; + } else { + throw new IOException("PNG writer doesn't support pixel storage buffer of type "+buf0.getClass().getName()); } - buf.rewind(); - - 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)+ " / type 0x"+Integer.toHexString(pixelFormat)+" (only GL_RGB/A, GL_BGR/A + bytes)"); diff --git a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java index 361d03446..87a9ca4fc 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java +++ b/src/nativewindow/classes/javax/media/nativewindow/util/PixelFormatUtil.java @@ -208,7 +208,6 @@ public class PixelFormatUtil { 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) { @@ -258,7 +257,7 @@ public class PixelFormatUtil { 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, -- cgit v1.2.3