/* * Copyright (c) 2002-2008 LWJGL Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of 'LWJGL' nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 THE COPYRIGHT OWNER 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. */ package org.lwjgl.util.glu; import java.nio.ByteBuffer; import org.lwjgl.BufferUtils; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.util.glu.GLU.*; /** * MipMap.java * * * Created 11-jan-2004 * @author Erik Duijs */ public class MipMap extends Util { /** * Method gluBuild2DMipmaps * * @param target * @param components * @param width * @param height * @param format * @param type * @param data * @return int */ public static int gluBuild2DMipmaps(final int target, final int components, final int width, final int height, final int format, final int type, final ByteBuffer data) { if ( width < 1 || height < 1 ) return GLU_INVALID_VALUE; final int bpp = bytesPerPixel(format, type); if ( bpp == 0 ) return GLU_INVALID_ENUM; final int maxSize = glGetIntegerv(GL_MAX_TEXTURE_SIZE); int w = nearestPower(width); if ( w > maxSize ) w = maxSize; int h = nearestPower(height); if ( h > maxSize ) h = maxSize; // Get current glPixelStore state PixelStoreState pss = new PixelStoreState(); // set pixel packing glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); ByteBuffer image; int retVal = 0; boolean done = false; if ( w != width || h != height ) { // must rescale image to get "top" mipmap texture image image = BufferUtils.createByteBuffer((w + 4) * h * bpp); int error = gluScaleImage(format, width, height, type, data, w, h, type, image); if ( error != 0 ) { retVal = error; done = true; } /* set pixel unpacking */ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); } else { image = data; } ByteBuffer bufferA = null; ByteBuffer bufferB = null; int level = 0; while ( !done ) { if (image != data) { /* set pixel unpacking */ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); } glTexImage2D(target, level, components, w, h, 0, format, type, image); if ( w == 1 && h == 1 ) break; final int newW = (w < 2) ? 1 : w >> 1; final int newH = (h < 2) ? 1 : h >> 1; final ByteBuffer newImage; if ( bufferA == null ) newImage = (bufferA = BufferUtils.createByteBuffer((newW + 4) * newH * bpp)); else if ( bufferB == null ) newImage = (bufferB = BufferUtils.createByteBuffer((newW + 4) * newH * bpp)); else newImage = bufferB; int error = gluScaleImage(format, w, h, type, image, newW, newH, type, newImage); if ( error != 0 ) { retVal = error; done = true; } image = newImage; if ( bufferB != null ) bufferB = bufferA; w = newW; h = newH; level++; } // Restore original glPixelStore state pss.save(); return retVal; } /** * Method gluScaleImage. * @param format * @param widthIn * @param heightIn * @param typein * @param dataIn * @param widthOut * @param heightOut * @param typeOut * @param dataOut * @return int */ public static int gluScaleImage(int format, int widthIn, int heightIn, int typein, ByteBuffer dataIn, int widthOut, int heightOut, int typeOut, ByteBuffer dataOut) { final int components = compPerPix(format); if ( components == -1 ) return GLU_INVALID_ENUM; int i, j, k; float[] tempIn, tempOut; float sx, sy; int sizein, sizeout; int rowstride, rowlen; // temp image data tempIn = new float[widthIn * heightIn * components]; tempOut = new float[widthOut * heightOut * components]; // Determine bytes per input type switch ( typein ) { case GL_UNSIGNED_BYTE: sizein = 1; break; case GL_FLOAT: sizein = 4; break; default: return GL_INVALID_ENUM; } // Determine bytes per output type switch ( typeOut ) { case GL_UNSIGNED_BYTE: sizeout = 1; break; case GL_FLOAT: sizeout = 4; break; default: return GL_INVALID_ENUM; } // Get glPixelStore state PixelStoreState pss = new PixelStoreState(); //Unpack the pixel data and convert to floating point if ( pss.unpackRowLength > 0 ) rowlen = pss.unpackRowLength; else rowlen = widthIn; if ( sizein >= pss.unpackAlignment ) rowstride = components * rowlen; else rowstride = pss.unpackAlignment / sizein * ceil(components * rowlen * sizein, pss.unpackAlignment); switch ( typein ) { case GL_UNSIGNED_BYTE: k = 0; dataIn.rewind(); for ( i = 0; i < heightIn; i++ ) { int ubptr = i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components; for ( j = 0; j < widthIn * components; j++ ) { tempIn[k++] = dataIn.get(ubptr++) & 0xff; } } break; case GL_FLOAT: k = 0; dataIn.rewind(); for ( i = 0; i < heightIn; i++ ) { int fptr = 4 * (i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components); for ( j = 0; j < widthIn * components; j++ ) { tempIn[k++] = dataIn.getFloat(fptr); fptr += 4; } } break; default: return GLU_INVALID_ENUM; } // Do scaling sx = (float)widthIn / (float)widthOut; sy = (float)heightIn / (float)heightOut; float[] c = new float[components]; int src, dst; for ( int iy = 0; iy < heightOut; iy++ ) { for ( int ix = 0; ix < widthOut; ix++ ) { int x0 = (int)(ix * sx); int x1 = (int)((ix + 1) * sx); int y0 = (int)(iy * sy); int y1 = (int)((iy + 1) * sy); int readPix = 0; // reset weighted pixel for ( int ic = 0; ic < components; ic++ ) { c[ic] = 0; } // create weighted pixel for ( int ix0 = x0; ix0 < x1; ix0++ ) { for ( int iy0 = y0; iy0 < y1; iy0++ ) { src = (iy0 * widthIn + ix0) * components; for ( int ic = 0; ic < components; ic++ ) { c[ic] += tempIn[src + ic]; } readPix++; } } // store weighted pixel dst = (iy * widthOut + ix) * components; if ( readPix == 0 ) { // Image is sized up, caused by non power of two texture as input src = (y0 * widthIn + x0) * components; for ( int ic = 0; ic < components; ic++ ) { tempOut[dst++] = tempIn[src + ic]; } } else { // sized down for ( k = 0; k < components; k++ ) { tempOut[dst++] = c[k] / readPix; } } } } // Convert temp output if ( pss.packRowLength > 0 ) rowlen = pss.packRowLength; else rowlen = widthOut; if ( sizeout >= pss.packAlignment ) rowstride = components * rowlen; else rowstride = pss.packAlignment / sizeout * ceil(components * rowlen * sizeout, pss.packAlignment); switch ( typeOut ) { case GL_UNSIGNED_BYTE: k = 0; for ( i = 0; i < heightOut; i++ ) { int ubptr = i * rowstride + pss.packSkipRows * rowstride + pss.packSkipPixels * components; for ( j = 0; j < widthOut * components; j++ ) { dataOut.put(ubptr++, (byte)tempOut[k++]); } } break; case GL_FLOAT: k = 0; for ( i = 0; i < heightOut; i++ ) { int fptr = 4 * (i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components); for ( j = 0; j < widthOut * components; j++ ) { dataOut.putFloat(fptr, tempOut[k++]); fptr += 4; } } break; default: return GLU_INVALID_ENUM; } return 0; } }