Package org.newdawn.slick

Source Code of org.newdawn.slick.BigImage

package org.newdawn.slick;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.newdawn.slick.opengl.ImageData;
import org.newdawn.slick.opengl.ImageDataFactory;
import org.newdawn.slick.opengl.LoadableImageData;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.util.OperationNotSupportedException;
import org.newdawn.slick.util.ResourceLoader;

/**
* An image implementation that handles loaded images that are larger than the
* maximum texture size supported by the card. In most cases it makes sense
* to make sure all of your image resources are less than 512x512 in size when
* using OpenGL. However, in the rare circumstances where this isn't possible
* this implementation can be used to draw a tiled version of the image built
* from several smaller textures.
*
* This implementation does come with limitations and some performance impact
* however - so use only when absolutely required.
*
* TODO: The code in here isn't pretty, really needs revisiting with a comment stick.
*
* @author kevin
*/
public class BigImage extends Image {
  /** The renderer to use for all GL operations */
  protected static SGL GL = Renderer.get();
 
  /**
   * Get the maximum size of an image supported by the underlying
   * hardware.
   *
   * @return The maximum size of the textures supported by the underlying
   * hardware.
   */
  public static final int getMaxSingleImageSize() {
    IntBuffer buffer = BufferUtils.createIntBuffer(16);
    GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, buffer);
   
    return buffer.get(0);
  }
 
  /** The last image that we put into "in use" mode */
  private static Image lastBind;
 
  /** The images building up this sub-image */
  private Image[][] images;
  /** The number of images on the xaxis */
  private int xcount;
  /** The number of images on the yaxis */
  private int ycount;
  /** The real width of the whole image - maintained even when scaled */
  private int realWidth;
  /** The real hieght of the whole image - maintained even when scaled */
  private int realHeight;
 
  /**
   * Create a new big image. Empty contructor for cloning only
   */
  private BigImage() {
    inited = true;
  }
 
  /**
   * Create a new big image by loading it from the specified reference
   *
   * @param ref The reference to the image to load
   * @throws SlickException Indicates we were unable to locate the resource
   */
  public BigImage(String ref) throws SlickException {
    this(ref, Image.FILTER_NEAREST);
  }

  /**
   * Create a new big image by loading it from the specified reference
   *
   * @param ref The reference to the image to load
   * @param filter The image filter to apply (@see #Image.FILTER_NEAREST)
   * @throws SlickException Indicates we were unable to locate the resource
   */
  public BigImage(String ref,int filter) throws SlickException {
   
    build(ref, filter, getMaxSingleImageSize());
  }

  /**
   * Create a new big image by loading it from the specified reference
   *
   * @param ref The reference to the image to load
   * @param filter The image filter to apply (@see #Image.FILTER_NEAREST)
   * @param tileSize The maximum size of the tiles to use to build the bigger image
   * @throws SlickException Indicates we were unable to locate the resource
   */
  public BigImage(String ref, int filter, int tileSize) throws SlickException {
    build(ref, filter, tileSize);
  }

  /**
   * Create a new big image by loading it from the specified image data
   *
   * @param data The pixelData to use to create the image
   * @param imageBuffer The buffer containing texture data
   * @param filter The image filter to apply (@see #Image.FILTER_NEAREST)
   */
  public BigImage(LoadableImageData data, ByteBuffer imageBuffer, int filter) {
    build(data, imageBuffer, filter, getMaxSingleImageSize());
  }
 
  /**
   * Create a new big image by loading it from the specified image data
   *
   * @param data The pixelData to use to create the image
   * @param imageBuffer The buffer containing texture data
   * @param filter The image filter to apply (@see #Image.FILTER_NEAREST)
   * @param tileSize The maximum size of the tiles to use to build the bigger image
   */
  public BigImage(LoadableImageData data, ByteBuffer imageBuffer, int filter, int tileSize) {
    build(data, imageBuffer, filter, tileSize);
  }

  /**
   * Get a sub tile of this big image. Useful for debugging
   *
   * @param x The x tile index
   * @param y The y tile index
   * @return The image used for this tile
   */
  public Image getTile(int x, int y) {
    return images[x][y];
  }
 
  /**
   * Create a new big image by loading it from the specified reference
   *
   * @param ref The reference to the image to load
   * @param filter The image filter to apply (@see #Image.FILTER_NEAREST)
   * @param tileSize The maximum size of the tiles to use to build the bigger image
   * @throws SlickException Indicates we were unable to locate the resource
   */
  private void build(String ref, int filter, int tileSize) throws SlickException {
    try {
      final LoadableImageData data = ImageDataFactory.getImageDataFor(ref);
      final ByteBuffer imageBuffer = data.loadImage(ResourceLoader.getResourceAsStream(ref), false, null);
      build(data, imageBuffer, filter, tileSize);
    } catch (IOException e) {
      throw new SlickException("Failed to load: "+ref, e);
    }
  }
 
  /**
   * Create an big image from a image data source.
   *
   * @param data The pixelData to use to create the image
   * @param imageBuffer The buffer containing texture data
   * @param filter The filter to use when scaling this image
   * @param tileSize The maximum size of the tiles to use to build the bigger image
   */
  private void build(final LoadableImageData data, final ByteBuffer imageBuffer, int filter, int tileSize) {
    final int dataWidth = data.getTexWidth();
    final int dataHeight = data.getTexHeight();
   
    realWidth = width = data.getWidth();
    realHeight = height = data.getHeight();
   
    if ((dataWidth <= tileSize) && (dataHeight <= tileSize)) {
      images = new Image[1][1];
      ImageData tempData = new ImageData() {
        public int getDepth() {
          return data.getDepth();
        }

        public int getHeight() {
          return dataHeight;
        }

        public ByteBuffer getImageBufferData() {
          return imageBuffer;
        }

        public int getTexHeight() {
          return dataHeight;
        }

        public int getTexWidth() {
          return dataWidth;
        }

        public int getWidth() {
          return dataWidth;
        }
      };
      images[0][0] = new Image(tempData, filter);
      xcount = 1;
      ycount = 1;
      inited = true;
      return;
    }
   
    xcount = ((realWidth-1) / tileSize) + 1;
    ycount = ((realHeight-1) / tileSize) + 1;
   
    images = new Image[xcount][ycount];
    int components = data.getDepth() / 8;
   
    for (int x=0;x<xcount;x++) {
      for (int y=0;y<ycount;y++) {
        int finalX = ((x+1) * tileSize);
        int finalY = ((y+1) * tileSize);
        final int imageWidth = tileSize;
        final int imageHeight = tileSize;
       
        final int xSize = Math.min(dataWidth, imageWidth);
        final int ySize = Math.min(dataHeight, imageHeight);
       
        final ByteBuffer subBuffer = BufferUtils.createByteBuffer(tileSize*tileSize*components);
        int xo = x*tileSize*components;

        byte[] byteData = new byte[xSize*components];
        for (int i=0;i<ySize;i++) {
          int yo = (((y * tileSize) + i) * dataWidth) * components;
          imageBuffer.position(yo+xo);
         
          imageBuffer.get(byteData, 0, xSize*components);
          subBuffer.put(byteData);
        }
       
        subBuffer.flip();
        ImageData imgData = new ImageData() {
          public int getDepth() {
            return data.getDepth();
          }

          public int getHeight() {
            return imageHeight;
          }

          public int getWidth() {
            return imageWidth;
          }
         
          public ByteBuffer getImageBufferData() {
            return subBuffer;
          }

          public int getTexHeight() {
            return ySize;
          }

          public int getTexWidth() {
            return xSize;
          }
        };
        images[x][y] = new Image(imgData, filter);
      }
    }
   
    inited = true;
  }
 
  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#bind()
   */
  public void bind() {
    throw new OperationNotSupportedException("Can't bind big images yet");
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#copy()
   */
  public Image copy() {
    throw new OperationNotSupportedException("Can't copy big images yet");
  }
 
  /**
   * @see org.newdawn.slick.Image#draw()
   */
  public void draw() {
    draw(0,0);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, org.newdawn.slick.Color)
   */
  public void draw(float x, float y, Color filter) {
    draw(x,y,width,height,filter);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float, org.newdawn.slick.Color)
   */
  public void draw(float x, float y, float scale, Color filter) {
    draw(x,y,width*scale,height*scale,filter);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float, float, org.newdawn.slick.Color)
   */
  public void draw(float x, float y, float width, float height, Color filter) {
    float sx = width / realWidth;
    float sy = height / realHeight;
   
    GL.glTranslatef(x,y,0);
    GL.glScalef(sx,sy,1);

    float xp = 0;
    float yp = 0;
   
    for (int tx=0;tx<xcount;tx++) {
      yp = 0;
      for (int ty=0;ty<ycount;ty++) {
        Image image = images[tx][ty];

        image.draw(xp,yp,image.getWidth(), image.getHeight(), filter);
     
        yp += image.getHeight();
        if (ty == ycount - 1) {
          xp += image.getWidth();
        }
      }
     
    }
   
    GL.glScalef(1.0f/sx,1.0f/sy,1);
    GL.glTranslatef(-x,-y,0);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float, float, float, float, float, float)
   */
  public void draw(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) {
    int srcwidth = (int) (srcx2 - srcx);
    int srcheight = (int) (srcy2 - srcy);

    Image subImage = getSubImage((int) srcx,(int) srcy,srcwidth,srcheight);

    int width = (int) (x2 - x);
    int height = (int) (y2 - y);

    subImage.draw(x,y,width,height);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float, float, float, float)
   */
  public void draw(float x, float y, float srcx, float srcy, float srcx2, float srcy2) {
    int srcwidth = (int) (srcx2 - srcx);
    int srcheight = (int) (srcy2 - srcy);

    draw(x,y,srcwidth,srcheight,srcx,srcy,srcx2,srcy2);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float, float)
   */
  public void draw(float x, float y, float width, float height) {
    draw(x,y,width,height,Color.white);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float)
   */
  public void draw(float x, float y, float scale) {
    draw(x,y,scale,Color.white);
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float)
   */
  public void draw(float x, float y) {
    draw(x,y,Color.white);
  }

  /**
   * @see org.newdawn.slick.Image#drawEmbedded(float, float, float, float)
   */
  public void drawEmbedded(float x, float y, float width, float height) {
    float sx = width / realWidth;
    float sy = height / realHeight;

    float xp = 0;
    float yp = 0;
   
    for (int tx=0;tx<xcount;tx++) {
      yp = 0;
      for (int ty=0;ty<ycount;ty++) {
        Image image = images[tx][ty];

        if ((lastBind == null) || (image.getTexture() != lastBind.getTexture())) {
          if (lastBind != null) {
            lastBind.endUse();
          }
          lastBind = image;
          lastBind.startUse();
        }
        image.drawEmbedded(xp+x,yp+y,image.getWidth(), image.getHeight());
     
        yp += image.getHeight();
        if (ty == ycount - 1) {
          xp += image.getWidth();
        }
      }
     
    }
  }

  /**
   * @see org.newdawn.slick.Image#drawFlash(float, float, float, float)
   */
  public void drawFlash(float x, float y, float width, float height) {
    float sx = width / realWidth;
    float sy = height / realHeight;
   
    GL.glTranslatef(x,y,0);
    GL.glScalef(sx,sy,1);

    float xp = 0;
    float yp = 0;
   
    for (int tx=0;tx<xcount;tx++) {
      yp = 0;
      for (int ty=0;ty<ycount;ty++) {
        Image image = images[tx][ty];

        image.drawFlash(xp,yp,image.getWidth(), image.getHeight());
     
        yp += image.getHeight();
        if (ty == ycount - 1) {
          xp += image.getWidth();
        }
      }
     
    }
   
    GL.glScalef(1.0f/sx,1.0f/sy,1);
    GL.glTranslatef(-x,-y,0);
  }

  /**
   * @see org.newdawn.slick.Image#drawFlash(float, float)
   */
  public void drawFlash(float x, float y) {
    drawFlash(x,y,width,height);
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#endUse()
   */
  public void endUse() {
    if (lastBind != null) {
      lastBind.endUse();
    }
    lastBind = null;
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#startUse()
   */
  public void startUse() {
  }
 
  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#ensureInverted()
   */
  public void ensureInverted() {
    throw new OperationNotSupportedException("Doesn't make sense for tiled operations");
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#getColor(int, int)
   */
  public Color getColor(int x, int y) {
    throw new OperationNotSupportedException("Can't use big images as buffers");
  }

  /**
   * @see org.newdawn.slick.Image#getFlippedCopy(boolean, boolean)
   */
  public Image getFlippedCopy(boolean flipHorizontal, boolean flipVertical) {
    BigImage image = new BigImage();
   
    image.images = images;
    image.xcount = xcount;
    image.ycount = ycount;
    image.width = width;
    image.height = height;
    image.realWidth = realWidth;
    image.realHeight = realHeight;
   
    if (flipHorizontal) {
      Image[][] images = image.images;
      image.images = new Image[xcount][ycount];
     
      for (int x=0;x<xcount;x++) {
        for (int y=0;y<ycount;y++) {
          image.images[x][y] = images[xcount-1-x][y].getFlippedCopy(true, false);
        }
      }
    }
   
    if (flipVertical) {
      Image[][] images = image.images;
      image.images = new Image[xcount][ycount];
     
      for (int x=0;x<xcount;x++) {
        for (int y=0;y<ycount;y++) {
          image.images[x][y] = images[x][ycount-1-y].getFlippedCopy(false, true);
        }
      }
    }
   
    return image;
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#getGraphics()
   */
  public Graphics getGraphics() throws SlickException {
    throw new OperationNotSupportedException("Can't use big images as offscreen buffers");
  }

  /**
   * @see org.newdawn.slick.Image#getScaledCopy(float)
   */
  public Image getScaledCopy(float scale) {
    return getScaledCopy((int) (scale * width), (int) (scale * height));
  }

  /**
   * @see org.newdawn.slick.Image#getScaledCopy(int, int)
   */
  public Image getScaledCopy(int width, int height) {
    BigImage image = new BigImage();
   
    image.images = images;
    image.xcount = xcount;
    image.ycount = ycount;
    image.width = width;
    image.height = height;
    image.realWidth = realWidth;
    image.realHeight = realHeight;
   
    return image;
  }

  /**
   * @see org.newdawn.slick.Image#getSubImage(int, int, int, int)
   */
  public Image getSubImage(int x, int y, int width, int height) {
    BigImage image = new BigImage();
   
    image.width = width;
    image.height = height;
    image.realWidth = width;
    image.realHeight = height;
    image.images = new Image[this.xcount][this.ycount];

    float xp = 0;
    float yp = 0;
    int x2 = x+width;
    int y2 = y+height;
   
    int startx = 0;
    int starty = 0;
    boolean foundStart = false;
   
    for (int xt=0;xt<xcount;xt++) {
      yp = 0;
      starty = 0;
      foundStart = false;
      for (int yt=0;yt<ycount;yt++) {
        Image current = images[xt][yt];
       
        int xp2 = (int) (xp + current.getWidth());
        int yp2 = (int) (yp + current.getHeight());
       
        // if the top corner of the subimage is inside the area
        // we want or the bottom corrent of the image is, then consider using the
        // image
       
        // this image contributes to the sub image we're attempt to retrieve
        int targetX1 = (int) Math.max(x, xp);
        int targetY1 = (int) Math.max(y, yp);
        int targetX2 = Math.min(x2, xp2);
        int targetY2 = Math.min(y2, yp2);
       
        int targetWidth = targetX2 - targetX1;
        int targetHeight = targetY2 - targetY1;
       
        if ((targetWidth > 0) && (targetHeight > 0)) {
          Image subImage = current.getSubImage((int) (targetX1 - xp), (int) (targetY1 - yp),
                            (targetX2 - targetX1),
                            (targetY2 - targetY1));
          foundStart = true;
          image.images[startx][starty] = subImage;
          starty++;
          image.ycount = Math.max(image.ycount, starty);
        }
       
        yp += current.getHeight();
        if (yt == ycount - 1) {
          xp += current.getWidth();
        }
      }
      if (foundStart) {
        startx++;
        image.xcount++;
      }
    }
   
    return image;
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#getTexture()
   */
  public Texture getTexture() {
    throw new OperationNotSupportedException("Can't use big images as offscreen buffers");
  }

  /**
   * @see org.newdawn.slick.Image#initImpl()
   */
  protected void initImpl() {
    throw new OperationNotSupportedException("Can't use big images as offscreen buffers");
  }

  /**
   * @see org.newdawn.slick.Image#reinit()
   */
  protected void reinit() {
    throw new OperationNotSupportedException("Can't use big images as offscreen buffers");
  }

  /**
   * Not supported in BigImage
   *
   * @see org.newdawn.slick.Image#setTexture(org.newdawn.slick.opengl.Texture)
   */
  public void setTexture(Texture texture) {
    throw new OperationNotSupportedException("Can't use big images as offscreen buffers");
  }
 
  /**
   * Get a sub-image that builds up this image. Note that the offsets
   * used will depend on the maximum texture size on the OpenGL hardware
   *
   * @param offsetX The x position of the image to return
   * @param offsetY The y position of the image to return
   * @return The image at the specified offset into the big image
   */
  public Image getSubImage(int offsetX, int offsetY) {
       return images[offsetX][offsetY];
  }

  /**
   * Get a count of the number images that build this image up horizontally
   *
   * @return The number of sub-images across the big image
   */
  public int getHorizontalImageCount() {
       return xcount;
  }

  /**
   * Get a count of the number images that build this image up vertically
   *
   * @return The number of sub-images down the big image
   */
  public int getVerticalImageCount() {
      return ycount;
  }
 
  /**
   * @see org.newdawn.slick.Image#toString()
   */
  public String toString() {
    return "[BIG IMAGE]";
  }
 
  /**
   * Destroy the image and release any native resources.
   * Calls on a destroyed image have undefined results
   */
  public void destroy() throws SlickException {
    for (int tx=0;tx<xcount;tx++) {
      for (int ty=0;ty<ycount;ty++) {
        Image image = images[tx][ty];
        image.destroy();
      }
    }
  }

  /**
   * @see org.newdawn.slick.Image#draw(float, float, float, float, float, float, float, float, org.newdawn.slick.Color)
   */
  public void draw(float x, float y, float x2, float y2, float srcx,
      float srcy, float srcx2, float srcy2, Color filter) { 
    int srcwidth = (int) (srcx2 - srcx);
    int srcheight = (int) (srcy2 - srcy);

    Image subImage = getSubImage((int) srcx,(int) srcy,srcwidth,srcheight);

    int width = (int) (x2 - x);
    int height = (int) (y2 - y);

    subImage.draw(x,y,width,height,filter);
  }

  /**
   * @see org.newdawn.slick.Image#drawCentered(float, float)
   */
  public void drawCentered(float x, float y) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.newdawn.slick.Image#drawEmbedded(float, float, float, float, float, float, float, float, org.newdawn.slick.Color)
   */
  public void drawEmbedded(float x, float y, float x2, float y2, float srcx,
      float srcy, float srcx2, float srcy2, Color filter) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.newdawn.slick.Image#drawEmbedded(float, float, float, float, float, float, float, float)
   */
  public void drawEmbedded(float x, float y, float x2, float y2, float srcx,
      float srcy, float srcx2, float srcy2) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.newdawn.slick.Image#drawFlash(float, float, float, float, org.newdawn.slick.Color)
   */
  public void drawFlash(float x, float y, float width, float height, Color col) {
    throw new UnsupportedOperationException();
  }

  /**
   * @see org.newdawn.slick.Image#drawSheared(float, float, float, float)
   */
  public void drawSheared(float x, float y, float hshear, float vshear) {
    throw new UnsupportedOperationException();
  }
}
TOP

Related Classes of org.newdawn.slick.BigImage

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.