JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
TGAImage.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * - Redistribution of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistribution in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * Neither the name of Sun Microsystems, Inc. or the names of
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * This software is provided "AS IS," without a warranty of any kind. ALL
20 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
23 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
24 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
25 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
26 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
27 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
28 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
29 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
30 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31 *
32 * You acknowledge that this software is not designed or intended for use
33 * in the design, construction, operation or maintenance of any nuclear
34 * facility.
35 *
36 * Sun gratefully acknowledges that this software was originally authored
37 * and developed by Kenneth Bradley Russell and Christopher John Kline.
38 */
39
40package com.jogamp.opengl.util.texture.spi;
41
42import java.io.*;
43import java.nio.*;
44import java.nio.channels.*;
45
46import com.jogamp.opengl.*;
47import com.jogamp.common.util.IOUtil;
48
49/**
50 * Targa image reader and writer adapted from sources of the <a href =
51 * "http://java.sun.com/products/jimi/">Jimi</a> image I/O class library.
52 *
53 * <P>
54 *
55 * Image decoder for image data stored in TGA file format.
56 * Currently only the original TGA file format is supported. This is
57 * because the new TGA format has data at the end of the file, getting
58 * to the end of a file in an InputStream orient environment presents
59 * several difficulties which are avoided at the moment.
60 *
61 * <P>
62 *
63 * This is a simple decoder and is only setup to load a single image
64 * from the input stream
65 *
66 * <P>
67 *
68 * @author Robin Luiten
69 * @author Kenneth Russell
70 * @version $Revision: 1768 $
71 */
72
73public class TGAImage {
74 private final Header header;
75 private int format;
76 private int bpp;
77 private ByteBuffer data;
78
79 private TGAImage(final Header header) {
80 this.header = header;
81 }
82
83 /**
84 * This class reads in all of the TGA image header in addition it also
85 * reads in the imageID field as it is convenient to handle that here.
86 *
87 * @author Robin Luiten
88 * @version 1.1
89 */
90 public static class Header {
91 /** Set of possible file format TGA types */
92 public final static int TYPE_NEW = 0;
93 public final static int TYPE_OLD = 1;
94 public final static int TYPE_UNK = 2; // cant rewind stream so unknown for now.
95
96 /** Set of possible image types in TGA file */
97 public final static int NO_IMAGE = 0; // no image data
98 public final static int UCOLORMAPPED = 1; // uncompressed color mapped image
99 public final static int UTRUECOLOR = 2; // uncompressed true color image
100 public final static int UBLACKWHITE = 3; // uncompressed black and white image
101 public final static int COLORMAPPED = 9; // compressed color mapped image
102 public final static int TRUECOLOR = 10; // compressed true color image
103 public final static int BLACKWHITE = 11; // compressed black and white image
104
105 /** Field image descriptor bitfield values definitions */
106 public final static int ID_ATTRIBPERPIXEL = 0xF;
107 public final static int ID_RIGHTTOLEFT = 0x10;
108 public final static int ID_TOPTOBOTTOM = 0x20;
109 public final static int ID_INTERLEAVE = 0xC0;
110
111 /** Field image descriptor / interleave values */
112 public final static int I_NOTINTERLEAVED = 0;
113 public final static int I_TWOWAY = 1;
114 public final static int I_FOURWAY = 2;
115
116 /** Type of this TGA file format */
117 private final int tgaType;
118
119 /** initial TGA image data fields */
120 private int idLength; // byte value
121 private int colorMapType; // byte value
122 private int imageType; // byte value
123
124 /** TGA image colour map fields */
125 private int firstEntryIndex;
126 private int colorMapLength;
127 private byte colorMapEntrySize;
128
129 /** TGA image specification fields */
130 private int xOrigin;
131 private int yOrigin;
132 private int width;
133 private int height;
134 private byte pixelDepth;
135 private byte imageDescriptor;
136
137 private byte[] imageIDbuf;
138 private String imageID;
139
140 // For construction from user data
141 Header() {
142 tgaType = TYPE_OLD; // dont try and get footer.
143 }
144
145 Header(final LEDataInputStream in) throws IOException {
146 tgaType = TYPE_OLD; // dont try and get footer.
147
148 // initial header fields
149 idLength = in.readUnsignedByte();
150 colorMapType = in.readUnsignedByte();
151 imageType = in.readUnsignedByte();
152
153 // color map header fields
154 firstEntryIndex = in.readUnsignedShort();
155 colorMapLength = in.readUnsignedShort();
156 colorMapEntrySize = in.readByte();
157
158 // TGA image specification fields
159 xOrigin = in.readUnsignedShort();
160 yOrigin = in.readUnsignedShort();
161 width = in.readUnsignedShort();
162 height = in.readUnsignedShort();
163 pixelDepth = in.readByte();
164 imageDescriptor = in.readByte();
165
166 if (idLength > 0) {
167 imageIDbuf = new byte[idLength];
168 in.read(imageIDbuf, 0, idLength);
169 imageID = new String(imageIDbuf, "US-ASCII");
170 }
171 }
172
173 public int tgaType() { return tgaType; }
174
175 /** initial TGA image data fields */
176 public int idLength() { return idLength; }
177 public int colorMapType() { return colorMapType; }
178 public int imageType() { return imageType; }
179
180 /** TGA image colour map fields */
181 public int firstEntryIndex() { return firstEntryIndex; }
182 public int colorMapLength() { return colorMapLength; }
183 public byte colorMapEntrySize() { return colorMapEntrySize; }
184
185 /** TGA image specification fields */
186 public int xOrigin() { return xOrigin; }
187 public int yOrigin() { return yOrigin; }
188 public int width() { return width; }
189 public int height() { return height; }
190 public byte pixelDepth() { return pixelDepth; }
191 public byte imageDescriptor() { return imageDescriptor; }
192
193 /** bitfields in imageDescriptor */
194 public byte attribPerPixel() { return (byte)(imageDescriptor & ID_ATTRIBPERPIXEL); }
195 public boolean rightToLeft() { return ((imageDescriptor & ID_RIGHTTOLEFT) != 0); }
196 public boolean topToBottom() { return ((imageDescriptor & ID_TOPTOBOTTOM) != 0); }
197 public byte interleave() { return (byte)((imageDescriptor & ID_INTERLEAVE) >> 6); }
198
199 public byte[] imageIDbuf() { return imageIDbuf; }
200 public String imageID() { return imageID; }
201
202 @Override
203 public String toString() {
204 return "TGA Header " +
205 " id length: " + idLength +
206 " color map type: "+ colorMapType +
207 " image type: "+ imageType +
208 " first entry index: " + firstEntryIndex +
209 " color map length: " + colorMapLength +
210 " color map entry size: " + colorMapEntrySize +
211 " x Origin: " + xOrigin +
212 " y Origin: " + yOrigin +
213 " width: "+ width +
214 " height: "+ height +
215 " pixel depth: "+ pixelDepth +
216 " image descriptor: "+ imageDescriptor +
217 (imageIDbuf == null ? "" : (" ID String: " + imageID));
218 }
219
220 public int size() { return 18 + idLength; }
221
222 // buf must be in little-endian byte order
223 private void write(final ByteBuffer buf) {
224 buf.put((byte) idLength);
225 buf.put((byte) colorMapType);
226 buf.put((byte) imageType);
227 buf.putShort((short) firstEntryIndex);
228 buf.putShort((short) colorMapLength);
229 buf.put(colorMapEntrySize);
230 buf.putShort((short) xOrigin);
231 buf.putShort((short) yOrigin);
232 buf.putShort((short) width);
233 buf.putShort((short) height);
234 buf.put(pixelDepth);
235 buf.put(imageDescriptor);
236 if (idLength > 0) {
237 try {
238 final byte[] chars = imageID.getBytes("US-ASCII");
239 buf.put(chars);
240 } catch (final UnsupportedEncodingException e) {
241 throw new RuntimeException(e);
242 }
243 }
244 }
245 }
246
247
248 /**
249 * Identifies the image type of the tga image data and loads
250 * it into the JimiImage structure. This was taken from the
251 * prototype and modified for the new Jimi structure
252 */
253 private void decodeImage(final GLProfile glp, final LEDataInputStream dIn) throws IOException {
254 switch (header.imageType()) {
255 case Header.UCOLORMAPPED:
256 throw new IOException("TGADecoder Uncompressed Colormapped images not supported");
257
258 case Header.UTRUECOLOR: // pixelDepth 15, 16, 24 and 32
259 switch (header.pixelDepth) {
260 case 16:
261 throw new IOException("TGADecoder Compressed 16-bit True Color images not supported");
262
263 case 24:
264 case 32:
265 decodeRGBImageU24_32(glp, dIn);
266 break;
267 }
268 break;
269
270 case Header.UBLACKWHITE:
271 throw new IOException("TGADecoder Uncompressed Grayscale images not supported");
272
273 case Header.COLORMAPPED:
274 throw new IOException("TGADecoder Compressed Colormapped images not supported");
275
276 case Header.TRUECOLOR:
277 switch (header.pixelDepth) {
278 case 16:
279 throw new IOException("TGADecoder Compressed 16-bit True Color images not supported");
280
281 case 24:
282 case 32:
283 decodeRGBImageRLE24_32(glp, dIn);
284 break;
285 }
286 break;
287
288 case Header.BLACKWHITE:
289 throw new IOException("TGADecoder Compressed Grayscale images not supported");
290 }
291 }
292
293 /**
294 * This assumes that the body is for a 24 bit or 32 bit for a
295 * RGB or ARGB image respectively.
296 */
297 private void decodeRGBImageU24_32(final GLProfile glp, final LEDataInputStream dIn) throws IOException {
298 setupImage24_32(glp);
299
300 int i; // row index
301 int y; // output row index
302 final int rawWidth = header.width() * bpp;
303 final byte[] rawBuf = new byte[rawWidth];
304 final byte[] tmpData = new byte[rawWidth * header.height()];
305
306 for (i = 0; i < header.height(); ++i) {
307 dIn.readFully(rawBuf, 0, rawWidth);
308
309 if (header.topToBottom())
310 y = header.height - i - 1; // range 0 to (header.height - 1)
311 else
312 y = i;
313
314 System.arraycopy(rawBuf, 0, tmpData, y * rawWidth, rawBuf.length);
315 }
316
317 if(format == GL.GL_RGB || format == GL.GL_RGBA)
318 swapBGR(tmpData, rawWidth, header.height(), bpp);
319 data = ByteBuffer.wrap(tmpData);
320 }
321
322 /**
323 * This assumes that the body is for a 24 bit or 32 bit for a
324 * RGB or ARGB image respectively.
325 */
326 private void decodeRGBImageRLE24_32(final GLProfile glp, final LEDataInputStream dIn) throws IOException {
327 setupImage24_32(glp);
328
329 final byte[] pixel = new byte[bpp];
330 final int rawWidth = header.width() * bpp;
331 final byte[] tmpData = new byte[rawWidth * header.height()];
332 int i = 0, j;
333 int packet, len;
334 while (i < tmpData.length) {
335 packet = dIn.readUnsignedByte();
336 len = (packet & 0x7F) + 1;
337 if ((packet & 0x80) != 0) {
338 dIn.read(pixel);
339 for (j = 0; j < len; ++j)
340 System.arraycopy(pixel, 0, tmpData, i + j * bpp, bpp);
341 } else
342 dIn.read(tmpData, i, len * bpp);
343 i += bpp * len;
344 }
345
346 if(format == GL.GL_RGB || format == GL.GL_RGBA)
347 swapBGR(tmpData, rawWidth, header.height(), bpp);
348 data = ByteBuffer.wrap(tmpData);
349 }
350
351 private void setupImage24_32(final GLProfile glp) {
352 bpp = header.pixelDepth / 8;
353 switch (header.pixelDepth) {
354 case 24:
355 format = glp.isGL2GL3() ? GL.GL_BGR : GL.GL_RGB;
356 break;
357 case 32:
358 boolean useBGRA = glp.isGL2GL3();
359 if(!useBGRA) {
360 final GLContext ctx = GLContext.getCurrent();
361 useBGRA = null != ctx && ctx.isTextureFormatBGRA8888Available();
362 }
363 format = useBGRA ? GL.GL_BGRA : GL.GL_RGBA;
364 break;
365 default:
366 assert false;
367 }
368 }
369
370 private static void swapBGR(final byte[] data, final int bWidth, final int height, final int bpp) {
371 byte r,b;
372 int k;
373 for(int i=0; i<height; ++i) {
374 for(int j=0; j<bWidth; j+=bpp) {
375 k=i*bWidth+j;
376 b=data[k+0];
377 r=data[k+2];
378 data[k+0]=r;
379 data[k+2]=b;
380 }
381 }
382 }
383
384 /** Returns the width of the image. */
385 public int getWidth() { return header.width(); }
386
387 /** Returns the height of the image. */
388 public int getHeight() { return header.height(); }
389
390 /** Returns the OpenGL format for this texture; e.g. GL.GL_BGR or GL.GL_BGRA. */
391 public int getGLFormat() { return format; }
392
393 /** Returns the bytes per pixel */
394 public int getBytesPerPixel() { return bpp; }
395
396 /** Returns the raw data for this texture in the correct
397 (bottom-to-top) order for calls to glTexImage2D. */
398 public ByteBuffer getData() { return data; }
399
400 /** Reads a Targa image from the specified file. */
401 public static TGAImage read(final GLProfile glp, final String filename) throws IOException {
402 return read(glp, new FileInputStream(filename));
403 }
404
405 /** Reads a Targa image from the specified InputStream. */
406 public static TGAImage read(final GLProfile glp, final InputStream in) throws IOException {
407 final LEDataInputStream dIn = new LEDataInputStream(new BufferedInputStream(in));
408
409 final Header header = new Header(dIn);
410 final TGAImage res = new TGAImage(header);
411 res.decodeImage(glp, dIn);
412 return res;
413 }
414
415 /** Writes the image in Targa format to the specified file name. */
416 public void write(final String filename) throws IOException {
417 write(new File(filename));
418 }
419
420 /** Writes the image in Targa format to the specified file. */
421 public void write(final File file) throws IOException {
422 final FileOutputStream stream = IOUtil.getFileOutputStream(file, true);
423 final FileChannel chan = stream.getChannel();
424 final ByteBuffer buf = ByteBuffer.allocate(header.size());
425 buf.order(ByteOrder.LITTLE_ENDIAN);
426 header.write(buf);
427 buf.rewind();
428 chan.write(buf);
429 chan.write(data);
430 chan.force(true);
431 chan.close();
432 stream.close();
433 data.rewind();
434 }
435
436 /** Creates a TGAImage from data supplied by the end user. Shares
437 data with the passed ByteBuffer. Assumes the data is already in
438 the correct byte order for writing to disk, i.e., BGR or
439 BGRA. */
440 public static TGAImage createFromData(final int width,
441 final int height,
442 final boolean hasAlpha,
443 final boolean topToBottom,
444 final ByteBuffer data) {
445 final Header header = new Header();
446 header.imageType = Header.UTRUECOLOR;
447 header.width = width;
448 header.height = height;
449 header.pixelDepth = (byte) (hasAlpha ? 32 : 24);
450 header.imageDescriptor = (byte) (topToBottom ? Header.ID_TOPTOBOTTOM : 0);
451 // Note ID not supported
452 final TGAImage ret = new TGAImage(header);
453 ret.data = data;
454 return ret;
455 }
456}
Abstraction for an OpenGL rendering context.
Definition: GLContext.java:74
static GLContext getCurrent()
Returns this thread current context.
Definition: GLContext.java:515
boolean isTextureFormatBGRA8888Available()
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
final boolean isGL2GL3()
Indicates whether this profile is capable of GL2GL3.
This class reads in all of the TGA image header in addition it also reads in the imageID field as it ...
Definition: TGAImage.java:90
static final int TYPE_NEW
Set of possible file format TGA types.
Definition: TGAImage.java:92
static final int NO_IMAGE
Set of possible image types in TGA file.
Definition: TGAImage.java:97
int firstEntryIndex()
TGA image colour map fields.
Definition: TGAImage.java:181
static final int I_NOTINTERLEAVED
Field image descriptor / interleave values.
Definition: TGAImage.java:112
static final int ID_ATTRIBPERPIXEL
Field image descriptor bitfield values definitions.
Definition: TGAImage.java:106
int xOrigin()
TGA image specification fields.
Definition: TGAImage.java:186
byte attribPerPixel()
bitfields in imageDescriptor
Definition: TGAImage.java:194
int idLength()
initial TGA image data fields
Definition: TGAImage.java:176
Targa image reader and writer adapted from sources of the Jimi image I/O class library.
Definition: TGAImage.java:73
int getWidth()
Returns the width of the image.
Definition: TGAImage.java:385
ByteBuffer getData()
Returns the raw data for this texture in the correct (bottom-to-top) order for calls to glTexImage2D.
Definition: TGAImage.java:398
static TGAImage read(final GLProfile glp, final InputStream in)
Reads a Targa image from the specified InputStream.
Definition: TGAImage.java:406
void write(final File file)
Writes the image in Targa format to the specified file.
Definition: TGAImage.java:421
void write(final String filename)
Writes the image in Targa format to the specified file name.
Definition: TGAImage.java:416
static TGAImage createFromData(final int width, final int height, final boolean hasAlpha, final boolean topToBottom, final ByteBuffer data)
Creates a TGAImage from data supplied by the end user.
Definition: TGAImage.java:440
int getHeight()
Returns the height of the image.
Definition: TGAImage.java:388
int getBytesPerPixel()
Returns the bytes per pixel.
Definition: TGAImage.java:394
int getGLFormat()
Returns the OpenGL format for this texture; e.g.
Definition: TGAImage.java:391
static TGAImage read(final GLProfile glp, final String filename)
Reads a Targa image from the specified file.
Definition: TGAImage.java:401
static final int GL_RGB
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_RGB" with expression ...
Definition: GL.java:374
static final int GL_RGBA
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_RGBA" with expression...
Definition: GL.java:150