Path: blob/master/SLICK_HOME/src/org/newdawn/slick/BigImage.java
1456 views
package org.newdawn.slick;12import java.io.IOException;3import java.nio.ByteBuffer;4import java.nio.IntBuffer;56import org.lwjgl.BufferUtils;7import org.newdawn.slick.opengl.ImageData;8import org.newdawn.slick.opengl.ImageDataFactory;9import org.newdawn.slick.opengl.LoadableImageData;10import org.newdawn.slick.opengl.Texture;11import org.newdawn.slick.opengl.renderer.SGL;12import org.newdawn.slick.opengl.renderer.Renderer;13import org.newdawn.slick.util.OperationNotSupportedException;14import org.newdawn.slick.util.ResourceLoader;1516/**17* An image implementation that handles loaded images that are larger than the18* maximum texture size supported by the card. In most cases it makes sense19* to make sure all of your image resources are less than 512x512 in size when20* using OpenGL. However, in the rare circumstances where this isn't possible21* this implementation can be used to draw a tiled version of the image built22* from several smaller textures.23*24* This implementation does come with limitations and some performance impact25* however - so use only when absolutely required.26*27* TODO: The code in here isn't pretty, really needs revisiting with a comment stick.28*29* @author kevin30*/31public class BigImage extends Image {32/** The renderer to use for all GL operations */33protected static SGL GL = Renderer.get();3435/**36* Get the maximum size of an image supported by the underlying37* hardware.38*39* @return The maximum size of the textures supported by the underlying40* hardware.41*/42public static final int getMaxSingleImageSize() {43IntBuffer buffer = BufferUtils.createIntBuffer(16);44GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, buffer);4546return buffer.get(0);47}4849/** The last image that we put into "in use" mode */50private static Image lastBind;5152/** The images building up this sub-image */53private Image[][] images;54/** The number of images on the xaxis */55private int xcount;56/** The number of images on the yaxis */57private int ycount;58/** The real width of the whole image - maintained even when scaled */59private int realWidth;60/** The real hieght of the whole image - maintained even when scaled */61private int realHeight;6263/**64* Create a new big image. Empty contructor for cloning only65*/66private BigImage() {67inited = true;68}6970/**71* Create a new big image by loading it from the specified reference72*73* @param ref The reference to the image to load74* @throws SlickException Indicates we were unable to locate the resource75*/76public BigImage(String ref) throws SlickException {77this(ref, Image.FILTER_NEAREST);78}7980/**81* Create a new big image by loading it from the specified reference82*83* @param ref The reference to the image to load84* @param filter The image filter to apply (@see #Image.FILTER_NEAREST)85* @throws SlickException Indicates we were unable to locate the resource86*/87public BigImage(String ref,int filter) throws SlickException {8889build(ref, filter, getMaxSingleImageSize());90}9192/**93* Create a new big image by loading it from the specified reference94*95* @param ref The reference to the image to load96* @param filter The image filter to apply (@see #Image.FILTER_NEAREST)97* @param tileSize The maximum size of the tiles to use to build the bigger image98* @throws SlickException Indicates we were unable to locate the resource99*/100public BigImage(String ref, int filter, int tileSize) throws SlickException {101build(ref, filter, tileSize);102}103104/**105* Create a new big image by loading it from the specified image data106*107* @param data The pixelData to use to create the image108* @param imageBuffer The buffer containing texture data109* @param filter The image filter to apply (@see #Image.FILTER_NEAREST)110*/111public BigImage(LoadableImageData data, ByteBuffer imageBuffer, int filter) {112build(data, imageBuffer, filter, getMaxSingleImageSize());113}114115/**116* Create a new big image by loading it from the specified image data117*118* @param data The pixelData to use to create the image119* @param imageBuffer The buffer containing texture data120* @param filter The image filter to apply (@see #Image.FILTER_NEAREST)121* @param tileSize The maximum size of the tiles to use to build the bigger image122*/123public BigImage(LoadableImageData data, ByteBuffer imageBuffer, int filter, int tileSize) {124build(data, imageBuffer, filter, tileSize);125}126127/**128* Get a sub tile of this big image. Useful for debugging129*130* @param x The x tile index131* @param y The y tile index132* @return The image used for this tile133*/134public Image getTile(int x, int y) {135return images[x][y];136}137138/**139* Create a new big image by loading it from the specified reference140*141* @param ref The reference to the image to load142* @param filter The image filter to apply (@see #Image.FILTER_NEAREST)143* @param tileSize The maximum size of the tiles to use to build the bigger image144* @throws SlickException Indicates we were unable to locate the resource145*/146private void build(String ref, int filter, int tileSize) throws SlickException {147try {148final LoadableImageData data = ImageDataFactory.getImageDataFor(ref);149final ByteBuffer imageBuffer = data.loadImage(ResourceLoader.getResourceAsStream(ref), false, null);150build(data, imageBuffer, filter, tileSize);151} catch (IOException e) {152throw new SlickException("Failed to load: "+ref, e);153}154}155156/**157* Create an big image from a image data source.158*159* @param data The pixelData to use to create the image160* @param imageBuffer The buffer containing texture data161* @param filter The filter to use when scaling this image162* @param tileSize The maximum size of the tiles to use to build the bigger image163*/164private void build(final LoadableImageData data, final ByteBuffer imageBuffer, int filter, int tileSize) {165final int dataWidth = data.getTexWidth();166final int dataHeight = data.getTexHeight();167168realWidth = width = data.getWidth();169realHeight = height = data.getHeight();170171if ((dataWidth <= tileSize) && (dataHeight <= tileSize)) {172images = new Image[1][1];173ImageData tempData = new ImageData() {174public int getDepth() {175return data.getDepth();176}177178public int getHeight() {179return dataHeight;180}181182public ByteBuffer getImageBufferData() {183return imageBuffer;184}185186public int getTexHeight() {187return dataHeight;188}189190public int getTexWidth() {191return dataWidth;192}193194public int getWidth() {195return dataWidth;196}197};198images[0][0] = new Image(tempData, filter);199xcount = 1;200ycount = 1;201inited = true;202return;203}204205xcount = ((realWidth-1) / tileSize) + 1;206ycount = ((realHeight-1) / tileSize) + 1;207208images = new Image[xcount][ycount];209int components = data.getDepth() / 8;210211for (int x=0;x<xcount;x++) {212for (int y=0;y<ycount;y++) {213int finalX = ((x+1) * tileSize);214int finalY = ((y+1) * tileSize);215final int imageWidth = tileSize;216final int imageHeight = tileSize;217218final int xSize = Math.min(dataWidth, imageWidth);219final int ySize = Math.min(dataHeight, imageHeight);220221final ByteBuffer subBuffer = BufferUtils.createByteBuffer(tileSize*tileSize*components);222int xo = x*tileSize*components;223224byte[] byteData = new byte[xSize*components];225for (int i=0;i<ySize;i++) {226int yo = (((y * tileSize) + i) * dataWidth) * components;227imageBuffer.position(yo+xo);228229imageBuffer.get(byteData, 0, xSize*components);230subBuffer.put(byteData);231}232233subBuffer.flip();234ImageData imgData = new ImageData() {235public int getDepth() {236return data.getDepth();237}238239public int getHeight() {240return imageHeight;241}242243public int getWidth() {244return imageWidth;245}246247public ByteBuffer getImageBufferData() {248return subBuffer;249}250251public int getTexHeight() {252return ySize;253}254255public int getTexWidth() {256return xSize;257}258};259images[x][y] = new Image(imgData, filter);260}261}262263inited = true;264}265266/**267* Not supported in BigImage268*269* @see org.newdawn.slick.Image#bind()270*/271public void bind() {272throw new OperationNotSupportedException("Can't bind big images yet");273}274275/**276* Not supported in BigImage277*278* @see org.newdawn.slick.Image#copy()279*/280public Image copy() {281throw new OperationNotSupportedException("Can't copy big images yet");282}283284/**285* @see org.newdawn.slick.Image#draw()286*/287public void draw() {288draw(0,0);289}290291/**292* @see org.newdawn.slick.Image#draw(float, float, org.newdawn.slick.Color)293*/294public void draw(float x, float y, Color filter) {295draw(x,y,width,height,filter);296}297298/**299* @see org.newdawn.slick.Image#draw(float, float, float, org.newdawn.slick.Color)300*/301public void draw(float x, float y, float scale, Color filter) {302draw(x,y,width*scale,height*scale,filter);303}304305/**306* @see org.newdawn.slick.Image#draw(float, float, float, float, org.newdawn.slick.Color)307*/308public void draw(float x, float y, float width, float height, Color filter) {309float sx = width / realWidth;310float sy = height / realHeight;311312GL.glTranslatef(x,y,0);313GL.glScalef(sx,sy,1);314315float xp = 0;316float yp = 0;317318for (int tx=0;tx<xcount;tx++) {319yp = 0;320for (int ty=0;ty<ycount;ty++) {321Image image = images[tx][ty];322323image.draw(xp,yp,image.getWidth(), image.getHeight(), filter);324325yp += image.getHeight();326if (ty == ycount - 1) {327xp += image.getWidth();328}329}330331}332333GL.glScalef(1.0f/sx,1.0f/sy,1);334GL.glTranslatef(-x,-y,0);335}336337/**338* @see org.newdawn.slick.Image#draw(float, float, float, float, float, float, float, float)339*/340public void draw(float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) {341int srcwidth = (int) (srcx2 - srcx);342int srcheight = (int) (srcy2 - srcy);343344Image subImage = getSubImage((int) srcx,(int) srcy,srcwidth,srcheight);345346int width = (int) (x2 - x);347int height = (int) (y2 - y);348349subImage.draw(x,y,width,height);350}351352/**353* @see org.newdawn.slick.Image#draw(float, float, float, float, float, float)354*/355public void draw(float x, float y, float srcx, float srcy, float srcx2, float srcy2) {356int srcwidth = (int) (srcx2 - srcx);357int srcheight = (int) (srcy2 - srcy);358359draw(x,y,srcwidth,srcheight,srcx,srcy,srcx2,srcy2);360}361362/**363* @see org.newdawn.slick.Image#draw(float, float, float, float)364*/365public void draw(float x, float y, float width, float height) {366draw(x,y,width,height,Color.white);367}368369/**370* @see org.newdawn.slick.Image#draw(float, float, float)371*/372public void draw(float x, float y, float scale) {373draw(x,y,scale,Color.white);374}375376/**377* @see org.newdawn.slick.Image#draw(float, float)378*/379public void draw(float x, float y) {380draw(x,y,Color.white);381}382383/**384* @see org.newdawn.slick.Image#drawEmbedded(float, float, float, float)385*/386public void drawEmbedded(float x, float y, float width, float height) {387float sx = width / realWidth;388float sy = height / realHeight;389390float xp = 0;391float yp = 0;392393for (int tx=0;tx<xcount;tx++) {394yp = 0;395for (int ty=0;ty<ycount;ty++) {396Image image = images[tx][ty];397398if ((lastBind == null) || (image.getTexture() != lastBind.getTexture())) {399if (lastBind != null) {400lastBind.endUse();401}402lastBind = image;403lastBind.startUse();404}405image.drawEmbedded(xp+x,yp+y,image.getWidth(), image.getHeight());406407yp += image.getHeight();408if (ty == ycount - 1) {409xp += image.getWidth();410}411}412413}414}415416/**417* @see org.newdawn.slick.Image#drawFlash(float, float, float, float)418*/419public void drawFlash(float x, float y, float width, float height) {420float sx = width / realWidth;421float sy = height / realHeight;422423GL.glTranslatef(x,y,0);424GL.glScalef(sx,sy,1);425426float xp = 0;427float yp = 0;428429for (int tx=0;tx<xcount;tx++) {430yp = 0;431for (int ty=0;ty<ycount;ty++) {432Image image = images[tx][ty];433434image.drawFlash(xp,yp,image.getWidth(), image.getHeight());435436yp += image.getHeight();437if (ty == ycount - 1) {438xp += image.getWidth();439}440}441442}443444GL.glScalef(1.0f/sx,1.0f/sy,1);445GL.glTranslatef(-x,-y,0);446}447448/**449* @see org.newdawn.slick.Image#drawFlash(float, float)450*/451public void drawFlash(float x, float y) {452drawFlash(x,y,width,height);453}454455/**456* Not supported in BigImage457*458* @see org.newdawn.slick.Image#endUse()459*/460public void endUse() {461if (lastBind != null) {462lastBind.endUse();463}464lastBind = null;465}466467/**468* Not supported in BigImage469*470* @see org.newdawn.slick.Image#startUse()471*/472public void startUse() {473}474475/**476* Not supported in BigImage477*478* @see org.newdawn.slick.Image#ensureInverted()479*/480public void ensureInverted() {481throw new OperationNotSupportedException("Doesn't make sense for tiled operations");482}483484/**485* Not supported in BigImage486*487* @see org.newdawn.slick.Image#getColor(int, int)488*/489public Color getColor(int x, int y) {490throw new OperationNotSupportedException("Can't use big images as buffers");491}492493/**494* @see org.newdawn.slick.Image#getFlippedCopy(boolean, boolean)495*/496public Image getFlippedCopy(boolean flipHorizontal, boolean flipVertical) {497BigImage image = new BigImage();498499image.images = images;500image.xcount = xcount;501image.ycount = ycount;502image.width = width;503image.height = height;504image.realWidth = realWidth;505image.realHeight = realHeight;506507if (flipHorizontal) {508Image[][] images = image.images;509image.images = new Image[xcount][ycount];510511for (int x=0;x<xcount;x++) {512for (int y=0;y<ycount;y++) {513image.images[x][y] = images[xcount-1-x][y].getFlippedCopy(true, false);514}515}516}517518if (flipVertical) {519Image[][] images = image.images;520image.images = new Image[xcount][ycount];521522for (int x=0;x<xcount;x++) {523for (int y=0;y<ycount;y++) {524image.images[x][y] = images[x][ycount-1-y].getFlippedCopy(false, true);525}526}527}528529return image;530}531532/**533* Not supported in BigImage534*535* @see org.newdawn.slick.Image#getGraphics()536*/537public Graphics getGraphics() throws SlickException {538throw new OperationNotSupportedException("Can't use big images as offscreen buffers");539}540541/**542* @see org.newdawn.slick.Image#getScaledCopy(float)543*/544public Image getScaledCopy(float scale) {545return getScaledCopy((int) (scale * width), (int) (scale * height));546}547548/**549* @see org.newdawn.slick.Image#getScaledCopy(int, int)550*/551public Image getScaledCopy(int width, int height) {552BigImage image = new BigImage();553554image.images = images;555image.xcount = xcount;556image.ycount = ycount;557image.width = width;558image.height = height;559image.realWidth = realWidth;560image.realHeight = realHeight;561562return image;563}564565/**566* @see org.newdawn.slick.Image#getSubImage(int, int, int, int)567*/568public Image getSubImage(int x, int y, int width, int height) {569BigImage image = new BigImage();570571image.width = width;572image.height = height;573image.realWidth = width;574image.realHeight = height;575image.images = new Image[this.xcount][this.ycount];576577float xp = 0;578float yp = 0;579int x2 = x+width;580int y2 = y+height;581582int startx = 0;583int starty = 0;584boolean foundStart = false;585586for (int xt=0;xt<xcount;xt++) {587yp = 0;588starty = 0;589foundStart = false;590for (int yt=0;yt<ycount;yt++) {591Image current = images[xt][yt];592593int xp2 = (int) (xp + current.getWidth());594int yp2 = (int) (yp + current.getHeight());595596// if the top corner of the subimage is inside the area597// we want or the bottom corrent of the image is, then consider using the598// image599600// this image contributes to the sub image we're attempt to retrieve601int targetX1 = (int) Math.max(x, xp);602int targetY1 = (int) Math.max(y, yp);603int targetX2 = Math.min(x2, xp2);604int targetY2 = Math.min(y2, yp2);605606int targetWidth = targetX2 - targetX1;607int targetHeight = targetY2 - targetY1;608609if ((targetWidth > 0) && (targetHeight > 0)) {610Image subImage = current.getSubImage((int) (targetX1 - xp), (int) (targetY1 - yp),611(targetX2 - targetX1),612(targetY2 - targetY1));613foundStart = true;614image.images[startx][starty] = subImage;615starty++;616image.ycount = Math.max(image.ycount, starty);617}618619yp += current.getHeight();620if (yt == ycount - 1) {621xp += current.getWidth();622}623}624if (foundStart) {625startx++;626image.xcount++;627}628}629630return image;631}632633/**634* Not supported in BigImage635*636* @see org.newdawn.slick.Image#getTexture()637*/638public Texture getTexture() {639throw new OperationNotSupportedException("Can't use big images as offscreen buffers");640}641642/**643* @see org.newdawn.slick.Image#initImpl()644*/645protected void initImpl() {646throw new OperationNotSupportedException("Can't use big images as offscreen buffers");647}648649/**650* @see org.newdawn.slick.Image#reinit()651*/652protected void reinit() {653throw new OperationNotSupportedException("Can't use big images as offscreen buffers");654}655656/**657* Not supported in BigImage658*659* @see org.newdawn.slick.Image#setTexture(org.newdawn.slick.opengl.Texture)660*/661public void setTexture(Texture texture) {662throw new OperationNotSupportedException("Can't use big images as offscreen buffers");663}664665/**666* Get a sub-image that builds up this image. Note that the offsets667* used will depend on the maximum texture size on the OpenGL hardware668*669* @param offsetX The x position of the image to return670* @param offsetY The y position of the image to return671* @return The image at the specified offset into the big image672*/673public Image getSubImage(int offsetX, int offsetY) {674return images[offsetX][offsetY];675}676677/**678* Get a count of the number images that build this image up horizontally679*680* @return The number of sub-images across the big image681*/682public int getHorizontalImageCount() {683return xcount;684}685686/**687* Get a count of the number images that build this image up vertically688*689* @return The number of sub-images down the big image690*/691public int getVerticalImageCount() {692return ycount;693}694695/**696* @see org.newdawn.slick.Image#toString()697*/698public String toString() {699return "[BIG IMAGE]";700}701702/**703* Destroy the image and release any native resources.704* Calls on a destroyed image have undefined results705*/706public void destroy() throws SlickException {707for (int tx=0;tx<xcount;tx++) {708for (int ty=0;ty<ycount;ty++) {709Image image = images[tx][ty];710image.destroy();711}712}713}714715/**716* @see org.newdawn.slick.Image#draw(float, float, float, float, float, float, float, float, org.newdawn.slick.Color)717*/718public void draw(float x, float y, float x2, float y2, float srcx,719float srcy, float srcx2, float srcy2, Color filter) {720int srcwidth = (int) (srcx2 - srcx);721int srcheight = (int) (srcy2 - srcy);722723Image subImage = getSubImage((int) srcx,(int) srcy,srcwidth,srcheight);724725int width = (int) (x2 - x);726int height = (int) (y2 - y);727728subImage.draw(x,y,width,height,filter);729}730731/**732* @see org.newdawn.slick.Image#drawCentered(float, float)733*/734public void drawCentered(float x, float y) {735throw new UnsupportedOperationException();736}737738/**739* @see org.newdawn.slick.Image#drawEmbedded(float, float, float, float, float, float, float, float, org.newdawn.slick.Color)740*/741public void drawEmbedded(float x, float y, float x2, float y2, float srcx,742float srcy, float srcx2, float srcy2, Color filter) {743throw new UnsupportedOperationException();744}745746/**747* @see org.newdawn.slick.Image#drawEmbedded(float, float, float, float, float, float, float, float)748*/749public void drawEmbedded(float x, float y, float x2, float y2, float srcx,750float srcy, float srcx2, float srcy2) {751throw new UnsupportedOperationException();752}753754/**755* @see org.newdawn.slick.Image#drawFlash(float, float, float, float, org.newdawn.slick.Color)756*/757public void drawFlash(float x, float y, float width, float height, Color col) {758throw new UnsupportedOperationException();759}760761/**762* @see org.newdawn.slick.Image#drawSheared(float, float, float, float)763*/764public void drawSheared(float x, float y, float hshear, float vshear) {765throw new UnsupportedOperationException();766}767}768769770