summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/com/jogamp
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-04-07 15:31:06 +0200
committerSven Gothel <[email protected]>2012-04-07 15:31:06 +0200
commit073c9744fa4a8982850a0f8d61275f8782497bbb (patch)
tree19257f42cfdf488fec38a3698e95233dfd0c35f0 /src/jogl/classes/com/jogamp
parent40830196070013432bc5f453eb31cfe4c64e0510 (diff)
TextureIO: Add PNG TextureProvider and TextureWriter for RGB[A]/BGR[A] - incl. unit tests; Test/Demos: Use PNG snapshots.
Diffstat (limited to 'src/jogl/classes/com/jogamp')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java97
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java3
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java2
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java172
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java5
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java4
6 files changed, 277 insertions, 6 deletions
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 89d840ac7..16e031f05 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java
@@ -64,6 +64,7 @@ import jogamp.opengl.Debug;
import com.jogamp.common.util.IOUtil;
import com.jogamp.opengl.util.texture.spi.DDSImage;
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;
@@ -775,6 +776,7 @@ public class TextureIO {
}
// Other special-case providers
+ addTextureProvider(new PNGTextureProvider());
addTextureProvider(new DDSTextureProvider());
addTextureProvider(new SGITextureProvider());
addTextureProvider(new TGATextureProvider());
@@ -798,6 +800,7 @@ public class TextureIO {
}
// Other special-case writers
+ addTextureWriter(new PNGTextureWriter());
addTextureWriter(new DDSTextureWriter());
addTextureWriter(new SGITextureWriter());
addTextureWriter(new TGATextureWriter());
@@ -1102,7 +1105,44 @@ public class TextureIO {
pixelFormat = image.getGLFormat();
}
if (internalFormat == 0) {
- if(glp.isGL2()) {
+ if(glp.isGL2GL3()) {
+ internalFormat = GL.GL_RGBA8;
+ } else {
+ internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB;
+ }
+ }
+ return new TextureData(glp, internalFormat,
+ image.getWidth(),
+ image.getHeight(),
+ 0,
+ pixelFormat,
+ GL.GL_UNSIGNED_BYTE,
+ mipmap,
+ false,
+ false,
+ image.getData(),
+ null);
+ }
+
+ return null;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // PNG image provider
+ static class PNGTextureProvider extends StreamBasedTextureProvider {
+ public TextureData newTextureData(GLProfile glp, InputStream stream,
+ int internalFormat,
+ int pixelFormat,
+ 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) {
+ if(glp.isGL2GL3()) {
internalFormat = GL.GL_RGBA8;
} else {
internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB;
@@ -1268,6 +1308,61 @@ public class TextureIO {
}
//----------------------------------------------------------------------
+ // PNG texture writer
+
+ static class PNGTextureWriter implements TextureWriter {
+ public boolean write(File file, TextureData data) throws IOException {
+ if (PNG.equals(IOUtil.getFileSuffix(file))) {
+ // See whether the PNG writer can handle this TextureData
+ int pixelFormat = data.getPixelFormat();
+ int pixelType = data.getPixelType();
+ boolean reversedChannels;
+ int bytesPerPixel;
+ switch(pixelFormat) {
+ case GL.GL_RGB:
+ reversedChannels=false;
+ bytesPerPixel=3;
+ break;
+ case GL.GL_RGBA:
+ reversedChannels=false;
+ bytesPerPixel=4;
+ break;
+ case GL2.GL_BGR:
+ reversedChannels=true;
+ bytesPerPixel=3;
+ break;
+ case GL.GL_BGRA:
+ reversedChannels=true;
+ bytesPerPixel=4;
+ break;
+ default:
+ reversedChannels=false;
+ bytesPerPixel=-1;
+ break;
+ }
+ if ( 1 < bytesPerPixel &&
+ (pixelType == GL.GL_BYTE ||
+ pixelType == GL.GL_UNSIGNED_BYTE)) {
+
+ ByteBuffer buf = (ByteBuffer) data.getBuffer();
+ if (null == buf) {
+ buf = (ByteBuffer) data.getMipmapData()[0];
+ }
+ buf.rewind();
+
+ PNGImage image = PNGImage.createFromData(data.getWidth(), data.getHeight(), -1f, -1f,
+ bytesPerPixel, reversedChannels, buf);
+ image.write(file, true);
+ 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)");
+ }
+ return false;
+ }
+ }
+
+ //----------------------------------------------------------------------
// Helper routines
//
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java
index 674e53182..306e7d2fe 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java
@@ -52,6 +52,7 @@ import java.nio.channels.FileChannel;
import javax.media.opengl.GL;
+import com.jogamp.common.util.IOUtil;
import com.jogamp.opengl.util.GLBuffers;
/** A reader and writer for DirectDraw Surface (.dds) files, which are
@@ -281,7 +282,7 @@ public class DDSImage {
* @throws java.io.IOException if an I/O exception occurred
*/
public void write(File file) throws IOException {
- FileOutputStream stream = new FileOutputStream(file);
+ FileOutputStream stream = IOUtil.getFileOutputStream(file, true);
FileChannel chan = stream.getChannel();
// Create ByteBuffer for header in case the start of our
// ByteBuffer isn't actually memory-mapped
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java
index 216c994c0..c2b131b97 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java
@@ -138,7 +138,7 @@ public class NetPbmTextureWriter implements TextureWriter {
throw new IOException("NetPbmTextureWriter magic 6 (PPM) doesn't RGBA pixel format, use magic 7 (PAM)");
}
- FileOutputStream fos = new FileOutputStream(file);
+ FileOutputStream fos = IOUtil.getFileOutputStream(file, true);
StringBuilder header = new StringBuilder();
header.append("P");
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
new file mode 100644
index 000000000..a89418f84
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java
@@ -0,0 +1,172 @@
+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.util.pngj.ImageInfo;
+import jogamp.opengl.util.pngj.ImageLine;
+import jogamp.opengl.util.pngj.PngReader;
+import jogamp.opengl.util.pngj.PngWriter;
+import jogamp.opengl.util.pngj.chunks.PngChunkTextVar;
+
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.common.util.IOUtil;
+
+
+public class 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., RGB or RGBA bottom-to-top (OpenGL coord). */
+ public static PNGImage createFromData(int width, int height, double dpiX, double dpiY,
+ int bytesPerPixel, boolean reversedChannels, ByteBuffer data) {
+ return new PNGImage(width, height, dpiX, dpiY, bytesPerPixel, reversedChannels, data);
+ }
+
+ /** Reads a PNG image from the specified InputStream. */
+ public static PNGImage read(InputStream in) throws IOException {
+ return new PNGImage(in);
+ }
+
+ /** Reverse read and store, implicitly flip image to GL coords. */
+ private static final int getPixelRGBA8(ByteBuffer d, int dOff, ImageLine line, int lineOff, boolean hasAlpha) {
+ if(hasAlpha) {
+ d.put(dOff--, (byte)line.scanline[lineOff + 3]); // A
+ }
+ d.put(dOff--, (byte)line.scanline[lineOff + 2]); // B
+ d.put(dOff--, (byte)line.scanline[lineOff + 1]); // G
+ d.put(dOff--, (byte)line.scanline[lineOff ]); // R
+ return dOff;
+ }
+ /** Reverse read and store, implicitly flip image from GL coords. */
+ private static int setPixelRGBA8(ImageLine line, int lineOff, ByteBuffer d, int dOff, boolean hasAlpha, boolean reversedChannels) {
+ if(reversedChannels) {
+ line.scanline[lineOff ] = d.get(dOff--); // R, A
+ line.scanline[lineOff + 1] = d.get(dOff--); // G, B
+ line.scanline[lineOff + 2] = d.get(dOff--); // B, G
+ if(hasAlpha) {
+ line.scanline[lineOff + 3] = d.get(dOff--);// R
+ }
+ } else {
+ if(hasAlpha) {
+ line.scanline[lineOff + 3] = d.get(dOff--); // A
+ }
+ line.scanline[lineOff + 2] = d.get(dOff--); // B
+ line.scanline[lineOff + 1] = d.get(dOff--); // G
+ line.scanline[lineOff ] = d.get(dOff--); // R
+ }
+ return dOff;
+ }
+
+ private PNGImage(int width, int height, double dpiX, double dpiY, int bytesPerPixel, boolean reversedChannels, 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.data = data;
+ }
+
+ private PNGImage(InputStream in) {
+ final PngReader pngr = new PngReader(new BufferedInputStream(in), null);
+ final int channels = pngr.imgInfo.channels;
+ if (3 > channels || channels > 4 ) {
+ throw new RuntimeException("PNGImage can only handle RGB/RGBA images for now. Channels "+channels);
+ }
+ bytesPerPixel=pngr.imgInfo.bytesPixel;
+ if (3 > bytesPerPixel || bytesPerPixel > 4 ) {
+ throw new RuntimeException("PNGImage can only handle RGB/RGBA images for now. BytesPerPixel "+bytesPerPixel);
+ }
+ pixelWidth=pngr.imgInfo.cols;
+ pixelHeight=pngr.imgInfo.rows;
+ dpi = new double[2];
+ {
+ final double[] dpi2 = pngr.getMetadata().getDpi();
+ dpi[0]=dpi2[0];
+ dpi[1]=dpi2[1];
+ }
+ glFormat= ( 4 == bytesPerPixel ) ? GL.GL_RGBA : GL.GL_RGB;
+ data = Buffers.newDirectByteBuffer(bytesPerPixel * pixelWidth * pixelHeight);
+ reversedChannels = false; // RGB[A]
+ final boolean hasAlpha = 4 == bytesPerPixel;
+ int dataOff = bytesPerPixel * pixelWidth * pixelHeight - 1; // start at end-of-buffer, reverse store
+ 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
+ for (int j = pixelWidth - 1; j >= 0; j--) {
+ dataOff = getPixelRGBA8(data, dataOff, l1, lineOff, hasAlpha);
+ lineOff -= bytesPerPixel;
+ }
+ }
+ pngr.end();
+ }
+ private final int pixelWidth, pixelHeight, glFormat, bytesPerPixel;
+ private boolean reversedChannels;
+ 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 the dpi of the image. */
+ public double[] getDpi() { return dpi; }
+
+ /** Returns the OpenGL format for this texture; e.g. GL.GL_BGR or GL.GL_BGRA. */
+ public int getGLFormat() { return glFormat; }
+
+ /** 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);
+ int dataOff = bytesPerPixel * pixelWidth * pixelHeight - 1; // start at end-of-buffer, reverse read
+ for (int row = 0; row < pixelHeight; row++) {
+ int lineOff = ( pixelWidth - 1 ) * bytesPerPixel ; // start w/ last pixel in line, reverse store
+ for (int j = pixelWidth - 1; j >= 0; j--) {
+ dataOff = setPixelRGBA8(l1, lineOff, data, dataOff, hasAlpha, reversedChannels);
+ lineOff -= bytesPerPixel;
+ }
+ png.writeRow(l1, row);
+ }
+ png.end();
+ } finally {
+ IOUtil.close(outs, false);
+ }
+ }
+
+ public String toString() { return "PNGImage["+pixelWidth+"x"+pixelHeight+", dpi "+dpi[0]+" x "+dpi[1]+", bytesPerPixel "+bytesPerPixel+", reversedChannels "+reversedChannels+", "+data+"]"; }
+}
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java
index c60c91bda..d35330f58 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java
@@ -41,7 +41,8 @@ package com.jogamp.opengl.util.texture.spi;
import java.io.*;
import javax.media.opengl.*;
-import com.jogamp.opengl.util.*;
+
+import com.jogamp.common.util.IOUtil;
/** <p> Reads and writes SGI RGB/RGBA images. </p>
@@ -584,7 +585,7 @@ public class SGIImage {
if (DEBUG)
System.err.println("total_size was " + total_size);
- DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+ DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(IOUtil.getFileOutputStream(file, true)));
writeHeader(stream, xsize, ysize, zsize, true);
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java
index c64644350..e202c59b7 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java
@@ -44,6 +44,8 @@ import java.nio.*;
import java.nio.channels.*;
import javax.media.opengl.*;
+import com.jogamp.common.util.IOUtil;
+
/**
* Targa image reader and writer adapted from sources of the <a href =
* "http://java.sun.com/products/jimi/">Jimi</a> image I/O class library.
@@ -379,7 +381,7 @@ public class TGAImage {
/** Writes the image in Targa format to the specified file. */
public void write(File file) throws IOException {
- FileOutputStream stream = new FileOutputStream(file);
+ FileOutputStream stream = IOUtil.getFileOutputStream(file, true);
FileChannel chan = stream.getChannel();
ByteBuffer buf = ByteBuffer.allocate(header.size());
buf.order(ByteOrder.LITTLE_ENDIAN);