aboutsummaryrefslogtreecommitdiffstats
path: root/src/classes/com/sun/opengl/util/texture/TextureData.java
diff options
context:
space:
mode:
authorKenneth Russel <[email protected]>2006-12-29 16:52:29 +0000
committerKenneth Russel <[email protected]>2006-12-29 16:52:29 +0000
commita736f85a834613d12e2b28e14b5a53fa6991b86f (patch)
treeb201813eff24423470a61fe1ca4f810d194d7a9a /src/classes/com/sun/opengl/util/texture/TextureData.java
parentfba74827e71fbd2ac832401c2b129e4ca0123e3a (diff)
Fixed Issue 257: RFE: Texture.updateSubImage(x,y,w,h)
Changed TextureIO's BufferedImage code paths to no longer copy the data in order to correct for the vertical flip needed between Java 2D's and OpenGL's coordinate systems, but instead to correct for this using a flip of the texture coordinates. This was needed to change the semantics for the BufferedImage from "by-copy" to "by-reference". Made the custom BufferedImage copying code path occur lazily upon a call to TextureData.getBuffer(). Added support for the GL_EXT_abgr extension but disabled the code path currently as it appears there may be bugs in its support on some drivers. Added computation of row width for all BufferedImage types with help and suggestions from Chris Campbell. Improved handling of some BufferedImage types like TYPE_INT_ARGB and TYPE_4BYTE_ABGR. Added Texture.updateSubImage() taking a source rectangle as well as a destination coordinate. Wrote TestSubImage test to show use of the new API which exercises all BufferedImage types. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@1057 232f8b59-042b-4e1e-8c03-345bb8c30851
Diffstat (limited to 'src/classes/com/sun/opengl/util/texture/TextureData.java')
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/TextureData.java280
1 files changed, 153 insertions, 127 deletions
diff --git a/src/classes/com/sun/opengl/util/texture/TextureData.java b/src/classes/com/sun/opengl/util/texture/TextureData.java
index d03faa697..39075e387 100755
--- a/src/classes/com/sun/opengl/util/texture/TextureData.java
+++ b/src/classes/com/sun/opengl/util/texture/TextureData.java
@@ -73,9 +73,18 @@ public class TextureData {
private Buffer buffer; // the actual data...
private Buffer[] mipmapData; // ...or a series of mipmaps
private Flusher flusher;
+ private int rowLength;
private int alignment; // 1, 2, or 4 bytes
private int estimatedMemorySize;
+ // Mechanism for lazily converting input BufferedImages with custom
+ // ColorModels to standard ones for uploading to OpenGL, as well as
+ // backing off from the optimization of hoping that GL_EXT_abgr is
+ // present
+ private BufferedImage imageForLazyCustomConversion;
+ private boolean expectingEXTABGR;
+ private boolean haveEXTABGR;
+
private static final ColorModel rgbaColorModel =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8, 8, 8, 8}, true, true,
@@ -220,12 +229,12 @@ public class TextureData {
/**
* Constructs a new TextureData object with the specified parameters
- * and data contained in the given BufferedImage. Note that
- * subsequent modifications to the BufferedImage after the
- * construction of the TextureData object are not guaranteed to be
- * visible in any Texture object created from the TextureData (and,
- * in fact, the expectation should be that they will not be visible,
- * although this behavior is explicitly left undefined).
+ * and data contained in the given BufferedImage. The resulting
+ * TextureData "wraps" the contents of the BufferedImage, so if a
+ * modification is made to the BufferedImage between the time the
+ * TextureData is constructed and when a Texture is made from the
+ * TextureData, that modification will be visible in the resulting
+ * Texture.
*
* @param internalFormat the OpenGL internal format for the
* resulting texture; may be 0, in which case
@@ -273,12 +282,26 @@ public class TextureData {
vertically for proper display. */
public boolean getMustFlipVertically() { return mustFlipVertically; }
/** Returns the texture data, or null if it is specified as a set of mipmaps. */
- public Buffer getBuffer() { return buffer; }
+ public Buffer getBuffer() {
+ if (imageForLazyCustomConversion != null) {
+ if (!expectingEXTABGR ||
+ (expectingEXTABGR && !haveEXTABGR)) {
+ // Must present the illusion to the end user that we are simply
+ // wrapping the input BufferedImage
+ createFromCustom(imageForLazyCustomConversion);
+ }
+ }
+ return buffer;
+ }
/** Returns all mipmap levels for the texture data, or null if it is
specified as a single image. */
public Buffer[] getMipmapData() { return mipmapData; }
/** Returns the required byte alignment for the texture data. */
public int getAlignment() { return alignment; }
+ /** Returns the row length needed for correct GL_UNPACK_ROW_LENGTH
+ specification. This is currently only supported for
+ non-mipmapped, non-compressed textures. */
+ public int getRowLength() { return rowLength; }
/** Sets the width in pixels of the texture data. */
public void setWidth(int width) { this.width = width; }
@@ -303,6 +326,16 @@ public class TextureData {
public void setBuffer(Buffer buffer) { this.buffer = buffer; }
/** Sets the required byte alignment for the texture data. */
public void setAlignment(int alignment) { this.alignment = alignment; }
+ /** Sets the row length needed for correct GL_UNPACK_ROW_LENGTH
+ specification. This is currently only supported for
+ non-mipmapped, non-compressed textures. */
+ public void setRowLength(int rowLength) { this.rowLength = rowLength; }
+ /** Indicates to this TextureData whether the GL_EXT_abgr extension
+ is available. Used for optimization along some code paths to
+ avoid data copies. */
+ public void setHaveEXTABGR(boolean haveEXTABGR) {
+ this.haveEXTABGR = haveEXTABGR;
+ }
/** Returns an estimate of the amount of memory in bytes this
TextureData will consume once uploaded to the graphics card. It
@@ -335,176 +368,145 @@ public class TextureData {
// Internals only below this point
//
- private void createNIOBufferFromImage(BufferedImage image, boolean flipVertically) {
- if (flipVertically) {
- ImageUtil.flipImageVertically(image);
- }
-
- try {
- //
- // Note: Grabbing the DataBuffer will defeat Java2D's image
- // management mechanism (as of JDK 5/6, at least). This shouldn't
- // be a problem for most JOGL apps, but those that try to upload
- // the image into an OpenGL texture and then use the same image in
- // Java2D rendering might find the 2D rendering is not as fast as
- // it could be.
- //
-
- // Allow previously-selected pixelType (if any) to override that
- // we can infer from the DataBuffer
- DataBuffer data = image.getRaster().getDataBuffer();
- if (data instanceof DataBufferByte) {
- if (pixelType == 0) pixelType = GL.GL_UNSIGNED_BYTE;
- buffer = ByteBuffer.wrap(copyIfNecessary(((DataBufferByte) data).getData(), flipVertically));
- } else if (data instanceof DataBufferDouble) {
- throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL");
- } else if (data instanceof DataBufferFloat) {
- if (pixelType == 0) pixelType = GL.GL_FLOAT;
- buffer = FloatBuffer.wrap(copyIfNecessary(((DataBufferFloat) data).getData(), flipVertically));
- } else if (data instanceof DataBufferInt) {
- // FIXME: should we support signed ints?
- if (pixelType == 0) pixelType = GL.GL_UNSIGNED_INT;
- buffer = IntBuffer.wrap(copyIfNecessary(((DataBufferInt) data).getData(), flipVertically));
- } else if (data instanceof DataBufferShort) {
- if (pixelType == 0) pixelType = GL.GL_SHORT;
- buffer = ShortBuffer.wrap(copyIfNecessary(((DataBufferShort) data).getData(), flipVertically));
- } else if (data instanceof DataBufferUShort) {
- if (pixelType == 0) pixelType = GL.GL_UNSIGNED_SHORT;
- buffer = ShortBuffer.wrap(copyIfNecessary(((DataBufferShort) data).getData(), flipVertically));
- } else {
- throw new RuntimeException("Unexpected DataBuffer type?");
- }
- } finally {
- // Put image back right-side up if necessary
- if (flipVertically) {
- ImageUtil.flipImageVertically(image);
- }
- }
- }
-
- private byte[] copyIfNecessary(byte[] data, boolean needsCopy) {
- if (needsCopy) {
- return (byte[]) data.clone();
- }
- return data;
- }
-
- private short[] copyIfNecessary(short[] data, boolean needsCopy) {
- if (needsCopy) {
- return (short[]) data.clone();
- }
- return data;
- }
-
- private int[] copyIfNecessary(int[] data, boolean needsCopy) {
- if (needsCopy) {
- return (int[]) data.clone();
- }
- return data;
- }
-
- private float[] copyIfNecessary(float[] data, boolean needsCopy) {
- if (needsCopy) {
- return (float[]) data.clone();
- }
- return data;
- }
-
- private double[] copyIfNecessary(double[] data, boolean needsCopy) {
- if (needsCopy) {
- return (double[]) data.clone();
+ private void createNIOBufferFromImage(BufferedImage image) {
+ //
+ // Note: Grabbing the DataBuffer will defeat Java2D's image
+ // management mechanism (as of JDK 5/6, at least). This shouldn't
+ // be a problem for most JOGL apps, but those that try to upload
+ // the image into an OpenGL texture and then use the same image in
+ // Java2D rendering might find the 2D rendering is not as fast as
+ // it could be.
+ //
+
+ DataBuffer data = image.getRaster().getDataBuffer();
+ if (data instanceof DataBufferByte) {
+ buffer = ByteBuffer.wrap(((DataBufferByte) data).getData());
+ } else if (data instanceof DataBufferDouble) {
+ throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL");
+ } else if (data instanceof DataBufferFloat) {
+ buffer = FloatBuffer.wrap(((DataBufferFloat) data).getData());
+ } else if (data instanceof DataBufferInt) {
+ buffer = IntBuffer.wrap(((DataBufferInt) data).getData());
+ } else if (data instanceof DataBufferShort) {
+ buffer = ShortBuffer.wrap(((DataBufferShort) data).getData());
+ } else if (data instanceof DataBufferUShort) {
+ buffer = ShortBuffer.wrap(((DataBufferUShort) data).getData());
+ } else {
+ throw new RuntimeException("Unexpected DataBuffer type?");
}
- return data;
}
-
private void createFromImage(BufferedImage image) {
pixelType = 0; // Determine from image
+ mustFlipVertically = true;
width = image.getWidth();
height = image.getHeight();
+ int scanlineStride;
+ SampleModel sm = image.getRaster().getSampleModel();
+ if (sm instanceof SinglePixelPackedSampleModel) {
+ scanlineStride =
+ ((SinglePixelPackedSampleModel)sm).getScanlineStride();
+ } else if (sm instanceof MultiPixelPackedSampleModel) {
+ scanlineStride =
+ ((MultiPixelPackedSampleModel)sm).getScanlineStride();
+ } else if (sm instanceof ComponentSampleModel) {
+ scanlineStride =
+ ((ComponentSampleModel)sm).getScanlineStride();
+ } else {
+ // This will only happen for TYPE_CUSTOM anyway
+ setupLazyCustomConversion(image);
+ return;
+ }
+
switch (image.getType()) {
case BufferedImage.TYPE_INT_RGB:
pixelFormat = GL.GL_BGRA;
pixelType = GL.GL_UNSIGNED_INT_8_8_8_8_REV;
+ rowLength = scanlineStride;
alignment = 4;
break;
+ case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
pixelFormat = GL.GL_BGRA;
pixelType = GL.GL_UNSIGNED_INT_8_8_8_8_REV;
+ rowLength = scanlineStride;
alignment = 4;
break;
case BufferedImage.TYPE_INT_BGR:
pixelFormat = GL.GL_RGBA;
pixelType = GL.GL_UNSIGNED_INT_8_8_8_8_REV;
+ rowLength = scanlineStride;
alignment = 4;
break;
case BufferedImage.TYPE_3BYTE_BGR:
{
- Raster raster = image.getRaster();
- ComponentSampleModel csm =
- (ComponentSampleModel)raster.getSampleModel();
// we can pass the image data directly to OpenGL only if
- // the raster is tightly packed (i.e. there is no extra
- // space at the end of each scanline)
- if ((csm.getScanlineStride() / 3) == csm.getWidth()) {
+ // we have an integral number of pixels in each scanline
+ if ((scanlineStride % 3) == 0) {
pixelFormat = GL.GL_BGR;
pixelType = GL.GL_UNSIGNED_BYTE;
+ rowLength = scanlineStride / 3;
alignment = 1;
} else {
- createFromCustom(image);
+ setupLazyCustomConversion(image);
return;
}
}
break;
+ case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
{
- Raster raster = image.getRaster();
- ComponentSampleModel csm =
- (ComponentSampleModel)raster.getSampleModel();
// we can pass the image data directly to OpenGL only if
- // the raster is tightly packed (i.e. there is no extra
- // space at the end of each scanline) and only if the
- // GL_EXT_abgr extension is present
-
- // FIXME: with the way this is currently organized we can't
- // probe for the existence of the GL_EXT_abgr extension
- // here; disable this code path for now
- if (((csm.getScanlineStride() / 4) == csm.getWidth()) &&
- /* gl.isExtensionAvailable("GL_EXT_abgr") */ false)
- {
- pixelFormat = GL.GL_ABGR_EXT;
- pixelType = GL.GL_UNSIGNED_BYTE;
- alignment = 4;
- } else {
- createFromCustom(image);
- return;
- }
+ // we have an integral number of pixels in each scanline
+ // and only if the GL_EXT_abgr extension is present
+
+ // NOTE: disabling this code path for now as it appears it's
+ // buggy at least on some NVidia drivers and doesn't perform
+ // the necessary byte swapping (FIXME: needs more
+ // investigation)
+ if ((scanlineStride % 4) == 0 && false) {
+ pixelFormat = GL.GL_ABGR_EXT;
+ pixelType = GL.GL_UNSIGNED_BYTE;
+ rowLength = scanlineStride / 4;
+ alignment = 4;
+
+ // Store a reference to the original image for later in
+ // case it turns out that we don't have GL_EXT_abgr at the
+ // time we're going to do the texture upload to OpenGL
+ setupLazyCustomConversion(image);
+ expectingEXTABGR = true;
+ break;
+ } else {
+ setupLazyCustomConversion(image);
+ return;
+ }
}
- break;
case BufferedImage.TYPE_USHORT_565_RGB:
pixelFormat = GL.GL_RGB;
pixelType = GL.GL_UNSIGNED_SHORT_5_6_5;
+ rowLength = scanlineStride;
alignment = 2;
break;
case BufferedImage.TYPE_USHORT_555_RGB:
pixelFormat = GL.GL_BGRA;
pixelType = GL.GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ rowLength = scanlineStride;
alignment = 2;
break;
case BufferedImage.TYPE_BYTE_GRAY:
pixelFormat = GL.GL_LUMINANCE;
pixelType = GL.GL_UNSIGNED_BYTE;
+ rowLength = scanlineStride;
alignment = 1;
break;
case BufferedImage.TYPE_USHORT_GRAY:
pixelFormat = GL.GL_LUMINANCE;
pixelType = GL.GL_UNSIGNED_SHORT;
+ rowLength = scanlineStride;
alignment = 2;
break;
- case BufferedImage.TYPE_INT_ARGB:
- case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_BYTE_BINARY:
case BufferedImage.TYPE_BYTE_INDEXED:
case BufferedImage.TYPE_CUSTOM:
@@ -513,19 +515,49 @@ public class TextureData {
if (cm.equals(rgbColorModel)) {
pixelFormat = GL.GL_RGB;
pixelType = GL.GL_UNSIGNED_BYTE;
+ rowLength = scanlineStride / 4; // FIXME: correct?
alignment = 1;
} else if (cm.equals(rgbaColorModel)) {
pixelFormat = GL.GL_RGBA;
pixelType = GL.GL_UNSIGNED_BYTE;
+ rowLength = scanlineStride / 4; // FIXME: correct?
alignment = 4;
} else {
- createFromCustom(image);
+ setupLazyCustomConversion(image);
return;
}
break;
}
- createNIOBufferFromImage(image, true);
+ createNIOBufferFromImage(image);
+ }
+
+ private void setupLazyCustomConversion(BufferedImage image) {
+ imageForLazyCustomConversion = image;
+ boolean hasAlpha = image.getColorModel().hasAlpha();
+ pixelFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB;
+ alignment = 1; // FIXME: do we need better?
+ rowLength = width; // FIXME: correct in all cases?
+
+ // Allow previously-selected pixelType (if any) to override that
+ // we can infer from the DataBuffer
+ DataBuffer data = image.getRaster().getDataBuffer();
+ if (data instanceof DataBufferByte) {
+ if (pixelType == 0) pixelType = GL.GL_UNSIGNED_BYTE;
+ } else if (data instanceof DataBufferDouble) {
+ throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL");
+ } else if (data instanceof DataBufferFloat) {
+ if (pixelType == 0) pixelType = GL.GL_FLOAT;
+ } else if (data instanceof DataBufferInt) {
+ // FIXME: should we support signed ints?
+ if (pixelType == 0) pixelType = GL.GL_UNSIGNED_INT;
+ } else if (data instanceof DataBufferShort) {
+ if (pixelType == 0) pixelType = GL.GL_SHORT;
+ } else if (data instanceof DataBufferUShort) {
+ if (pixelType == 0) pixelType = GL.GL_UNSIGNED_SHORT;
+ } else {
+ throw new RuntimeException("Unexpected DataBuffer type?");
+ }
}
private void createFromCustom(BufferedImage image) {
@@ -560,17 +592,11 @@ public class TextureData {
// copy the source image into the temporary image
Graphics2D g = texImage.createGraphics();
g.setComposite(AlphaComposite.Src);
- // Flip image vertically as long as we're at it
- g.drawImage(image,
- 0, height, width, 0,
- 0, 0, width, height,
- null);
+ g.drawImage(image, 0, 0, null);
g.dispose();
// Wrap the buffer from the temporary image
- createNIOBufferFromImage(texImage, false);
- pixelFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB;
- alignment = 1; // FIXME: do we need better?
+ createNIOBufferFromImage(texImage);
}
private int estimatedMemorySize(Buffer buffer) {