View Javadoc
1   package org.newdawn.slick.opengl;
2   
3   import java.io.BufferedInputStream;
4   import java.io.DataInputStream;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.nio.ByteBuffer;
8   import java.nio.ByteOrder;
9   
10  import org.lwjgl.BufferUtils;
11  
12  import javax.annotation.Nonnull;
13  import javax.annotation.Nullable;
14  
15  /**
16   * A utility to load TGAs. Note: NOT THREAD SAFE
17   * 
18   * Fresh cut of code but largely influeneced by the TGA loading class
19   * provided as part of the Java Monkey Engine (JME). Why not check out 
20   * what they're doing over at http://www.jmonkeyengine.com. kudos to 
21   * Mark Powell.
22   * 
23   * @author Kevin Glass
24   */
25  public class TGAImageData implements LoadableImageData {
26      /** The width of the texture that needs to be generated */
27      private int texWidth;
28      /** The height of the texture that needs to be generated */
29      private int texHeight;
30      /** The width of the TGA image */
31      private int width;
32      /** The height of the TGA image */
33      private int height;
34      /** The format of this image */
35      private Format format;
36  
37      /**
38       * Create a new TGA Loader
39       */
40      public TGAImageData() {
41      }
42  
43      /**
44       * Flip the endian-ness of the short
45       *
46       * @param signedShort The short to flip
47       * @return The flipped short
48       */
49      private short flipEndian(short signedShort) {
50          int input = signedShort & 0xFFFF;
51          return (short) (input << 8 | (input & 0xFF00) >>> 8);
52      }
53  
54      /**
55       * @see org.newdawn.slick.opengl.ImageData#getFormat()
56       */
57      public Format getFormat() {
58          return format;
59      }
60  
61      /**
62       * @see org.newdawn.slick.opengl.ImageData#getWidth()
63       */
64      public int getWidth() {
65          return width;
66      }
67  
68      /**
69       * @see org.newdawn.slick.opengl.ImageData#getHeight()
70       */
71      public int getHeight() {
72          return height;
73      }
74  
75      /**
76       * @see org.newdawn.slick.opengl.ImageData#getTexWidth()
77       */
78      public int getTexWidth() {
79          return texWidth;
80      }
81  
82      /**
83       * @see org.newdawn.slick.opengl.ImageData#getTexHeight()
84       */
85      public int getTexHeight() {
86          return texHeight;
87      }
88  
89      /**
90       * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream)
91       */
92      public ByteBuffer loadImage(@Nonnull InputStream fis) throws IOException {
93          return loadImage(fis,true, null);
94      }
95  
96      /**
97       * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, int[])
98       */
99      public ByteBuffer loadImage(@Nonnull InputStream fis, boolean flipped, @Nullable int[] transparent) throws IOException {
100         return loadImage(fis, flipped, false, transparent);
101     }
102 
103     /**
104      * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, boolean, int[])
105      */
106     public ByteBuffer loadImage(@Nonnull InputStream fis, boolean flipped, boolean forceAlpha, @Nullable int[] transparent) throws IOException {
107         if (transparent != null) {
108             forceAlpha = true;
109         }
110         byte red;
111         byte green;
112         byte blue;
113         byte alpha;
114 
115         BufferedInputStream bis = new BufferedInputStream(fis, 100000);
116         DataInputStream dis = new DataInputStream(bis);
117 
118         // Read in the Header
119         short idLength = (short) dis.read();
120         dis.read();
121         short imageType = (short) dis.read();
122         flipEndian(dis.readShort());
123         flipEndian(dis.readShort());
124         dis.read();
125         flipEndian(dis.readShort());
126         flipEndian(dis.readShort());
127 
128         if (imageType != 2) {
129             throw new IOException("Slick only supports uncompressed RGB(A) TGA images");
130         }
131 
132         width = flipEndian(dis.readShort());
133         height = flipEndian(dis.readShort());
134         short pixelDepth = (short) dis.read();
135         if (pixelDepth == 32) {
136             forceAlpha = false;
137         }
138 
139         texWidth = get2Fold(width);
140         texHeight = get2Fold(height);
141 
142         short imageDescriptor = (short) dis.read();
143         if ((imageDescriptor & 0x0020) == 0) {
144            flipped = !flipped;
145         }
146 
147         // Skip image ID
148         if (idLength > 0) {
149             bis.skip(idLength);
150         }
151 
152         byte[] rawData;
153         if ((pixelDepth == 32) || (forceAlpha)) {
154             pixelDepth = 32;
155             format = Format.RGBA;
156             rawData = new byte[texWidth * texHeight * 4];
157         } else if (pixelDepth == 24) {
158             format = Format.RGB;
159             rawData = new byte[texWidth * texHeight * 3];
160         } else {
161             throw new RuntimeException("Only 24 and 32 bit TGAs are supported");
162         }
163 
164         if (pixelDepth == 24) {
165             if (flipped) {
166                 for (int i = height-1; i >= 0; i--) {
167                     for (int j = 0; j < width; j++) {
168                         blue = dis.readByte();
169                         green = dis.readByte();
170                         red = dis.readByte();
171 
172                         int ofs = ((j + (i * texWidth)) * 3);
173                         rawData[ofs] = red;
174                         rawData[ofs + 1] = green;
175                         rawData[ofs + 2] = blue;
176                     }
177                 }
178             } else {
179                 for (int i = 0; i < height; i++) {
180                     for (int j = 0; j < width; j++) {
181                         blue = dis.readByte();
182                         green = dis.readByte();
183                         red = dis.readByte();
184 
185                         int ofs = ((j + (i * texWidth)) * 3);
186                         rawData[ofs] = red;
187                         rawData[ofs + 1] = green;
188                         rawData[ofs + 2] = blue;
189                     }
190                 }
191             }
192         } else if (pixelDepth == 32) {
193             if (flipped) {
194                 for (int i = height-1; i >= 0; i--) {
195                     for (int j = 0; j < width; j++) {
196                         blue = dis.readByte();
197                         green = dis.readByte();
198                         red = dis.readByte();
199                         if (forceAlpha) {
200                             alpha = (byte) 255;
201                         } else {
202                             alpha = dis.readByte();
203                         }
204 
205                         int ofs = ((j + (i * texWidth)) * 4);
206 
207                         rawData[ofs] = red;
208                         rawData[ofs + 1] = green;
209                         rawData[ofs + 2] = blue;
210                         rawData[ofs + 3] = alpha;
211 
212                         if (alpha == 0) {
213                             rawData[ofs + 2] = (byte) 0;
214                             rawData[ofs + 1] = (byte) 0;
215                             rawData[ofs] = (byte) 0;
216                         }
217                     }
218                 }
219             } else {
220                 for (int i = 0; i < height; i++) {
221                     for (int j = 0; j < width; j++) {
222                         blue = dis.readByte();
223                         green = dis.readByte();
224                         red = dis.readByte();
225                         if (forceAlpha) {
226                             alpha = (byte) 255;
227                         } else {
228                             alpha = dis.readByte();
229                         }
230 
231                         int ofs = ((j + (i * texWidth)) * 4);
232 
233                         if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
234                             rawData[ofs] = red;
235                             rawData[ofs + 1] = green;
236                             rawData[ofs + 2] = blue;
237                             rawData[ofs + 3] = alpha;
238                         } else {
239                             rawData[ofs] = red;
240                             rawData[ofs + 1] = green;
241                             rawData[ofs + 2] = blue;
242                             rawData[ofs + 3] = alpha;
243                         }
244 
245                         if (alpha == 0) {
246                             rawData[ofs + 2] = 0;
247                             rawData[ofs + 1] = 0;
248                             rawData[ofs] = 0;
249                         }
250                     }
251                 }
252             }
253         }
254         fis.close();
255 
256         if (transparent != null) {
257             for (int i=0;i<rawData.length;i+=4) {
258                 boolean match = true;
259                 for (int c=0;c<3;c++) {
260                     if (rawData[i+c] != transparent[c]) {
261                         match = false;
262                     }
263                 }
264 
265                 if (match) {
266                     rawData[i+3] = 0;
267                    }
268             }
269         }
270 
271         // Get a pointer to the image memory
272         ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length);
273         scratch.put(rawData);
274 
275         int perPixel = pixelDepth / 8;
276         if (height < texHeight-1) {
277             int topOffset = (texHeight-1) * (texWidth*perPixel);
278             int bottomOffset = (height-1) * (texWidth*perPixel);
279             for (int x=0;x<texWidth*perPixel;x++) {
280                 scratch.put(topOffset+x, scratch.get(x));
281                 scratch.put(bottomOffset+(texWidth*perPixel)+x, scratch.get((texWidth*perPixel)+x));
282             }
283         }
284         if (width < texWidth-1) {
285             for (int y=0;y<texHeight;y++) {
286                 for (int i=0;i<perPixel;i++) {
287                     scratch.put(((y+1)*(texWidth*perPixel))-perPixel+i, scratch.get(y*(texWidth*perPixel)+i));
288                     scratch.put((y*(texWidth*perPixel))+(width*perPixel)+i, scratch.get((y*(texWidth*perPixel))+((width-1)*perPixel)+i));
289                 }
290             }
291         }
292 
293         scratch.flip();
294 
295         return scratch;
296     }
297 
298     /**
299      * Get the closest greater power of 2 to the fold number
300      * 
301      * @param fold The target number
302      * @return The power of 2
303      */
304     private int get2Fold(int fold) {
305         int ret = 2;
306         while (ret < fold) {
307             ret *= 2;
308         }
309         return ret;
310     }
311 
312     /**
313      * @see org.newdawn.slick.opengl.ImageData#getImageBufferData()
314      */
315     @Nonnull
316     public ByteBuffer getImageBufferData() {
317         throw new RuntimeException("TGAImageData doesn't store it's image.");
318     }
319 
320     /**
321      * @see org.newdawn.slick.opengl.LoadableImageData#configureEdging(boolean)
322      */
323     public void configureEdging(boolean edging) {
324     }
325 }