/*
 * License Applicability. Except to the extent portions of this file are
 * made subject to an alternative license as permitted in the SGI Free
 * Software License B, Version 2.0 (the "License"), the contents of this
 * file are subject only to the provisions of the License. You may not use
 * this file except in compliance with the License. You may obtain a copy
 * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
 * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
 * 
 * http://oss.sgi.com/projects/FreeB
 * 
 * Note that, as provided in the License, the Software is distributed on an
 * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
 * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
 * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
 * 
 * NOTE:  The Original Code (as defined below) has been licensed to Sun
 * Microsystems, Inc. ("Sun") under the SGI Free Software License B
 * (Version 1.1), shown above ("SGI License").   Pursuant to Section
 * 3.2(3) of the SGI License, Sun is distributing the Covered Code to
 * you under an alternative license ("Alternative License").  This
 * Alternative License includes all of the provisions of the SGI License
 * except that Section 2.2 and 11 are omitted.  Any differences between
 * the Alternative License and the SGI License are offered solely by Sun
 * and not by SGI.
 *
 * Original Code. The Original Code is: OpenGL Sample Implementation,
 * Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
 * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
 * Copyright in any portions created by third parties is as indicated
 * elsewhere herein. All Rights Reserved.
 * 
 * Additional Notice Provisions: The application programming interfaces
 * established by SGI in conjunction with the Original Code are The
 * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
 * April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
 * 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
 * Window System(R) (Version 1.3), released October 19, 1998. This software
 * was created using the OpenGL(R) version 1.2.1 Sample Implementation
 * published by SGI, but has not been independently verified as being
 * compliant with the OpenGL(R) version 1.2.1 Specification.
 */

package jogamp.opengl.glu.mipmap;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GL2GL3;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.GLException;
import java.nio.*;
import com.jogamp.common.nio.Buffers;

/**
 *
 * @author  Administrator
 */
public class Mipmap {
  
  /** Creates a new instance of Mipmap */
  public Mipmap() {
  }
  
  public static int computeLog( int value ) {
    int i = 0;
    // Error
    if( value == 0 ) {
      return( -1 );
    }
    for( ;; ) {
      if( (value & 1) >= 1 ) {
        if( value != 1 ) {
          return( -1 );
        }
        return( i );
      }
      value = value >> 1;
      i++;
    }
  }
  
  /* Compute the nearest power of 2 number.  This algorithm is a little strange
   * but it works quite well.
   */
  public static int nearestPower( int value ) {
    int i = 1;
    // Error!
    if( value == 0 ) {
      return( -1 );
    }
    for( ;; ) {
      if( value == 1 ) {
        return( i );
      } else if( value == 3 ) {
        return( i * 4 );
      }
      value = value >> 1;
      i *= 2;
    }
  }
  
  public static short GLU_SWAP_2_BYTES( short s ) {
    byte b = 0;
    b = (byte)( s >>> 8 );
    s = (short)( s << 8 );
    s = (short)( s | (0x00FF & b) );
    return( s );
  }
  
  public static int GLU_SWAP_4_BYTES( int i ) {
    int t = i << 24;
    t |= 0x00FF0000 & ( i << 8 );
    t |= 0x0000FF00 & ( i >>> 8 );
    t |= 0x000000FF & ( i >>> 24 );
    return( t );
  }
  
  public static float GLU_SWAP_4_BYTES( float f ) {
    int i = Float.floatToRawIntBits( f );
    float temp = Float.intBitsToFloat( i );
    return( temp );
  }
  
  public static int checkMipmapArgs( int internalFormat, int format, int type ) {
    if( !legalFormat( format ) || !legalType( type ) ) {
      return( GLU.GLU_INVALID_ENUM );
    }
    if( format == GL2GL3.GL_STENCIL_INDEX ) {
      return( GLU.GLU_INVALID_ENUM );
    }
    if( !isLegalFormatForPackedPixelType( format, type ) ) {
      return( GLU.GLU_INVALID_OPERATION );
    }
    return( 0 );
  }
  
  public static boolean legalFormat( int format ) {
    switch( format ) {
      case( GL2.GL_COLOR_INDEX ):
      case( GL2GL3.GL_STENCIL_INDEX ):
      case( GL2GL3.GL_DEPTH_COMPONENT ):
      case( GL2GL3.GL_RED ):
      case( GL2GL3.GL_GREEN ):
      case( GL2GL3.GL_BLUE ):
      case( GL2GL3.GL_ALPHA ):
      case( GL2GL3.GL_RGB ):
      case( GL2GL3.GL_RGBA ):
      case( GL2GL3.GL_LUMINANCE ):
      case( GL2GL3.GL_LUMINANCE_ALPHA ):
      case( GL2GL3.GL_BGR ):
      case( GL.GL_BGRA ):
        return( true );
      default:
        return( false );
    }
  }
  
  public static boolean legalType( int type ) {
    switch( type ) {
      case( GL2.GL_BITMAP ):
      case( GL2GL3.GL_BYTE ):
      case( GL2GL3.GL_UNSIGNED_BYTE ):
      case( GL2GL3.GL_SHORT ):
      case( GL2GL3.GL_UNSIGNED_SHORT ):
      case( GL2GL3.GL_INT ):
      case( GL2GL3.GL_UNSIGNED_INT ):
      case( GL2GL3.GL_FLOAT ):
      case( GL2GL3.GL_UNSIGNED_BYTE_3_3_2 ):
      case( GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV ):
      case( GL2GL3.GL_UNSIGNED_SHORT_5_6_5 ):
      case( GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV ):
      case( GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4 ):
      case( GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV ):
      case( GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1 ):
      case( GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV ):
      case( GL2GL3.GL_UNSIGNED_INT_8_8_8_8 ):
      case( GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV ):
      case( GL2GL3.GL_UNSIGNED_INT_10_10_10_2 ):
      case( GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV ):
        return( true );
      default:
        return( false );
    }
  }
  
  public static boolean isTypePackedPixel( int type ) {
    assert( legalType( type ) );
    
    if( type == GL2GL3.GL_UNSIGNED_BYTE_3_3_2 ||
        type == GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5 ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4 ||
        type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1 ||
        type == GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV ||
        type == GL2GL3.GL_UNSIGNED_INT_8_8_8_8 ||
        type == GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV ||
        type == GL2GL3.GL_UNSIGNED_INT_10_10_10_2 ||
        type == GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV ) {
          return( true );
    }
    return( false );
  }
  
  public static boolean isLegalFormatForPackedPixelType( int format, int type ) {
    // if not a packed pixel type then return true
    if( isTypePackedPixel( type ) ) {
      return( true );
    }
    
    // 3_3_2/2_3_3_REV & 5_6_5/5_6_5_REV are only compatible with RGB
    if( (type == GL2GL3.GL_UNSIGNED_BYTE_3_3_2 || type == GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5 || type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV )
        & format != GL2GL3.GL_RGB ) {
          return( false );
    }
    
    // 4_4_4_4/4_4_4_4_REV & 5_5_5_1/1_5_5_5_REV & 8_8_8_8/8_8_8_8_REV &
    // 10_10_10_2/2_10_10_10_REV are only campatible with RGBA, BGRA & ARGB_EXT
    if( ( type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4 ||
          type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV ||
          type == GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1 ||
          type == GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV ||
          type == GL2GL3.GL_UNSIGNED_INT_8_8_8_8 ||
          type == GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV ||
          type == GL2GL3.GL_UNSIGNED_INT_10_10_10_2 ||
          type == GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV ) &&
          (format != GL.GL_RGBA && format != GL.GL_BGRA) ) {
            return( false );
    }
    return( true );
  }
  
  public static boolean isLegalLevels( int userLevel, int baseLevel, int maxLevel,
                                            int totalLevels ) {
    if( (baseLevel < 0) || (baseLevel < userLevel) || (maxLevel < baseLevel) ||
                                                  (totalLevels < maxLevel) ) {
      return( false );
    }
    return( true );
  }
  
  /* Given user requested textures size, determine if it fits. If it doesn't then
   * halve both sides and make the determination again until it does fit ( for
   * IR only ).
   * Note that proxy textures are not implemented in RE* even though they
   * advertise the texture extension.
   * Note that proxy textures are implemented but not according to spec in IMPACT*
   */
  public static void closestFit( GL gl, int target, int width, int height, int internalFormat,
                                int format, int type, int[] newWidth, int[] newHeight ) {
    // Use proxy textures if OpenGL version >= 1.1
    if( Double.parseDouble( gl.glGetString( GL.GL_VERSION ).trim().substring( 0, 3 ) ) >= 1.1 ) {
      int widthPowerOf2 = nearestPower( width );
      int heightPowerOf2 = nearestPower( height );
      int[] proxyWidth = new int[1];
      boolean noProxyTextures = false;
      
      // Some drivers (in particular, ATI's) seem to set a GL error
      // when proxy textures are used even though this is in violation
      // of the spec. Guard against this and interactions with the
      // DebugGL by watching for GLException.
      try {
        do {
          // compute level 1 width & height, clamping each at 1
          int widthAtLevelOne = ( ( width > 1 ) ? (widthPowerOf2 >> 1) : widthPowerOf2 );
          int heightAtLevelOne = ( ( height > 1 ) ? (heightPowerOf2 >> 1) : heightPowerOf2 );
          int proxyTarget;
        
          assert( widthAtLevelOne > 0 );
          assert( heightAtLevelOne > 0 );
        
          // does width x height at level 1 & all their mipmaps fit?
          if( target == GL2GL3.GL_TEXTURE_2D || target == GL2GL3.GL_PROXY_TEXTURE_2D ) {
            proxyTarget = GL2GL3.GL_PROXY_TEXTURE_2D;
            gl.glTexImage2D( proxyTarget, 1, internalFormat, widthAtLevelOne,
                             heightAtLevelOne, 0, format, type, null );
          } else if( (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_X) || 
                     (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_X) || 
                     (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_Y) || 
                     (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) || 
                     (target == GL2GL3.GL_TEXTURE_CUBE_MAP_POSITIVE_Z) || 
                     (target == GL2GL3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) ) {
            proxyTarget = GL2GL3.GL_PROXY_TEXTURE_CUBE_MAP;
            gl.glTexImage2D( proxyTarget, 1, internalFormat, widthAtLevelOne,
                             heightAtLevelOne, 0, format, type, null );
          } else {
            assert( target == GL2GL3.GL_TEXTURE_1D || target == GL2GL3.GL_PROXY_TEXTURE_1D );
            proxyTarget = GL2GL3.GL_PROXY_TEXTURE_1D;
            gl.getGL2GL3().glTexImage1D( proxyTarget, 1, internalFormat, widthAtLevelOne, 
                             0, format, type, null );
          }
          if(gl.isGL2GL3()) {
            gl.getGL2GL3().glGetTexLevelParameteriv( proxyTarget, 1, GL2GL3.GL_TEXTURE_WIDTH, proxyWidth, 0 );
          } else {
            proxyWidth[0] = 0;
          }
          // does it fit?
          if( proxyWidth[0] == 0 ) { // nope, so try again with theses sizes
            if( widthPowerOf2 == 1 && heightPowerOf2 == 1 ) {
              /* A 1x1 texture couldn't fit for some reason so break out.  This
               * should never happen.  But things happen.  The disadvantage with
               * this if-statement is that we will never be aware of when this
               * happens since it will silently branch out.
               */
              noProxyTextures = true;
              break;
            }
            widthPowerOf2 = widthAtLevelOne;
            heightPowerOf2 = heightAtLevelOne;
          }
          // else it does fit
        } while( proxyWidth[0] == 0 );
      } catch (GLException e) {
        noProxyTextures = true;
      }
      // loop must terminate
      // return the width & height at level 0 that fits
      if( !noProxyTextures ) {
        newWidth[0] = widthPowerOf2;
        newHeight[0] = heightPowerOf2;
        return;
      }
    }
    int[] maxsize = new int[1];
    gl.glGetIntegerv( GL2GL3.GL_MAX_TEXTURE_SIZE, maxsize , 0);
    // clamp user's texture sizes to maximum sizes, if necessary
    newWidth[0] = nearestPower( width );
    if( newWidth[0] > maxsize[0] ) {
      newWidth[0] = maxsize[0];
    }
    newHeight[0] = nearestPower( height );
    if( newHeight[0] > maxsize[0] ) {
      newHeight[0] = maxsize[0];
    }
  }
  
  public static void closestFit3D( GL gl, int target, int width, int height, int depth, 
          int internalFormat, int format, int type, int[] newWidth, int[] newHeight,
          int[] newDepth ) {
    int widthPowerOf2 = nearestPower( width );
    int heightPowerOf2 = nearestPower( height );
    int depthPowerOf2 = nearestPower( depth );
    int[] proxyWidth = new int[1];
    
    do {
      // compute level 1 width & height & depth, clamping each at 1
      int widthAtLevelOne = (widthPowerOf2 > 1) ? widthPowerOf2 >> 1 : widthPowerOf2;
      int heightAtLevelOne = (heightPowerOf2 > 1) ? heightPowerOf2 >> 1 : heightPowerOf2;
      int depthAtLevelOne = (depthPowerOf2 > 1) ? depthPowerOf2 >> 1 : depthPowerOf2;
      int proxyTarget = 0;
      assert( widthAtLevelOne > 0 );
      assert( heightAtLevelOne > 0 );
      assert( depthAtLevelOne > 0 );
      
      // does width x height x depth at level 1 & all their mipmaps fit?
      if( target == GL2GL3.GL_TEXTURE_3D || target == GL2GL3.GL_PROXY_TEXTURE_3D ) {
        proxyTarget = GL2GL3.GL_PROXY_TEXTURE_3D;
        gl.getGL2GL3().glTexImage3D( proxyTarget, 1, internalFormat, widthAtLevelOne,
                heightAtLevelOne, depthAtLevelOne, 0, format, type, null );
      }
      if(gl.isGL2GL3()) {
        gl.getGL2GL3().glGetTexLevelParameteriv( proxyTarget, 1, GL2GL3.GL_TEXTURE_WIDTH, proxyWidth, 0 );
      } else {
        proxyWidth[0] = 0;
      }
      // does it fit
      if( proxyWidth[0] == 0 ) {
        if( widthPowerOf2 == 1 && heightPowerOf2 == 1 && depthPowerOf2 == 1 ) {
          newWidth[0] = newHeight[0] = newDepth[0] = 1;
          return;
        }
        widthPowerOf2 = widthAtLevelOne;
        heightPowerOf2 = heightAtLevelOne;
        depthPowerOf2 = depthAtLevelOne;
      }
    } while( proxyWidth[0] == 0 );
    // loop must terminate
    
    // return the width & height at level 0 that fits
    newWidth[0] = widthPowerOf2;
    newHeight[0] = heightPowerOf2;
    newDepth[0] = depthPowerOf2;
  }
  
  public static int elements_per_group( int format, int type ) {
    // Return the number of elements per grtoup of a specified gromat
    
    // If the type is packedpixels then answer is 1
    if( type == GL2GL3.GL_UNSIGNED_BYTE_3_3_2 ||
        type == GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5 ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4 ||
        type == GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV ||
        type == GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1 ||
        type == GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV ||
        type == GL2GL3.GL_UNSIGNED_INT_8_8_8_8 ||
        type == GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV ||
        type == GL2GL3.GL_UNSIGNED_INT_10_10_10_2 ||
        type == GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV ) {
          return( 1 );
    }
    
    // Types are not packed pixels so get elements per group
    switch( format ) {
      case( GL2GL3.GL_RGB ):
      case( GL2GL3.GL_BGR ):
        return( 3 );
      case( GL2GL3.GL_LUMINANCE_ALPHA ):
        return( 2 );
      case( GL.GL_RGBA ):
      case( GL.GL_BGRA ):
        return( 4 );
      default:
        return( 1 );
    }
  }
  
  public static int bytes_per_element( int type ) {
    // return the number of bytes per element, based on the element type
    
    switch( type ) {
      case( GL2.GL_BITMAP ):
      case( GL2GL3.GL_BYTE ):
      case( GL2GL3.GL_UNSIGNED_BYTE ):
      case( GL2GL3.GL_UNSIGNED_BYTE_3_3_2 ):
      case( GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV ):
        return( 1 );
      case( GL2GL3.GL_SHORT ):
      case( GL2GL3.GL_UNSIGNED_SHORT ):
      case( GL2GL3.GL_UNSIGNED_SHORT_5_6_5 ):
      case( GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV ):
      case( GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4 ):
      case( GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV ):
      case( GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1 ):
      case( GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV ):
        return( 2 );
      case( GL2GL3.GL_INT ):
      case( GL2GL3.GL_UNSIGNED_INT ):
      case( GL2GL3.GL_UNSIGNED_INT_8_8_8_8 ):
      case( GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV ):
      case( GL2GL3.GL_UNSIGNED_INT_10_10_10_2 ):
      case( GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV ):
      case( GL2GL3.GL_FLOAT ):
        return( 4 );
      default:
        return( 4 );
    }
  }
  
  public static boolean is_index( int format ) {
    return( format == GL2.GL_COLOR_INDEX || format == GL2GL3.GL_STENCIL_INDEX );
  }
  
  /* Compute memory required for internal packed array of data of given type and format. */
  
  public static int image_size( int width, int height, int format, int type ) {
    int bytes_per_row;
    int components;
    
    assert( width > 0 );
    assert( height > 0 );
    components = elements_per_group( format, type );
    if( type == GL2.GL_BITMAP ) {
      bytes_per_row = (width + 7) / 8;
    } else {
      bytes_per_row = bytes_per_element( type ) * width;
    }
    return( bytes_per_row * height * components );
  }
  
  public static int imageSize3D( int width, int height, int depth, int format, int type ) {
    int components = elements_per_group( format, type );
    int bytes_per_row = bytes_per_element( type ) * width;
    
    assert( width > 0 && height > 0 && depth > 0 );
    assert( type != GL2.GL_BITMAP );
    
    return( bytes_per_row * height * depth * components );
  }
  
  public static void retrieveStoreModes( GL gl, PixelStorageModes psm ) {
    int[] a = new int[1];
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_ALIGNMENT, a, 0);
    psm.setUnpackAlignment( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_ROW_LENGTH, a, 0);
    psm.setUnpackRowLength( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SKIP_ROWS, a, 0);
    psm.setUnpackSkipRows( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SKIP_PIXELS, a, 0);
    psm.setUnpackSkipPixels( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_LSB_FIRST, a, 0);
    psm.setUnpackLsbFirst( ( a[0] == 1 ) );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SWAP_BYTES, a, 0);
    psm.setUnpackSwapBytes( ( a[0] == 1 ) );
    
    gl.glGetIntegerv( GL2GL3.GL_PACK_ALIGNMENT, a, 0);
    psm.setPackAlignment( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_ROW_LENGTH, a, 0);
    psm.setPackRowLength( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SKIP_ROWS, a, 0);
    psm.setPackSkipRows( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SKIP_PIXELS, a, 0);
    psm.setPackSkipPixels( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_LSB_FIRST, a, 0);
    psm.setPackLsbFirst( ( a[0] == 1 ) );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SWAP_BYTES, a, 0);
    psm.setPackSwapBytes( ( a[0] == 1 ) );
  }
  
  public static void retrieveStoreModes3D( GL gl, PixelStorageModes psm ) {
    int[] a = new int[1];
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_ALIGNMENT, a, 0);
    psm.setUnpackAlignment( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_ROW_LENGTH, a, 0);
    psm.setUnpackRowLength( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SKIP_ROWS, a, 0);
    psm.setUnpackSkipRows( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SKIP_PIXELS, a, 0);
    psm.setUnpackSkipPixels( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_LSB_FIRST, a, 0);
    psm.setUnpackLsbFirst( ( a[0] == 1 ) );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SWAP_BYTES, a, 0);
    psm.setUnpackSwapBytes( ( a[0] == 1 ) );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_SKIP_IMAGES, a, 0);
    psm.setUnpackSkipImages( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_UNPACK_IMAGE_HEIGHT, a, 0);
    psm.setUnpackImageHeight( a[0] );
    
    gl.glGetIntegerv( GL2GL3.GL_PACK_ALIGNMENT, a, 0);
    psm.setPackAlignment( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_ROW_LENGTH, a, 0);
    psm.setPackRowLength( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SKIP_ROWS, a, 0);
    psm.setPackSkipRows( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SKIP_PIXELS, a, 0 );
    psm.setPackSkipPixels( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_LSB_FIRST, a, 0 );
    psm.setPackLsbFirst( ( a[0] == 1 ) );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SWAP_BYTES, a, 0 );
    psm.setPackSwapBytes( ( a[0] == 1 ) );
    gl.glGetIntegerv( GL2GL3.GL_PACK_SKIP_IMAGES, a, 0 );
    psm.setPackSkipImages( a[0] );
    gl.glGetIntegerv( GL2GL3.GL_PACK_IMAGE_HEIGHT, a, 0 );
    psm.setPackImageHeight( a[0] );
  }
  
  public static int gluScaleImage( GL gl, int format, int widthin, int heightin, 
          int typein, ByteBuffer datain, int widthout, int heightout, 
          int typeout, ByteBuffer dataout ) {
    int datainPos = datain.position();
    int dataoutPos = dataout.position();
    try {

      int components;
      ByteBuffer beforeimage;
      ByteBuffer afterimage;
      PixelStorageModes psm = new PixelStorageModes();
    
      if( (widthin == 0)  || (heightin == 0) || (widthout == 0) || (heightout == 0) ) {
        return( 0 );
      }
      if( (widthin < 0) || (heightin < 0) || (widthout < 0) || (heightout < 0) ) {
        return( GLU.GLU_INVALID_VALUE );
      }
      if( !legalFormat( format ) || !legalType( typein ) || !legalType( typeout ) ) {
        return( GLU.GLU_INVALID_ENUM );
      }
      if( !isLegalFormatForPackedPixelType( format, typein ) ) {
        return( GLU.GLU_INVALID_OPERATION );
      }
      if( !isLegalFormatForPackedPixelType( format, typeout ) ) {
        return( GLU.GLU_INVALID_OPERATION );
      }
      beforeimage = Buffers.newDirectByteBuffer( image_size( widthin, heightin, format, GL2GL3.GL_UNSIGNED_SHORT ) );
      afterimage = Buffers.newDirectByteBuffer( image_size( widthout, heightout, format, GL2GL3.GL_UNSIGNED_SHORT ) );
      if( beforeimage == null || afterimage == null ) {
        return( GLU.GLU_OUT_OF_MEMORY );
      }
    
      retrieveStoreModes( gl, psm );
      Image.fill_image( psm, widthin, heightin, format, typein, is_index( format ), datain, beforeimage.asShortBuffer() );
      components = elements_per_group( format, 0 );
      ScaleInternal.scale_internal( components, widthin, heightin, beforeimage.asShortBuffer(), widthout, heightout, afterimage.asShortBuffer() );
      Image.empty_image( psm, widthout, heightout, format, typeout, is_index( format ), afterimage.asShortBuffer(), dataout );
    
      return( 0 );
    } finally {
      datain.position(datainPos);
      dataout.position(dataoutPos);
    }
  }
  
  public static int gluBuild1DMipmapLevels( GL gl, int target, int internalFormat,
                          int width, int format, int type, int userLevel, int baseLevel,
                          int maxLevel, ByteBuffer data ) {
    int dataPos = data.position();
    try {

      int levels;
    
      int rc = checkMipmapArgs( internalFormat, format, type );
      if( rc != 0 ) {
        return( rc );
      }
    
      if( width < 1 ) {
        return( GLU.GLU_INVALID_VALUE );
      }
    
      levels = computeLog( width );
    
      levels += userLevel;
      if( !isLegalLevels( userLevel, baseLevel, maxLevel, levels ) ) {
        return( GLU.GLU_INVALID_VALUE );
      }
    
      return( BuildMipmap.gluBuild1DMipmapLevelsCore( gl, target, internalFormat, width,
              width, format, type, userLevel, baseLevel, maxLevel, data ) );
    } finally {
      data.position(dataPos);
    }
  }
  
  public static int gluBuild1DMipmaps( GL gl, int target, int internalFormat, int width,
              int format, int type, ByteBuffer data ) {
    int dataPos = data.position();

    try {
      int[] widthPowerOf2 = new int[1];
      int levels;
      int[] dummy = new int[1];
    
      int rc = checkMipmapArgs( internalFormat, format, type );
      if( rc != 0 ) {
        return( rc );
      }
    
      if( width < 1 ) {
        return( GLU.GLU_INVALID_VALUE );
      }
    
      closestFit( gl, target, width, 1, internalFormat, format, type, widthPowerOf2, dummy );
      levels = computeLog( widthPowerOf2[0] );
    
      return( BuildMipmap.gluBuild1DMipmapLevelsCore( gl, target, internalFormat, 
                    width, widthPowerOf2[0], format, type, 0, 0, levels, data ) );
    } finally {
      data.position(dataPos);
    }
  }

 
  public static int gluBuild2DMipmapLevels( GL gl, int target, int internalFormat,
          int width, int height, int format, int type, int userLevel,
          int baseLevel, int maxLevel, Object data ) {
    int dataPos = 0;

    int level, levels;
    
    int rc = checkMipmapArgs( internalFormat, format, type );
    if( rc != 0 ) {
      return( rc );
    }
    
    if( width < 1 || height < 1 ) {
      return( GLU.GLU_INVALID_VALUE );
    }
    
    levels = computeLog( width );
    level = computeLog( height );
    if( level > levels ) {
      levels = level;
    }
    
    levels += userLevel;
    if( !isLegalLevels( userLevel, baseLevel, maxLevel, levels ) ) {
      return( GLU.GLU_INVALID_VALUE );
    }
    
    //PointerWrapper pointer = PointerWrapperFactory.getPointerWrapper( data );
    ByteBuffer buffer = null;
    if( data instanceof ByteBuffer ) {
        buffer = (ByteBuffer)data;
        dataPos = buffer.position();
    } else if( data instanceof byte[] ) {
      byte[] array = (byte[])data;
      buffer = ByteBuffer.allocateDirect(array.length);
      buffer.put(array);
    } else if( data instanceof short[] ) {
        short[] array = (short[])data;
        buffer = ByteBuffer.allocateDirect( array.length * 2 );
        ShortBuffer sb = buffer.asShortBuffer();
        sb.put( array );
    } else if( data instanceof int[] ) {
        int[] array = (int[])data;
        buffer = ByteBuffer.allocateDirect( array.length * 4 );
        IntBuffer ib = buffer.asIntBuffer();
        ib.put( array );
    } else if( data instanceof float[] ) {
        float[] array = (float[])data;
        buffer = ByteBuffer.allocateDirect( array.length * 4 );
        FloatBuffer fb = buffer.asFloatBuffer();
        fb.put( array );
    }
    
    try {
      return( BuildMipmap.gluBuild2DMipmapLevelsCore( gl, target, internalFormat,
              width, height, width, height, format, type, userLevel, baseLevel,
              maxLevel, buffer ) );
    } finally {
      buffer.position(dataPos);
    }
  }

 
  public static int gluBuild2DMipmaps( GL gl, int target, int internalFormat,
          int width, int height, int format, int type, Object data ) {
    int dataPos = 0;

    int[] widthPowerOf2 = new int[1];
    int[] heightPowerOf2 = new int[1];
    int level, levels;
    
    int rc = checkMipmapArgs( internalFormat, format, type );
    if( rc != 0 ) {
      return( rc );
    }
    
    if( width < 1 || height < 1 ) {
      return( GLU.GLU_INVALID_VALUE );
    }
    
    closestFit( gl, target, width, height, internalFormat, format, type, 
            widthPowerOf2, heightPowerOf2 );
    
    levels = computeLog( widthPowerOf2[0] );
    level = computeLog( heightPowerOf2[0] );
    if( level > levels ) {
      levels = level;
    }
    
    //PointerWrapper pointer = PointerWrapperFactory.getPointerWrapper( data );
    ByteBuffer buffer = null;
    if( data instanceof ByteBuffer ) {
        buffer = (ByteBuffer)data;
        dataPos = buffer.position();
    } else if( data instanceof byte[] ) {
      byte[] array = (byte[])data;
      buffer = ByteBuffer.allocateDirect(array.length);
      buffer.put(array);
    } else if( data instanceof short[] ) {
        short[] array = (short[])data;
        buffer = ByteBuffer.allocateDirect( array.length * 2 );
        ShortBuffer sb = buffer.asShortBuffer();
        sb.put( array );
    } else if( data instanceof int[] ) {
        int[] array = (int[])data;
        buffer = ByteBuffer.allocateDirect( array.length * 4 );
        IntBuffer ib = buffer.asIntBuffer();
        ib.put( array );
    } else if( data instanceof float[] ) {
        float[] array = (float[])data;
        buffer = ByteBuffer.allocateDirect( array.length * 4 );
        FloatBuffer fb = buffer.asFloatBuffer();
        fb.put( array );
    }
    
    try {
      return( BuildMipmap.gluBuild2DMipmapLevelsCore( gl, target, internalFormat, 
              width, height, widthPowerOf2[0], heightPowerOf2[0], format, type, 0, 
              0, levels, buffer ) );
    } finally {
      buffer.position(dataPos);
    }
  }
 
 
  public static int gluBuild3DMipmaps( GL gl, int target, int internalFormat,
          int width, int height, int depth, int format, int type, ByteBuffer data ) {
    int dataPos = data.position();
    try {

      int[] widthPowerOf2 = new int[1];
      int[] heightPowerOf2 = new int[1];
      int[] depthPowerOf2 = new int[1];
      int level, levels;
    
      int rc = checkMipmapArgs( internalFormat, format, type );
      if( rc != 0 ) {
        return( rc );
      }
    
      if( width < 1 || height < 1 || depth < 1 ) {
        return( GLU.GLU_INVALID_VALUE );
      }
    
      if( type == GL2.GL_BITMAP ) {
        return( GLU.GLU_INVALID_ENUM );
      }
    
      closestFit3D( gl, target, width, height, depth, internalFormat, format,
                    type, widthPowerOf2, heightPowerOf2, depthPowerOf2 );
    
      levels = computeLog( widthPowerOf2[0] );
      level = computeLog( heightPowerOf2[0] );
      if( level > levels ) {
        levels = level;
      }
      level = computeLog( depthPowerOf2[0] );
      if( level > levels ) {
        levels = level;
      }
    
      return( BuildMipmap.gluBuild3DMipmapLevelsCore( gl, target, internalFormat, width,
              height, depth, widthPowerOf2[0], heightPowerOf2[0], depthPowerOf2[0],
              format, type, 0, 0, levels, data ) );
    } finally {
      data.position(dataPos);
    }
  }
  
  public static int gluBuild3DMipmapLevels( GL gl, int target, int internalFormat,
          int width, int height, int depth, int format, int type, int userLevel,
          int baseLevel, int maxLevel, ByteBuffer data ) {
    int dataPos = data.position();
    try {
      int level, levels;
    
      int rc = checkMipmapArgs( internalFormat, format, type );
      if( rc != 0 ) {
        return( rc );
      }
    
      if( width < 1 || height < 1 || depth < 1 ) {
        return( GLU.GLU_INVALID_VALUE );
      }
    
      if( type == GL2.GL_BITMAP ) {
        return( GLU.GLU_INVALID_ENUM );
      }
    
      levels = computeLog( width );
      level = computeLog( height );
      if( level > levels ) {
        levels = level;
      }
      level = computeLog( depth );
      if( level > levels ) {
        levels = level;
      }
    
      levels += userLevel;
      if( !isLegalLevels( userLevel, baseLevel, maxLevel, levels ) ) {
        return( GLU.GLU_INVALID_VALUE );
      }
    
      return( BuildMipmap.gluBuild3DMipmapLevelsCore( gl, target, internalFormat, width,
              height, depth, width, height, depth, format, type, userLevel,
              baseLevel, maxLevel, data ) );
    } finally {
      data.position(dataPos);
    }
  }
}
