This algorithm will produce a texture, given an image.
the result will both be in texImage, as well as in a textureCompatible buffer, which can be fed to glTexSubImage2D, glTexImage2D or gluBuild2DMipmaps
Basically, for any given image I, it will generate an ARGB image Y, which is a scaled ARGB version (to the nearest power of 2) of I. Testing shows it is about 16 times faster than a similar graphics.drawImage() scaling operation, even with all “dd” performance flags set on the VM. It uses fixed point arithmetics to achieve maximum speed.
I needed this because I was rendering video to a texture, and the java library algorithms were too slow.
in case you are wondering what the fastest way to update a texture in gl is, it is glTexSubImage2D, and you can use it as such, in combination with the algorithm:
gl.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, textureCompatibleBuffer);
anyway, I hope it is usefull to you, and if you find other optimizations I haven’t thought of, share it with us here, please 8)
you will need the “get2fold” algorithm, as mentioned in this post:
http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=share;action=display;num=1117186907
// variables used
private BufferedImage image;
private WritableRaster raster;
private BufferedImage texImage;
private byte[] iBuffer;
private byte[] tBuffer;
private ByteBuffer textureCompatibleBuffer;
private int[] bankOffsets;
private int textureWidth = -1;
private int textureHeight = -1;
private int xScaleUnit;
private int yScaleUnit;
private ComponentSampleModel imageSampleModel;
private int scanLineStride;
private int pixelStride;
public static final ColorModel glAlphaColorModel =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8,8,8,8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
// start code
// only generate the raster and the buffer the first time
if(raster == null) {
// generate a texture compatible image size, large enough to hold the image
textureWidth = get2Fold(image.getWidth());
textureHeight = get2Fold(image.getHeight());
// generate an interleaved byte-based ARGB raster and image
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,this.textureWidth,this.textureHeight,4,null);
texImage = new BufferedImage(glAlphaColorModel,raster,false,new Hashtable());
// get the pointers to the data of the [t]exture buffer
// and the data of the [i]image buffer
iBuffer = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
tBuffer= ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData();
// get information on how the image is stored in the buffer
ComponentSampleModel imageSampleModel = (ComponentSampleModel) image.getSampleModel();
scanLineStride = imageSampleModel.getScanlineStride();
pixelStride = imageSampleModel.getPixelStride();
bankOffsets = imageSampleModel.getBandOffsets();
// generate a fixed point floating point number
// which will allow us to calculate for a given x or y on the texture
// where the source pixel on the image is
xScaleUnit = (int) (((double) image.getWidth() / (double) textureWidth) * 65536);
yScaleUnit = (int) (((double) image.getHeight() / (double) textureHeight) * 65536);
// generate a bytebuffer to store the resulting image
textureCompatibleBuffer = ByteBuffer.allocateDirect(tBuffer.length);
textureCompatibleBuffer.order(ByteOrder.nativeOrder());
}
int adr = 0; // the address in the texture buffer
int xOffset = 0; // the x coordinate in the image
int bufferOffset = 0; // the final buffer offset in the image
int yOffset = 0; // the y coordinate in the image
int yBufferOffset = 0; // a temp value containing the start of the scanline in the image
if(bankOffsets.length > 3) { // RGBA or ABGR images
for(int y=0; y<textureHeight; y++) {
xOffset = 0;
yBufferOffset = (yOffset >> 16) * scanLineStride;
for(int x=0; x<textureWidth; x++) {
bufferOffset = yBufferOffset + (xOffset >> 16) * pixelStride;
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[0]];
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[1]];
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[2]];
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[3]];
xOffset += xScaleUnit;
}
yOffset += yScaleUnit;
}
} else { // RGB or BGR images
for(int y=0; y<textureHeight; y++) {
xOffset = 0;
yBufferOffset = (yOffset >> 16) * scanLineStride;
for(int x=0; x<textureWidth; x++) {
bufferOffset = yBufferOffset + (xOffset >> 16) * pixelStride;
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[0]];
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[1]];
tBuffer[adr++] = iBuffer[bufferOffset + bankOffsets[2]];
tBuffer[adr++] = -1; // -1 signed = 255 unsigned
xOffset += xScaleUnit;
}
yOffset += yScaleUnit;
}
}
textureCompatibleBuffer.rewind();
textureCompatibleBuffer.put(tBuffer, 0, tBuffer.length);