/*
 * 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 java.nio.*;

/**
 *
 * @author  Administrator
 */
public class HalveImage {
  
  private static final int BOX2 = 2;
  private static final int BOX4 = 4;
  private static final int BOX8 = 8;
  
  public static void halveImage( int components, int width, int height,
          ShortBuffer datain, ShortBuffer dataout ) {
    int i, j, k;
    int newwidth, newheight;
    int delta;
    int t = 0;
    short temp = 0;
    
    newwidth = width / 2;
    newheight = height /2;
    delta = width * components;
    
    // Piece of cake
    for( i = 0; i < newheight; i++ ) {
      for( j = 0; j < newwidth; j++ ) {
        for( k = 0; k < components; k++ ) {
          datain.position( t );
          temp = datain.get();
          datain.position( t + components );
          temp += datain.get();
          datain.position( t + delta );
          temp += datain.get();
          datain.position( t + delta + components );
          temp +=datain.get();
          temp += 2;
          temp /= 4;
          dataout.put( temp );
          t++;
        }
        t += components;
      }
      t += delta;
    }
  }
  
  public static void halveImage_ubyte( int components, int width, int height,
                                      ByteBuffer datain, ByteBuffer dataout,
                                      int element_size, int ysize, int group_size ) {
    int i, j, k;
    int newwidth, newheight;
    int s;
    int t;
    
    // Handle case where there is only 1 column/row
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) ); // can't be 1x1
      halve1Dimage_ubyte( components, width, height, datain, dataout, element_size, ysize, group_size );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    s = 0;
    t = 0;
    
    int temp = 0;
    // piece of cake
    for( i = 0; i < newheight; i++ ) {
      for( j = 0; j < newwidth; j++ ) {
        for( k = 0; k < components; k++ ) {
          datain.position( t );
          temp = ( 0x000000FF & datain.get() );
          datain.position( t + group_size );
          temp += ( 0x000000FF & datain.get() );
          datain.position( t + ysize );
          temp += ( 0x000000FF & datain.get() );
          datain.position( t + ysize + group_size );
          temp += ( 0x000000FF & datain.get() ) + 2;
          dataout.put( (byte)(temp / 4) );
          t += element_size;
        }
        t += group_size;
      }
      t += ysize;
    }
  }
  
  public static void halve1Dimage_ubyte( int components, int width, int height,
                      ByteBuffer datain, ByteBuffer dataout, 
                      int element_size, int ysize, int group_size ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int dest = 0;
    int jj;
    int temp = 0;
    
    assert( width == 1 || height == 1 ); // Must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // widthxheight can't be 1x1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          datain.position( src );
          temp = ( 0x000000FF & datain.get() );
          datain.position( src + group_size );
          temp += ( 0x000000FF & datain.get() );
          temp /= 2;
          dataout.put( (byte)temp );
          /*
          dataout.setByte( (byte)(((0x000000FF & datain.setIndexInBytes(src).getByte()) + 
                    (0x000000FF & datain.setIndexInBytes( src + group_size ).getByte())) / 2 ) );
           */
          src += element_size;
          //dataout.plusPlus();
          dest++;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assertion only
    } else if( width == 1 ) { // 1 column
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 );
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          datain.position( src );
          temp = ( 0x000000FF & datain.get() );
          datain.position( src + ysize );
          temp += ( 0x000000FF & datain.get() );
          temp /= 2;
          dataout.put( (byte)temp );
          /*
          dataout.setByte( (byte)(((0x000000FF & datain.setIndexInBytes(src).getByte()) + 
                    (0x000000FF & datain.setIndexInBytes(src + ysize).getByte()) ) / 2 ) );
           */
          src += element_size;
          //dataout.plusPlus();
          dest++;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
    }
    assert( src == ysize * height );
    assert( dest == components * element_size * halfWidth * halfHeight );
  }
  
  public static void halveImage_byte( int components, int width, int height,
                    ByteBuffer datain, ByteBuffer dataout, int element_size,
                    int ysize, int group_size ) {
    int i, j, k;
    int newwidth, newheight;
    int s = 0;
    int t = 0;
    byte temp = (byte)0;
    
    // handle case where there is only 1 column
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) );
      halve1Dimage_byte( components, width, height, datain, dataout, element_size,
                                                            ysize, group_size );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    
    for( i = 0; i < newheight; i++ ) {
      for( j = 0; j < newwidth; j++ ) {
        for( k = 0; k < components; k++ ) {
          datain.position( t );
          temp = datain.get();
          datain.position( t + group_size );
          temp += datain.get();
          datain.position( t + ysize );
          temp += datain.get();
          datain.position( t + ysize + group_size );
          temp += datain.get();
          temp += 2;
          temp /= 4;
          dataout.put( temp );
          t += element_size;
        }
        t += group_size;
      }
      t += ysize;
    }
  }
  
  public static void halve1Dimage_byte( int components, int width, int height,
                      ByteBuffer datain, ByteBuffer dataout,
                      int element_size, int ysize, int group_size ) {
    int halfWidth = width / 2;
    int halfHeight = width / 2;
    int src = 0;
    int dest = 0;
    int jj;
    byte temp = (byte)0;
    
    assert( width == 1 || height == 1 ); // must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // widthxheight can't be 1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          datain.position( src );
          temp = datain.get();
          datain.position( src + group_size );
          temp += datain.get();
          temp /= 2;
          dataout.put( temp );
          src += element_size;
          dest++;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assert only
    } else if( width == 1 ) { // 1 column
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 ); // widthxheight can't be 1
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          datain.position( src );
          temp = datain.get();
          datain.position( src + ysize );
          temp += datain.get();
          temp /= 2;
          src += element_size;
          dest++;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
      assert( src == ysize * height );
    }
    assert( dest == components * element_size * halfWidth * halfHeight );
  }
  
  public static void halveImage_ushort( int components, int width, int height,
                          ByteBuffer datain, ShortBuffer dataout, int element_size,
                          int ysize, int group_size, boolean myswap_bytes ) {
    int i, j, k, l;
    int newwidth, newheight;
    int s = 0;
    int t = 0;
    int temp = 0;
    // handle case where there is only 1 column/row
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) ); // can't be 1x1
      halve1Dimage_ushort( components, width, height, datain, dataout, element_size,
                                ysize, group_size, myswap_bytes );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    
    // Piece of cake
    if( !myswap_bytes ) {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            datain.position( t );
            temp = ( 0x0000FFFF & datain.getShort() );
            datain.position( t + group_size );
            temp += ( 0x0000FFFF & datain.getShort() );
            datain.position( t + ysize );
            temp += ( 0x0000FFFF & datain.getShort() );
            datain.position( t + ysize + group_size );
            temp += ( 0x0000FFFF & datain.getShort() );
            dataout.put( (short)( ( temp + 2 ) / 4 ) );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    } else {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            datain.position( t );
            temp = ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
            datain.position( t + group_size );
            temp += ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
            datain.position( t + ysize );
            temp += ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
            datain.position( t + ysize + group_size );
            temp += ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
            dataout.put( (short)( ( temp + 2 ) / 4 ) );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    }
  }
  
  public static void halve1Dimage_ushort( int components, int width, int height,
                      ByteBuffer datain, ShortBuffer dataout, int element_size,
                      int ysize, int group_size, boolean myswap_bytes ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int dest = 0;
    int jj;
    
    assert( width == 1 || height == 1 ); // must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // widthxheight can't be 1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < halfHeight; kk++ ) {
          int[] ushort = new int[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            ushort[0] = ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
            datain.position( src + group_size );
            ushort[1] = (0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
          } else {
            datain.position( src );
            ushort[0] = (0x0000FFFF & datain.getShort() );
            datain.position( src + group_size );
            ushort[1] = (0x0000FFFF & datain.getShort() );
          }
          dataout.put( (short)( (ushort[0] + ushort[1]) / 2 ) );
          src += element_size;
          dest += 2;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assertion only
    } else if( width == 1 ) { // 1 column
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 ); // widthxheight can't be 1
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          int[] ushort = new int[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            ushort[0] = ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
            datain.position( src + ysize );
            ushort[0] = ( 0x0000FFFF & Mipmap.GLU_SWAP_2_BYTES( datain.getShort() ) );
          } else {
            datain.position( src );
            ushort[0] = ( 0x0000FFFF & datain.getShort() );
            datain.position( src + ysize );
            ushort[1] = ( 0x0000FFFF & datain.getShort() );
          }
          dataout.put( (short)((ushort[0] + ushort[1]) / 2) );
          src += element_size;
          dest += 2;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
      assert( src == ysize * height );
    }
    assert( dest == components * element_size * halfWidth * halfHeight );
  }
  
  public static void halveImage_short( int components, int width, int height,
                        ByteBuffer datain, ShortBuffer dataout, int element_size,
                        int ysize, int group_size, boolean myswap_bytes ) {
    int i, j, k, l;
    int newwidth, newheight;
    int s = 0;
    int t = 0;
    short temp = (short)0;
    // handle case where there is only 1 column/row
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) ); // can't be 1x1
      halve1Dimage_short( components, width, height, datain, dataout, element_size,
                            ysize, group_size, myswap_bytes );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    
    // Piece of cake
    if( !myswap_bytes ) {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            datain.position( t );
            temp = datain.getShort();
            datain.position( t + group_size );
            temp += datain.getShort();
            datain.position( t + ysize );
            temp += datain.getShort();
            datain.position( t + ysize + group_size );
            temp += datain.getShort();
            temp += 2;
            temp /= 4;
            dataout.put( (short)temp );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    } else {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            short b;
            int buf;
            datain.position( t );
            temp = Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
            datain.position( t + group_size );
            temp += Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
            datain.position( t + ysize );
            temp += Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
            datain.position( t + ysize + group_size );
            temp += Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
            temp += 2;
            temp /= 4;
            dataout.put( temp );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    }
  }
  
  public static void halve1Dimage_short( int components, int width, int height,
              ByteBuffer datain, ShortBuffer dataout, int element_size, int ysize,
              int group_size, boolean myswap_bytes ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int dest = 0;
    int jj;
    
    assert( width == 1 || height == 1 ); // must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // can't be 1x1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          short[] sshort = new short[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            sshort[0] = Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
            datain.position( src + group_size );
            sshort[1] = Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
          } else {
            datain.position( src );
            sshort[0] = datain.getShort();
            datain.position( src + group_size );
            sshort[1] = datain.getShort();
          }
          dataout.put( (short)(( sshort[0] + sshort[1] ) / 2) );
          src += element_size;
          dest += 2;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assertion only
    } else if( width == 1 ) {
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 );
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          short[] sshort = new short[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            sshort[0] = Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
            datain.position( src + ysize );
            sshort[1] = Mipmap.GLU_SWAP_2_BYTES( datain.getShort() );
          } else {
            datain.position( src );
            sshort[0] = datain.getShort();
            datain.position( src + ysize );
            sshort[1] = datain.getShort();
          }
          dataout.put( (short)(( sshort[0] + sshort[1] ) / 2) );
          src += element_size;
          dest += 2;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
      assert( src == ysize * height );
    }
    assert( dest == ( components * element_size * halfWidth * halfHeight ) );
  }
  
  public static void halveImage_uint( int components, int width, int height,
                          ByteBuffer datain, IntBuffer dataout, int element_size,
                          int ysize, int group_size, boolean myswap_bytes ) {
    int i, j, k, l;
    int newwidth, newheight;
    int s = 0;
    int t = 0;
    double temp = 0;
    
    // handle case where there is only 1 column/row
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) ); // can't be 1x1
      halve1Dimage_uint( components, width, height, datain, dataout, element_size,
                                ysize, group_size, myswap_bytes );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    
    // Piece of cake
    if( !myswap_bytes ) {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            datain.position( t );
            temp = (0x000000007FFFFFFFL & datain.getInt() );
            datain.position( t + group_size );
            temp += (0x000000007FFFFFFFL & datain.getInt() );
            datain.position( t + ysize );
            temp += (0x000000007FFFFFFFL & datain.getInt() );
            datain.position( t + ysize + group_size );
            temp += (0x000000007FFFFFFFL & datain.getInt() );
            dataout.put( (int)( ( temp / 4 ) + 0.5 ) );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    } else {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            // need to cast to double to hold large unsigned ints
            double buf;
            datain.position( t );
            buf = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( t + group_size );
            buf += ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( t + ysize );
            buf += ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( t + ysize + group_size );
            buf += ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            temp /= 4;
            temp += 0.5;
            dataout.put( (int)temp );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    }
  }
  
  public static void halve1Dimage_uint( int components, int width, int height,
                      ByteBuffer datain, IntBuffer dataout, int element_size, int ysize,
                      int group_size, boolean myswap_bytes ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int dest = 0;
    int jj;
    
    assert( width == 1 || height == 1 ); // must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // widthxheight can't be 1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < halfHeight; kk++ ) {
          long[] uint = new long[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( src + group_size );
            uint[1] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
          } else {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & datain.getInt() );
            datain.position( src + group_size );
            uint[1] = (0x00000000FFFFFFFF & datain.getInt() );
          }
          dataout.put( (int)( ( uint[0] + uint[1] ) / 2.0 ) );
          src += element_size;
          dest += 4;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assertion only
    } else if( width == 1 ) { // 1 column
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 ); // widthxheight can't be 1
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          long[] uint = new long[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( src + group_size );
            uint[0] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
          } else {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & datain.getInt() );
            datain.position( src + ysize );
            uint[1] = ( 0x00000000FFFFFFFF & datain.getInt() );
          }
          dataout.put( (int)( ( uint[0] + uint[1] ) / 2.0 ) );
          src += element_size;
          dest += 4;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
      assert( src == ysize * height );
    }
    assert( dest == components * element_size * halfWidth * halfHeight );
  }
  
  public static void halveImage_int( int components, int width, int height,
                        ByteBuffer datain, IntBuffer dataout, int element_size,
                        int ysize, int group_size, boolean myswap_bytes ) {
    int i, j, k, l;
    int newwidth, newheight;
    int s = 0;
    int t = 0;
    int temp = 0;
    
    // handle case where there is only 1 column/row
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) ); // can't be 1x1
      halve1Dimage_int( components, width, height, datain, dataout, element_size,
                            ysize, group_size, myswap_bytes );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    
    // Piece of cake
    if( !myswap_bytes ) {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            datain.position( t );
            temp = datain.getInt();
            datain.position( t + group_size );
            temp += datain.getInt();
            datain.position( t + ysize );
            temp += datain.getInt();
            datain.position( t + ysize + group_size );
            temp += datain.getInt();
            temp = (int)( ( temp / 4.0f ) + 0.5f );
            dataout.put( temp );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    } else {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            long b;
            float buf;
            datain.position( t );
            b = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            buf = b;
            datain.position( t + group_size );
            b = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            buf += b;
            datain.position( t + ysize );
            b = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            buf += b;
            datain.position( t + ysize + group_size );
            b = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            buf += b;
            dataout.put( (int)( ( buf / 4.0f ) + 0.5f ) );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    }
  }
  
  public static void halve1Dimage_int( int components, int width, int height,
              ByteBuffer datain, IntBuffer dataout, int element_size, int ysize,
              int group_size, boolean myswap_bytes ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int dest = 0;
    int jj;
    
    assert( width == 1 || height == 1 ); // must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // can't be 1x1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          long[] uint = new long[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( src + group_size );
            uint[1] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
          } else {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & datain.getInt() );
            datain.position( src + group_size );
            uint[1] = ( 0x00000000FFFFFFFF & datain.getInt() );
          }
          dataout.put( (int)( ( (float)uint[0] + (float)uint[1] ) / 2.0f) );
          src += element_size;
          dest += 4;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assertion only
    } else if( width == 1 ) {
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 );
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          long[] uint = new long[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
            datain.position( src + ysize );
            uint[1] = ( 0x00000000FFFFFFFF & Mipmap.GLU_SWAP_4_BYTES( datain.getInt() ) );
          } else {
            datain.position( src );
            uint[0] = ( 0x00000000FFFFFFFF & datain.getInt() );
            datain.position( src + ysize );
            uint[1] = ( 0x00000000FFFFFFFF & datain.getInt() );
          }
          dataout.put( (int)(( (float)uint[0] + (float)uint[1] ) / 2.0f) );
          src += element_size;
          dest += 4;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
      assert( src == ysize * height );
    }
    assert( dest == ( components * element_size * halfWidth * halfHeight ) );
  }
  
  public static void halveImage_float( int components, int width, int height,
                    ByteBuffer datain, FloatBuffer dataout, int element_size,
                    int ysize, int group_size, boolean myswap_bytes ) {
    int i, j, k, l;
    int newwidth, newheight;
    int s = 0;
    int t = 0;
    float temp = 0.0f;
    // handle case where there is only 1 column/row
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) ); // can't be 1x1
      halve1Dimage_float( components, width, height, datain, dataout, element_size,
                                              ysize, group_size, myswap_bytes );
      return;
    }
    
    newwidth = width / 2;
    newheight = height / 2;
    
    // Piece of cake
    if( !myswap_bytes ) {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            datain.position( t );
            temp = datain.getFloat();
            datain.position( t + group_size );
            temp += datain.getFloat();
            datain.position( t + ysize );
            temp += datain.getFloat();
            datain.position( t + ysize + group_size );
            temp /= 4.0f;
            dataout.put( temp );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    } else {
      for( i = 0; i < newheight; i++ ) {
        for( j = 0; j < newwidth; j++ ) {
          for( k = 0; k < components; k++ ) {
            float buf;
            datain.position( t );
            buf = Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
            datain.position( t + group_size );
            buf += Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
            datain.position( t + ysize );
            buf += Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
            datain.position( t + ysize + group_size );
            buf += Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
            dataout.put( buf / 4.0f );
            t += element_size;
          }
          t += group_size;
        }
        t += ysize;
      }
    }
  }
  
  public static void halve1Dimage_float( int components, int width, int height,
              ByteBuffer datain, FloatBuffer dataout, int element_size, int ysize,
              int group_size, boolean myswap_bytes ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int dest = 0;
    int jj;
    
    assert( width == 1 || height == 1 ); // must be 1D
    assert( width != height ); // can't be square
    
    if( height == 1 ) { // 1 row
      assert( width != 1 ); // can't be 1x1
      halfHeight = 1;
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          float[] sfloat = new float[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            sfloat[0] = Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
            datain.position( src + group_size );
            sfloat[1] = Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
          } else {
            datain.position( src );
            sfloat[0] = datain.getFloat();
            datain.position( src + group_size );
            sfloat[1] = datain.getFloat();
          }
          dataout.put( (sfloat[0] + sfloat[1]) / 2.0f );
          src += element_size;
          dest += 4;
        }
        src += group_size; // skip to next 2
      }
      int padBytes = ysize - ( width * group_size );
      src += padBytes; // for assertion only
    } else if( width == 1 ) {
      int padBytes = ysize - ( width * group_size );
      assert( height != 1 );
      halfWidth = 1;
      // one vertical column with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        int kk;
        for( kk = 0; kk < components; kk++ ) {
          float[] sfloat = new float[BOX2];
          if( myswap_bytes ) {
            datain.position( src );
            sfloat[0] = Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
            datain.position( src + ysize );
            sfloat[1] = Mipmap.GLU_SWAP_4_BYTES( datain.getFloat() );
          } else {
            datain.position( src );
            sfloat[0] = datain.getFloat();
            datain.position( src + ysize );
            sfloat[1] = datain.getFloat();
          }
          dataout.put( ( sfloat[0] + sfloat[1] ) / 2.0f );
          src += element_size;
          dest += 4;
        }
        src += padBytes; // add pad bytes, if any, to get to end of row
        src += ysize;
      }
      assert( src == ysize * height );
    }
    assert( dest == ( components * element_size * halfWidth * halfHeight ) );
  }
  
  public static void halveImagePackedPixel( int components, Extract extract, int width, 
          int height, ByteBuffer datain, ByteBuffer dataout, 
          int pixelSizeInBytes, int rowSizeInBytes, boolean isSwap ) {
    if( width == 1 || height == 1 ) {
      assert( !( width == 1 && height == 1 ) );
      halve1DimagePackedPixel( components, extract, width, height, datain, dataout,
                          pixelSizeInBytes, rowSizeInBytes, isSwap );
      return;
    }
    int ii, jj;
    
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes );
    int outIndex = 0;
    
    for( ii = 0; ii < halfHeight; ii++ ) {
      for( jj = 0; jj < halfWidth; jj++ ) {
        float totals[] = new float[4];
        float extractTotals[][] = new float[BOX4][4];
        int cc;
        
        datain.position( src );
        extract.extract( isSwap, datain, extractTotals[0] );
        datain.position( src + pixelSizeInBytes );
        extract.extract( isSwap, datain, extractTotals[1] );
        datain.position( src + rowSizeInBytes );
        extract.extract( isSwap, datain, extractTotals[2] );
        datain.position( src + rowSizeInBytes + pixelSizeInBytes );
        extract.extract( isSwap, datain, extractTotals[3] );
        for( cc = 0; cc < components; cc++ ) {
          int kk = 0;
          // grab 4 pixels to average
          totals[cc] = 0.0f;
          for( kk = 0; kk < BOX4; kk++ ) {
            totals[cc] += extractTotals[kk][cc];
          }
          totals[cc] /= BOX4;
        }
        extract.shove( totals, outIndex, dataout );
        outIndex++;
        src += pixelSizeInBytes + pixelSizeInBytes;
      }
      // skip past pad bytes, if any, to get to next row
      src += padBytes;
      src += rowSizeInBytes;
    }
    assert( src == rowSizeInBytes * height );
    assert( outIndex == halfWidth * halfHeight );
  }
  
  public static void halve1DimagePackedPixel( int components, Extract extract, int width,
              int height, ByteBuffer datain, ByteBuffer dataout,
              int pixelSizeInBytes, int rowSizeInBytes, boolean isSwap ) {
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int src = 0;
    int jj;
    
    assert( width == 1 || height == 1 );
    assert( width != height );
    
    if( height == 1 ) {
      int outIndex = 0;
      
      assert( width != 1 );
      halfHeight = 1;
      
      // one horizontal row with possible pad bytes
      
      for( jj = 0; jj < halfWidth; jj++ ) {
        float[] totals = new float[4];
        float[][] extractTotals = new float[BOX2][4];
        int cc;
        
        datain.position( src );
        extract.extract( isSwap, datain, extractTotals[0] );
        datain.position( src + pixelSizeInBytes );
        extract.extract( isSwap, datain, extractTotals[1] );
        for( cc = 0; cc < components; cc++ ) {
          int kk = 0;
          // grab 4 pixels to average
          totals[cc] = 0.0f;
          for( kk = 0; kk < BOX2; kk++ ) {
            totals[cc] += extractTotals[kk][cc];
          }
          totals[cc] /= BOX2;
        }
        extract.shove( totals, outIndex, dataout );
        outIndex++;
        // skip over to next group of 2
        src += pixelSizeInBytes + pixelSizeInBytes;
      }
      int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes );
      src += padBytes;
      
      assert( src == rowSizeInBytes );
      assert( outIndex == halfWidth * halfHeight );
    } else if( width == 1 ) {
      int outIndex = 0;
      
      assert( height != 1 );
      halfWidth = 1;
      // one vertical volumn with possible pad bytes per row
      // average two at a time
      
      for( jj = 0; jj < halfHeight; jj++ ) {
        float[] totals = new float[4];
        float[][] extractTotals = new float[BOX2][4];
        int cc;
        // average two at a time, instead of four
        datain.position( src );
        extract.extract( isSwap, datain, extractTotals[0] );
        datain.position( src + rowSizeInBytes );
        extract.extract( isSwap, datain, extractTotals[1] );
        for( cc = 0; cc < components; cc++ ) {
          int kk = 0;
          // grab 4 pixels to average
          totals[cc] = 0.0f;
          for( kk = 0; kk < BOX2; kk++ ) {
            totals[cc] += extractTotals[kk][cc];
          }
          totals[cc] /= BOX2;
        }
        extract.shove( totals, outIndex, dataout );
        outIndex++;
        // skip over to next group of 2
        src += rowSizeInBytes + rowSizeInBytes;
      }
      assert( src == rowSizeInBytes );
      assert( outIndex == halfWidth * halfHeight );
    }
  }
  
  public static void halveImagePackedPixelSlice( int components, Extract extract,
          int width, int height, int depth, ByteBuffer dataIn,
          ByteBuffer dataOut, int pixelSizeInBytes, int rowSizeInBytes,
          int imageSizeInBytes, boolean isSwap ) {
    int ii, jj;
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int halfDepth = depth / 2;
    int src = 0;
    int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes );
    int outIndex = 0;
    
    assert( (width == 1 || height == 1) && depth >= 2 );
    
    if( width == height ) {
      assert( width == 1 && height == 1 );
      assert( depth >= 2 );
      
      for( ii = 0; ii < halfDepth; ii++ ) {
        float totals[] = new float[4];
        float extractTotals[][] = new float[BOX2][4];
        int cc;
        
        dataIn.position( src );
        extract.extract( isSwap, dataIn, extractTotals[0] );
        dataIn.position( src + imageSizeInBytes );
        extract.extract( isSwap, dataIn, extractTotals[1] );
        
        for( cc = 0; cc < components; cc++ ) {
          int kk;
          
          // average only 2 pixels since a column
          totals[cc]= 0.0f;
          for( kk = 0; kk < BOX2; kk++ ) {
            totals[cc] += extractTotals[kk][cc];
          }
          totals[cc] /= BOX2;
        } // for cc
        
        extract.shove( totals, outIndex, dataOut );
        outIndex++;
        // skip over to next group of 2
        src += imageSizeInBytes + imageSizeInBytes;
      } // for ii
    } else if( height == 1 ) {
      assert( width != 1 );
      
      for( ii = 0; ii < halfDepth; ii++ ) {
        for( jj = 0; jj < halfWidth; jj++ ) {
          float totals[] = new float[4];
          float extractTotals[][] = new float[BOX4][4];
          int cc;
          
          dataIn.position( src );
          extract.extract( isSwap, dataIn, extractTotals[0] );
          dataIn.position( src + pixelSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[1] );
          dataIn.position( src + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[2] );
          dataIn.position( src + pixelSizeInBytes + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[3] );
          
          for( cc = 0; cc < components; cc++ ) {
            int kk;
            
            // grab 4 pixels to average
            totals[cc] = 0.0f;
            for( kk = 0; kk < BOX4; kk++ ) {
              totals[cc]+= extractTotals[kk][cc];
            }
            totals[cc]/= (float)BOX4;
          }
          extract.shove( totals, outIndex, dataOut );
          outIndex++;
          // skip over to next horizontal square of 4
          src += imageSizeInBytes + imageSizeInBytes;
        }
      }
    } else if( width == 1 ) {
      assert( height != 1 );
      
      for( ii = 0; ii < halfDepth; ii++ ) {
        for( jj = 0; jj < halfWidth; jj++ ) {
          float totals[] = new float[4];
          float extractTotals[][] = new float[BOX4][4];
          int cc;
          
          dataIn.position( src );
          extract.extract( isSwap, dataIn, extractTotals[0] );
          dataIn.position( src + rowSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[1] );
          dataIn.position( src + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[2] );
          dataIn.position( src + rowSizeInBytes + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[3] );
          
          for( cc = 0; cc < components; cc++ ) {
            int kk;
            
            // grab 4 pixels to average
            totals[cc] = 0.0f;
            for( kk = 0; kk < BOX4; kk++ ) {
              totals[cc]+= extractTotals[kk][cc];
            }
            totals[cc]/= (float)BOX4;
          }
          extract.shove( totals, outIndex, dataOut );
          outIndex++;
          // skip over to next horizontal square of 4
          src += imageSizeInBytes + imageSizeInBytes;
        }
      }
    }
  }
  
  public static void halveImageSlice( int components, ExtractPrimitive extract, int width,
          int height, int depth, ByteBuffer dataIn, ByteBuffer dataOut,
          int elementSizeInBytes, int groupSizeInBytes, int rowSizeInBytes,
          int imageSizeInBytes, boolean isSwap ) {
    int ii, jj;
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int halfDepth = depth / 2;
    int src = 0;
    int padBytes = rowSizeInBytes - ( width * groupSizeInBytes );
    int outIndex = 0;
    
    assert( (width == 1 || height == 1) && depth >= 2 );
    
    if( width == height ) {
      assert( width == 1 && height == 1 );
      assert( depth >= 2 );
      
      for( ii = 0; ii < halfDepth; ii++ ) {
        int cc;
        for( cc = 0; cc < components; cc++ ) {
          double[] totals = new double[4];
          double[][] extractTotals = new double[BOX2][4];
          int kk;
          
          dataIn.position( src );
          extractTotals[0][cc] = extract.extract( isSwap, dataIn );
          dataIn.position( src + imageSizeInBytes );
          extractTotals[1][cc] = extract.extract( isSwap, dataIn );
          
          // average 2 pixels since only a column
          totals[cc] = 0.0f;
          // totals[red] = extractTotals[0][red] + extractTotals[1][red];
          // totals[red] = red / 2;
          for( kk = 0; kk < BOX2; kk++ ) {
            totals[cc] += extractTotals[kk][cc];
          }
          totals[cc] /= (double)BOX2;
          
          extract.shove( totals[cc], outIndex, dataOut );
          outIndex++;
          src += elementSizeInBytes;
        } // for cc
        // skip over next group of 2
        src += rowSizeInBytes;
      } // for ii
      
      assert( src == rowSizeInBytes * height * depth );
      assert( outIndex == halfDepth * components );
    } else if( height == 1 ) {
      assert( width != 1 );
      
      for( ii = 0; ii < halfDepth; ii++ ) {
        for( jj = 0; jj < halfWidth; jj++ ) {
          int cc;
          for( cc = 0; cc < components; cc++ ) {
            int kk;
            double totals[] = new double[4];
            double extractTotals[][] = new double[BOX4][4];
            
            dataIn.position( src );
            extractTotals[0][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + groupSizeInBytes );
            extractTotals[1][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + imageSizeInBytes );
            extractTotals[2][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + imageSizeInBytes + groupSizeInBytes );
            extractTotals[3][cc] = extract.extract( isSwap, dataIn );
            
            // grab 4 pixels to average
            totals[cc] = 0.0f;
            // totals[red] = extractTotals[0][red] + extractTotals[1][red] +
            //               extractTotals[2][red] + extractTotals[3][red];
            // totals[red] /= (double)BOX4;
            for( kk = 0; kk < BOX4; kk++ ) {
              totals[cc] += extractTotals[kk][cc];
            }
            totals[cc] /= (double)BOX4;
            
            extract.shove( totals[cc], outIndex, dataOut );
            outIndex++;
            src += elementSizeInBytes;
          } // for cc
          // skip over to next horizontal square of 4
          src += elementSizeInBytes;
        } // for jj
        src += padBytes;
        src += rowSizeInBytes;
      } // for ii
      assert( src == rowSizeInBytes * height * depth );
      assert( outIndex == halfWidth * halfDepth * components );
    } else if( width == 1 ) {
      assert( height != 1 );
      
      for( ii = 0; ii < halfDepth; ii++ ) {
        for( jj = 0; jj < halfHeight; jj++ ) {
          int cc;
          for( cc = 0; cc < components; cc++ ) {
            int kk;
            double totals[] = new double[4];
            double extractTotals[][] = new double[BOX4][4];
            
            dataIn.position( src );
            extractTotals[0][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + rowSizeInBytes );
            extractTotals[1][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + imageSizeInBytes );
            extractTotals[2][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + imageSizeInBytes + groupSizeInBytes );
            extractTotals[3][cc] = extract.extract( isSwap, dataIn );
            
            
            // grab 4 pixels to average
            totals[cc] = 0.0f;
            // totals[red] = extractTotals[0][red] + extractTotals[1][red] +
            //               extractTotals[2][red] + extractTotals[3][red];
            // totals[red] /= (double)BOX4;
            for( kk = 0; kk < BOX4; kk++ ) {
              totals[cc] += extractTotals[kk][cc];
            }
            totals[cc] /= (double)BOX4;
            
            extract.shove( totals[cc], outIndex, dataOut );
            outIndex++;
            src += elementSizeInBytes;
          } // for cc
          // skip over to next horizontal square of 4
          src += padBytes;
          src += rowSizeInBytes;
        } // for jj
        src += imageSizeInBytes;
      } // for ii
      assert( src == rowSizeInBytes * height * depth );
      assert( outIndex == halfWidth * halfDepth * components );
    }
  }
  
  public static void halveImage3D( int components, ExtractPrimitive extract,
          int width, int height, int depth, ByteBuffer dataIn, ByteBuffer dataOut,
          int elementSizeInBytes, int groupSizeInBytes, int rowSizeInBytes,
          int imageSizeInBytes, boolean isSwap ) {
    assert( depth > 1 );
    
    // horizontal/vertical/onecolumn slice viewed from top
    if( width == 1 || height == 1 ) {
      assert( 1 <= depth );
      
      halveImageSlice( components, extract, width, height, depth, dataIn, dataOut,
              elementSizeInBytes, groupSizeInBytes, rowSizeInBytes, imageSizeInBytes,
              isSwap );
      return;
    }
    
    int ii, jj, dd;
    
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int halfDepth = depth / 2;
    int src = 0;
    int padBytes = rowSizeInBytes - ( width * groupSizeInBytes );
    int outIndex = 0;
    
    for( dd = 0; dd < halfDepth; dd++ ) {
      for( ii = 0; ii < halfHeight; ii++ ) {
        for( jj = 0; jj < halfWidth; jj++ ) {
          int cc;
          for( cc = 0; cc < components; cc++ ) {
            int kk;
            double totals[] = new double[4];
            double extractTotals[][] = new double[BOX8][4];
            
            dataIn.position( src );
            extractTotals[0][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + groupSizeInBytes );
            extractTotals[1][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + rowSizeInBytes );
            extractTotals[2][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + rowSizeInBytes + groupSizeInBytes );
            extractTotals[3][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + imageSizeInBytes );
            extractTotals[4][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + groupSizeInBytes + imageSizeInBytes );
            extractTotals[5][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + rowSizeInBytes + imageSizeInBytes );
            extractTotals[6][cc] = extract.extract( isSwap, dataIn );
            dataIn.position( src + rowSizeInBytes + imageSizeInBytes + groupSizeInBytes );
            extractTotals[7][cc] = extract.extract( isSwap, dataIn );
            
            totals[cc] = 0.0f;
            
            for( kk = 0; kk < BOX8; kk++ ) {
              totals[cc] += extractTotals[kk][cc];
            }
            totals[cc] /= (double)BOX8;
            
            extract.shove( totals[cc], outIndex, dataOut );
            outIndex++;
            
            src += elementSizeInBytes;
          } // for cc
          // skip over to next square of 4
          src += groupSizeInBytes;
        } // for jj
        // skip past pad bytes, if any, to get to next row
        src += padBytes;
        src += rowSizeInBytes;
      } // for ii
      src += imageSizeInBytes;
    } // for dd
    assert( src == rowSizeInBytes * height * depth );
    assert( outIndex == halfWidth * halfHeight * halfDepth * components );
  }
  
  public static void halveImagePackedPixel3D( int components, Extract extract,
          int width, int height, int depth, ByteBuffer dataIn, 
          ByteBuffer dataOut, int pixelSizeInBytes, int rowSizeInBytes,
          int imageSizeInBytes, boolean isSwap ) {
    if( depth == 1 ) {
      assert( 1 <= width && 1 <= height );
      
      halveImagePackedPixel( components, extract, width, height, dataIn, dataOut,
              pixelSizeInBytes, rowSizeInBytes, isSwap );
      return;
    } else if( width == 1 || height == 1 ) { // a horizontal or vertical slice viewed from top
      assert( 1 <= depth );
      
      halveImagePackedPixelSlice( components, extract, width, height, depth, dataIn,
              dataOut, pixelSizeInBytes, rowSizeInBytes, imageSizeInBytes, isSwap );
      return;
    }
    int ii, jj, dd;
    
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int halfDepth = depth / 2;
    int src = 0;
    int padBytes = rowSizeInBytes - ( width * pixelSizeInBytes );
    int outIndex = 0;
    
    for( dd = 0; dd < halfDepth; dd++ ) {
      for( ii = 0; ii < halfHeight; ii++ ) {
        for( jj = 0; jj < halfWidth; jj++ ) {
          float totals[] = new float[4]; // 4 is max components
          float extractTotals[][] = new float[BOX8][4];
          int cc;
          
          dataIn.position( src );
          extract.extract( isSwap, dataIn, extractTotals[0] );
          dataIn.position( src + pixelSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[1] );
          dataIn.position( src + rowSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[2] );
          dataIn.position( src + rowSizeInBytes + pixelSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[3] );
          dataIn.position( src + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[4] );
          dataIn.position( src + pixelSizeInBytes + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[5] );
          dataIn.position( src + rowSizeInBytes + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[6] );
          dataIn.position( src + rowSizeInBytes + pixelSizeInBytes + imageSizeInBytes );
          extract.extract( isSwap, dataIn, extractTotals[7] );
          
          for( cc = 0; cc < components; cc++ ) {
            int kk;
            // grab 8 pixels to average
            totals[cc] = 0.0f;
            for( kk = 0; kk < BOX8; kk++ ) {
              totals[cc] += extractTotals[kk][cc];
            }
            totals[cc] /= (float)BOX8;
          }
          extract.shove( totals, outIndex, dataOut );
          outIndex++;
          // skip over to next square of 4
          src += pixelSizeInBytes + pixelSizeInBytes;
        }
        // skip past pad bytes, if any, to get to next row
        src += padBytes;
        src += rowSizeInBytes;
      }
      src += imageSizeInBytes;
    }
    assert( src == rowSizeInBytes * height * depth );
    assert( outIndex == halfWidth * halfHeight * halfDepth );
  }
}
