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
17
18
19
20
21
22
23
24
25 public class TGAImageData implements LoadableImageData {
26
27 private int texWidth;
28
29 private int texHeight;
30
31 private int width;
32
33 private int height;
34
35 private Format format;
36
37
38
39
40 public TGAImageData() {
41 }
42
43
44
45
46
47
48
49 private short flipEndian(short signedShort) {
50 int input = signedShort & 0xFFFF;
51 return (short) (input << 8 | (input & 0xFF00) >>> 8);
52 }
53
54
55
56
57 public Format getFormat() {
58 return format;
59 }
60
61
62
63
64 public int getWidth() {
65 return width;
66 }
67
68
69
70
71 public int getHeight() {
72 return height;
73 }
74
75
76
77
78 public int getTexWidth() {
79 return texWidth;
80 }
81
82
83
84
85 public int getTexHeight() {
86 return texHeight;
87 }
88
89
90
91
92 public ByteBuffer loadImage(@Nonnull InputStream fis) throws IOException {
93 return loadImage(fis,true, null);
94 }
95
96
97
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
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
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
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
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
300
301
302
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
314
315 @Nonnull
316 public ByteBuffer getImageBufferData() {
317 throw new RuntimeException("TGAImageData doesn't store it's image.");
318 }
319
320
321
322
323 public void configureEdging(boolean edging) {
324 }
325 }