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 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