aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/com
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-04-01 05:16:35 +0200
committerSven Gothel <[email protected]>2013-04-01 05:16:35 +0200
commita54bd3e963a7be320dee0c9692d237607fcd0f96 (patch)
treee365b6baeda020429355f9a7a98a7c3e65da713b /src/jogl/classes/com
parent00c65b4a34445d140ca9e6ee531dfa4278b8e770 (diff)
Fix Bug 671: Add JPEG Decoder w/o AWT Dependencies
Original JavaScript code from <https://github.com/notmasteryet/jpgjs/blob/master/jpg.js>, author 'notmasteryet' <async.processingjs at yahoo.com>. Ported to Java. Enhancements: * InputStream instead of memory buffer * User provided memory handler * Fixed JPEG Component ID/Index mapping * Color space conversion (YCCK, CMYK -> RGB) * More error tolerant +++ Features: JOGL AWT RGB ok ok YCCK ok Exception CMYK ok Exception YUV Store ok n/a Need Y-Flip no yes +++ Benchmark: TestJPEGJoglAWTBenchmarkNewtAWT JOGL.RGB Loops 100, dt 1199 ms, 11.99 ms/l JOGL.YUV Loops 100, dt 351 ms, 3.51 ms/l AWT..... Loops 100, dt 2144 ms, 21.44 ms/l File: jogl/src/test/com/jogamp/opengl/test/junit/jogl/util/texture/j1-baseline.jpg Machine: GNU/Linux PC (AMD 8 core), JavaSE 6 (1.6.0_38) .++++ UITestCase.setUp: com.jogamp.opengl.test.junit.jogl.util.texture.TestJPEGJoglAWTBenchmarkNewtAWT - benchmark libEGL warning: DRI2: failed to authenticate 0: JPEGImage[261x202, bytesPerPixel 3, reversedChannels false, JPEGPixels[261x202, sourceComp 3, sourceCS YCbCr, storageCS RGB, storageComp 3], java.nio.DirectByteBuffer[pos=0 lim=158166 cap=158166]] 0: TextureData[261x202, y-flip false, internFormat 0x1907, pixelFormat 0x1907, pixelType 0x1401, border 0, estSize 158166, alignment 1, rowlen 0, buffer java.nio.DirectByteBuffer[pos=0 lim=158166 cap=158166] JOGL.RGB Loops 100, dt 1199 ms, 11.99 ms/l 0: JPEGImage[261x202, bytesPerPixel 3, reversedChannels false, JPEGPixels[261x202, sourceComp 3, sourceCS YCbCr, storageCS YCbCr, storageComp 3], java.nio.DirectByteBuffer[pos=0 lim=158166 cap=158166]] 0: TextureData[261x202, y-flip false, internFormat 0x1907, pixelFormat 0x1907, pixelType 0x1401, border 0, estSize 158166, alignment 1, rowlen 0, buffer java.nio.DirectByteBuffer[pos=0 lim=158166 cap=158166] JOGL.YUV Loops 100, dt 351 ms, 3.51 ms/l 0: TextureData[261x202, y-flip true, internFormat 0x1907, pixelFormat 0x80e0, pixelType 0x1401, border 0, estSize 158166, alignment 1, rowlen 261, buffer java.nio.HeapByteBuffer[pos=0 lim=158166 cap=158166] AWT..... Loops 100, dt 2144 ms, 21.44 ms/l ++++ UITestCase.tearDown: com.jogamp.opengl.test.junit.jogl.util.texture.TestJPEGJoglAWTBenchmarkNewtAWT - benchmark
Diffstat (limited to 'src/jogl/classes/com')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java15
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java57
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java50
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java175
4 files changed, 277 insertions, 20 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java
index 96ee233fd..5b72bea82 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java
@@ -54,6 +54,8 @@ import com.jogamp.opengl.util.GLBuffers;
*/
public class TextureData {
+ public static enum ColorSpace { RGB, YCbCr, YCCK, CMYK };
+
protected int width;
protected int height;
private int border;
@@ -77,6 +79,7 @@ public class TextureData {
protected boolean haveEXTABGR;
protected boolean haveGL12;
protected GLProfile glProfile;
+ protected ColorSpace pixelCS = ColorSpace.RGB;
/**
* Constructs a new TextureData object with the specified parameters
@@ -217,6 +220,18 @@ public class TextureData {
}
}
+ /**
+ * Returns the color space of the pixel data.
+ * @see #setColorSpace(ColorSpace)
+ */
+ public ColorSpace getColorSpace() { return pixelCS; }
+
+ /**
+ * Set the color space of the pixel data, which defaults to {@link ColorSpace#RGB}.
+ * @see #getColorSpace()
+ */
+ public void setColorSpace(ColorSpace cs) { pixelCS = cs; }
+
/** Used only by subclasses */
protected TextureData(GLProfile glp) { this.glProfile = glp; }
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 b878c6002..0b0af5625 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java
@@ -63,6 +63,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.JPEGImage;
import com.jogamp.opengl.util.texture.spi.NetPbmTextureWriter;
import com.jogamp.opengl.util.texture.spi.PNGImage;
import com.jogamp.opengl.util.texture.spi.SGIImage;
@@ -714,8 +715,12 @@ public class TextureIO {
// SPI support
//
- /** Adds a TextureProvider to support reading of a new file
- format. */
+ /**
+ * Adds a TextureProvider to support reading of a new file format.
+ * <p>
+ * The last provider added, will be the first provider to be tested.
+ * </p>
+ */
public static void addTextureProvider(TextureProvider provider) {
// Must always add at the front so the ImageIO provider is last,
// so we don't accidentally use it instead of a user's possibly
@@ -723,8 +728,12 @@ public class TextureIO {
textureProviders.add(0, provider);
}
- /** Adds a TextureWriter to support writing of a new file
- format. */
+ /**
+ * Adds a TextureWriter to support writing of a new file format.
+ * <p>
+ * The last provider added, will be the first provider to be tested.
+ * </p>
+ */
public static void addTextureWriter(TextureWriter writer) {
// Must always add at the front so the ImageIO writer is last,
// so we don't accidentally use it instead of a user's possibly
@@ -768,7 +777,7 @@ public class TextureIO {
private static List<TextureProvider> textureProviders = new ArrayList<TextureProvider>();
private static List<TextureWriter> textureWriters = new ArrayList<TextureWriter>();
- static {
+ static {
// ImageIO provider, the fall-back, must be the first one added
if(GLProfile.isAWTAvailable()) {
try {
@@ -787,6 +796,7 @@ public class TextureIO {
addTextureProvider(new DDSTextureProvider());
addTextureProvider(new SGITextureProvider());
addTextureProvider(new TGATextureProvider());
+ addTextureProvider(new JPGTextureProvider());
addTextureProvider(new PNGTextureProvider());
// ImageIO writer, the fall-back, must be the first one added
@@ -1174,6 +1184,43 @@ public class TextureIO {
}
//----------------------------------------------------------------------
+ // JPEG image provider
+ static class JPGTextureProvider extends StreamBasedTextureProvider {
+ public TextureData newTextureData(GLProfile glp, InputStream stream,
+ int internalFormat,
+ int pixelFormat,
+ boolean mipmap,
+ String fileSuffix) throws IOException {
+ if (JPG.equals(fileSuffix)) {
+ JPEGImage image = JPEGImage.read(/*glp, */ stream);
+ if (pixelFormat == 0) {
+ pixelFormat = image.getGLFormat();
+ }
+ if (internalFormat == 0) {
+ if(glp.isGL2GL3()) {
+ internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8;
+ } else {
+ internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB;
+ }
+ }
+ return new TextureData(glp, internalFormat,
+ image.getWidth(),
+ image.getHeight(),
+ 0,
+ pixelFormat,
+ image.getGLType(),
+ mipmap,
+ false,
+ false,
+ image.getData(),
+ null);
+ }
+
+ return null;
+ }
+ }
+
+ //----------------------------------------------------------------------
// DDS texture writer
//
static class DDSTextureWriter implements TextureWriter {
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java
index ad96a9939..3b90fad65 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java
@@ -37,15 +37,35 @@
package com.jogamp.opengl.util.texture.awt;
import java.awt.AlphaComposite;
-import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Transparency;
-import java.awt.color.*;
-import java.awt.image.*;
-import java.nio.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferDouble;
+import java.awt.image.DataBufferFloat;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferShort;
+import java.awt.image.DataBufferUShort;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
-import javax.media.opengl.*;
-import com.jogamp.opengl.util.texture.*;
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2;
+import javax.media.opengl.GL2GL3;
+import javax.media.opengl.GLException;
+import javax.media.opengl.GLProfile;
+
+import com.jogamp.opengl.util.texture.TextureData;
public class AWTTextureData extends TextureData {
// Mechanism for lazily converting input BufferedImages with custom
@@ -56,13 +76,13 @@ public class AWTTextureData extends TextureData {
private boolean expectingEXTABGR;
private boolean expectingGL12;
- private static final ColorModel rgbaColorModel =
- new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ private static final java.awt.image.ColorModel rgbaColorModel =
+ new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
new int[] {8, 8, 8, 8}, true, true,
Transparency.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
- private static final ColorModel rgbColorModel =
- new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ private static final java.awt.image.ColorModel rgbColorModel =
+ new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
new int[] {8, 8, 8, 0}, false, false,
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
@@ -282,7 +302,7 @@ public class AWTTextureData extends TextureData {
case BufferedImage.TYPE_BYTE_INDEXED:
case BufferedImage.TYPE_CUSTOM:
default:
- ColorModel cm = image.getColorModel();
+ java.awt.image.ColorModel cm = image.getColorModel();
if (cm.equals(rgbColorModel)) {
pixelFormat = GL.GL_RGB;
pixelType = GL.GL_UNSIGNED_BYTE;
@@ -350,7 +370,7 @@ public class AWTTextureData extends TextureData {
case BufferedImage.TYPE_BYTE_INDEXED:
case BufferedImage.TYPE_CUSTOM:
default:
- ColorModel cm = image.getColorModel();
+ java.awt.image.ColorModel cm = image.getColorModel();
if (cm.equals(rgbColorModel)) {
pixelFormat = GL.GL_RGB;
pixelType = GL.GL_UNSIGNED_BYTE;
@@ -409,7 +429,7 @@ public class AWTTextureData extends TextureData {
// create a temporary image that is compatible with OpenGL
boolean hasAlpha = image.getColorModel().hasAlpha();
- ColorModel cm = null;
+ java.awt.image.ColorModel cm = null;
int dataBufferType = image.getRaster().getDataBuffer().getDataType();
// Don't use integer components for packed int images
if (isPackedInt(image)) {
@@ -419,12 +439,12 @@ public class AWTTextureData extends TextureData {
cm = hasAlpha ? rgbaColorModel : rgbColorModel;
} else {
if (hasAlpha) {
- cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ cm = new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
null, true, true,
Transparency.TRANSLUCENT,
dataBufferType);
} else {
- cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ cm = new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
null, false, false,
Transparency.OPAQUE,
dataBufferType);
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java
new file mode 100644
index 000000000..71dd53939
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java
@@ -0,0 +1,175 @@
+/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.util.texture.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import javax.media.opengl.GL;
+
+import jogamp.opengl.Debug;
+import jogamp.opengl.util.jpeg.JPEGDecoder;
+
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.opengl.util.texture.TextureData.ColorSpace;
+
+public class JPEGImage {
+ private static final boolean DEBUG = Debug.debug("JPEGImage");
+
+
+ /**
+ * Reads a JPEG image from the specified InputStream, using the given color space for storage.
+ *
+ * @param in
+ * @param cs Storage color space, either {@link ColorSpace#RGB} or {@link ColorSpace#YCbCr}. {@link ColorSpace#YCCK} and {@link ColorSpace#CMYK} will throw an exception!
+ * @return
+ * @throws IOException
+ */
+ public static JPEGImage read(InputStream in, ColorSpace cs) throws IOException {
+ return new JPEGImage(in, cs);
+ }
+
+ /** Reads a JPEG image from the specified InputStream, using the {@link ColorSpace#RGB}. */
+ public static JPEGImage read(InputStream in) throws IOException {
+ return new JPEGImage(in, ColorSpace.RGB);
+ }
+
+ private static class JPEGPixelStorage implements JPEGDecoder.PixelStorage {
+ int width=0, height=0;
+ int sourceComponents=0;
+ ColorSpace sourceCS = ColorSpace.YCbCr;
+ int storageComponents;
+ final ColorSpace storageCS;
+ ByteBuffer data = null;
+
+ JPEGPixelStorage(ColorSpace storageCM) {
+ this.storageCS = storageCM;
+ switch(storageCS) {
+ case RGB:
+ case YCbCr:
+ storageComponents = 3;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported storage color-space: "+storageCS);
+ }
+ }
+
+ @Override
+ public final ColorSpace allocate(int width, int height, ColorSpace sourceCM, int sourceComponents) throws RuntimeException {
+ this.width = width;
+ this.height = height;
+ this.sourceComponents = sourceComponents;
+ this.sourceCS = sourceCM;
+ this.data = Buffers.newDirectByteBuffer(width * height * storageComponents);
+ return storageCS;
+ }
+
+ @Override
+ public final void storeRGB(int x, int y, byte r, byte g, byte b) {
+ int i = ( ( height - y - 1 ) * width + x ) * storageComponents;
+ data.put(i++, r);
+ data.put(i++, g);
+ data.put(i++, b);
+ // data.put(i++, (byte)0xff);
+ }
+
+ @Override
+ public final void store2(int x, int y, byte c1, byte c2) {
+ throw new RuntimeException("not supported yet");
+ }
+
+ @Override
+ public final void storeYCbCr(int x, int y, byte Y, byte Cb, byte Cr) {
+ int i = ( ( height - y - 1 ) * width + x ) * storageComponents;
+ data.put(i++, Y);
+ data.put(i++, Cb);
+ data.put(i++, Cr);
+ }
+
+ public String toString() {
+ return "JPEGPixels["+width+"x"+height+", sourceComp "+sourceComponents+", sourceCS "+sourceCS+", storageCS "+storageCS+", storageComp "+storageComponents+"]";
+ }
+ };
+
+ private JPEGImage(InputStream in, ColorSpace cs) throws IOException {
+ pixelStorage = new JPEGPixelStorage(cs);
+ final JPEGDecoder decoder = new JPEGDecoder();
+ decoder.parse(in);
+ pixelWidth = decoder.getWidth();
+ pixelHeight = decoder.getHeight();
+ decoder.getPixel(pixelStorage, pixelWidth, pixelHeight);
+ data = pixelStorage.data;
+ final boolean hasAlpha = false;
+
+ bytesPerPixel = 3;
+ glFormat = GL.GL_RGB;
+ reversedChannels = false; // RGB[A]
+ if(DEBUG) {
+ System.err.println("JPEGImage: alpha "+hasAlpha+", bytesPerPixel "+bytesPerPixel+
+ ", pixels "+pixelWidth+"x"+pixelHeight+", glFormat 0x"+Integer.toHexString(glFormat));
+ System.err.println("JPEGImage: "+decoder);
+ System.err.println("JPEGImage: "+pixelStorage);
+ }
+ decoder.clear(null);
+ }
+ private JPEGPixelStorage pixelStorage;
+ private final int pixelWidth, pixelHeight, glFormat, bytesPerPixel;
+ private boolean reversedChannels;
+ private final ByteBuffer data;
+
+ /** Returns the color space of the pixel data */
+ public ColorSpace getColorSpace() { return pixelStorage.storageCS; }
+
+ /** Returns the number of components of the pixel data */
+ public int getComponentCount() { return pixelStorage.storageComponents; }
+
+ /** 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 OpenGL format for this texture; e.g. GL.GL_LUMINANCE, GL.GL_RGB or GL.GL_RGBA. */
+ public int getGLFormat() { return glFormat; }
+
+ /** Returns the OpenGL data type: GL.GL_UNSIGNED_BYTE. */
+ public int getGLType() { return GL.GL_UNSIGNED_BYTE; }
+
+ /** Returns the bytes per pixel */
+ public int getBytesPerPixel() { return bytesPerPixel; }
+
+ /** Returns the raw data for this texture in the correct
+ (bottom-to-top) order for calls to glTexImage2D. */
+ public ByteBuffer getData() { return data; }
+
+ public String toString() { return "JPEGImage["+pixelWidth+"x"+pixelHeight+", bytesPerPixel "+bytesPerPixel+", reversedChannels "+reversedChannels+", "+pixelStorage+", "+data+"]"; }
+}