Package k8

Source Code of k8.Texture

package k8;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import javax.imageio.ImageIO;

import k8.util.LinkedList;
import k8.util.PixelStoreState;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;

public class Texture
{

  private static LinkedList<Texture> textures = new LinkedList<Texture>();

  private int texture_id;

  private LinkedList.Node<Texture> listnode;

  /** Destroys all Texture instances. */
  public static void destroyAll()
  {
    // Call destroy() on all Texture instances
    LinkedList.Node<Texture> curr = null;
    while ((curr = textures.next(curr)) != null)
      curr.item.destroy();
  }

  /** Creates a new instance of Texture */
  public Texture()
  {
    texture_id = 0;
    listnode = null;
  }

  /** Creates a new instance of Texture */
  public Texture(String name)
  {
    URL url = this.getClass().getResource(name);
    BufferedImage tex = null;
    try
    {
      tex = ImageIO.read(url);
    } catch (IOException e)
    {
      k8.logger.warning("Unable to find texture " + name);
      return;
    }
    generate(tex, GL11.GL_RGB4);
  }

  /**
   * Generates the texture.
   *
   * @param image
   *            A BufferedImage
   * @param interanlFormat
   *            Internal format used by OpenGL
   */
  public void generate(BufferedImage image, int internalFormat)
  {
    // Don't proceed if we were given crap data
    if (image == null || internalFormat == 0)
    {
      return;
    }

    // Acquire dimensions and pixel data of image
    int width = image.getWidth();
    int height = image.getHeight();
    byte data[] = (byte[]) image.getRaster().getDataElements(0, 0, width,
        height, null);

    // Put the pixel data of the BufferedImage into a ByteBuffer
    ByteBuffer pixel_buffer = ByteBuffer.allocateDirect(3 * width * height);
    pixel_buffer.put(data);
    pixel_buffer.rewind();

    // Generate the texture
    generate(pixel_buffer, width, height, internalFormat);
  }

  /**
   * Generates the texture.
   *
   * @param pixel_buffer
   *            Pixel data
   * @param width
   *            Width
   * @param height
   *            Height
   * @param interanlFormat
   *            Internal format used by OpenGL
   */
  public void generate(ByteBuffer pixel_buffer, int width, int height,
      int internalFormat)
  {
    // Don't proceed if we were given crap data
    if (pixel_buffer == null || width <= 0 || height <= 0
        || internalFormat == 0)
    {
      return;
    }

    // Destroy any previous generation
    if (texture_id != 0)
    {
      destroy();
    }

    // Make sure the ByteBuffer is rewound
    pixel_buffer.rewind();

    // Create a texture id
    IntBuffer tex_buffer = ByteBuffer.allocateDirect(4).order(
        ByteOrder.nativeOrder()).asIntBuffer();
    GL11.glGenTextures(tex_buffer);
    texture_id = tex_buffer.get(0);

    // Create a MipMapped texture
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture_id);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
        GL11.GL_REPEAT);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
        GL11.GL_REPEAT);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
        GL11.GL_LINEAR);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
        GL11.GL_LINEAR_MIPMAP_LINEAR);
    Texture.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, internalFormat, width,
        height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, pixel_buffer);

    // Add this texture to the list of generated textures
    listnode = textures.append(this);
  }

  /**
   * Builds Mipmaps.
   *
   * @param target
   * @param components
   * @param width
   * @param height
   * @param format
   * @param type
   * @param data
   *
   * @return int
   */
  private static void 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;

    final int bpp = Texture.bytesPerPixel(format, type);

    if (bpp == 0)
      return;

    final int maxSize = PixelStoreState
        .glGetIntegerv(GL11.GL_MAX_TEXTURE_SIZE);

    int w = Texture.nearestPower(width);
    if (w > maxSize)
      w = maxSize;

    int h = Texture.nearestPower(height);
    if (h > maxSize)
      h = maxSize;

    // Get current glPixelStore state
    PixelStoreState pss = new PixelStoreState();

    // set pixel packing
    GL11.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
    GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
    GL11.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
    GL11.glPixelStorei(GL11.GL_PACK_SKIP_PIXELS, 0);

    ByteBuffer image;
    boolean done = false;

    if (w != width || h != height)
    {
      // Must rescale image to get "top" mipmap texture image
      image = BufferUtils.createByteBuffer((w + 4) * h * bpp);
     
      if (!Texture.gluScaleImage(format, width, height, type, data, w, h,
          type, image))
        done = true;

      /* set pixel unpacking */
      GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
      GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
      GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
      GL11.glPixelStorei(GL11.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 */
        GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
        GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
        GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
        GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
      }

      GL11.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;

      if (!Texture.gluScaleImage(format, w, h, type, image, newW, newH,
          type, newImage))
        done = true;

      image = newImage;
      if (bufferB != null)
        bufferB = bufferA;

      w = newW;
      h = newH;
      level++;
    }

    // Restore original glPixelStore state
    pss.save();
  }

  /**
   * Method bytesPerPixel.
   *
   * @param format
   * @param type
   *
   * @return int
   */
  private static int bytesPerPixel(int format, int type)
  {
    int n, m;

    switch (format)
    {
    case GL11.GL_COLOR_INDEX:
    case GL11.GL_STENCIL_INDEX:
    case GL11.GL_DEPTH_COMPONENT:
    case GL11.GL_RED:
    case GL11.GL_GREEN:
    case GL11.GL_BLUE:
    case GL11.GL_ALPHA:
    case GL11.GL_LUMINANCE:
      n = 1;
      break;
    case GL11.GL_LUMINANCE_ALPHA:
      n = 2;
      break;
    case GL11.GL_RGB:
    case GL12.GL_BGR:
      n = 3;
      break;
    case GL11.GL_RGBA:
    case GL12.GL_BGRA:
      n = 4;
      break;
    default:
      n = 0;
    }

    switch (type)
    {
    case GL11.GL_UNSIGNED_BYTE:
      m = 1;
      break;
    case GL11.GL_BYTE:
      m = 1;
      break;
    case GL11.GL_BITMAP:
      m = 1;
      break;
    case GL11.GL_UNSIGNED_SHORT:
      m = 2;
      break;
    case GL11.GL_SHORT:
      m = 2;
      break;
    case GL11.GL_UNSIGNED_INT:
      m = 4;
      break;
    case GL11.GL_INT:
      m = 4;
      break;
    case GL11.GL_FLOAT:
      m = 4;
      break;
    default:
      m = 0;
    }

    return n * m;
  }

  /**
   * Method nearestPower.
   *
   * Compute the nearest power of 2 number. This algorithm is a little
   * strange, but it works quite well.
   *
   * @param value
   *
   * @return int
   */
  private 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 << 2;
      }
      value >>= 1;
      i <<= 1;
    }
  }

  /**
   * Method gluScaleImage.
   *
   * @param format
   * @param widthIn
   * @param heightIn
   * @param typein
   * @param dataIn
   * @param widthOut
   * @param heightOut
   * @param typeOut
   * @param dataOut
   *
   * @return int
   */
  private static boolean gluScaleImage(int format, int widthIn, int heightIn,
      int typein, ByteBuffer dataIn, int widthOut, int heightOut,
      int typeOut, ByteBuffer dataOut)
  {

    final int components = Texture.compPerPix(format);
    if (components == -1)
      return false;

    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 GL11.GL_UNSIGNED_BYTE:
      sizein = 1;
      break;
    case GL11.GL_FLOAT:
      sizein = 4;
      break;
    default:
      return false;
    }

    // Determine bytes per output type
    switch (typeOut)
    {
    case GL11.GL_UNSIGNED_BYTE:
      sizeout = 1;
      break;
    case GL11.GL_FLOAT:
      sizeout = 4;
      break;
    default:
      return false;
    }

    // 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
          * Texture.ceil(components * rowlen * sizein,
              pss.unpackAlignment);

    switch (typein)
    {
    case GL11.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 GL11.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 false;
    }

    // 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
          * Texture.ceil(components * rowlen * sizeout,
              pss.packAlignment);

    switch (typeOut)
    {
    case GL11.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 GL11.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 false;
    }

    return true;
  }

  /**
   * Return ceiling of integer division
   *
   * @param a
   * @param b
   *
   * @return int
   */
  private static int ceil(int a, int b)
  {
    return (a % b == 0 ? a / b : a / b + 1);
  }

  /**
   * Determine number of components per pixel.
   *
   * @param format
   *
   * @return int
   */
  private static int compPerPix(int format)
  {
    switch (format)
    {
    case GL11.GL_COLOR_INDEX:
    case GL11.GL_STENCIL_INDEX:
    case GL11.GL_DEPTH_COMPONENT:
    case GL11.GL_RED:
    case GL11.GL_GREEN:
    case GL11.GL_BLUE:
    case GL11.GL_ALPHA:
    case GL11.GL_LUMINANCE:
      return 1;
    case GL11.GL_LUMINANCE_ALPHA:
      return 2;
    case GL11.GL_RGB:
    case GL12.GL_BGR:
      return 3;
    case GL11.GL_RGBA:
    case GL12.GL_BGRA:
      return 4;
    default:
      return -1;
    }
  }

  /** Deletes the texture from OpenGL */
  public void destroy()
  {
    if (texture_id != 0)
    {
      IntBuffer tex_buf = ByteBuffer.allocateDirect(4).order(
          ByteOrder.nativeOrder()).asIntBuffer();
      tex_buf.put(texture_id);
      tex_buf.rewind();
      GL11.glDeleteTextures(tex_buf);
      texture_id = 0;
      textures.remove(listnode);
    }
  }

  /** Makes current texture for rendering */
  public void glBindTexture()
  {
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture_id);
  }

}
TOP

Related Classes of k8.Texture

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.