JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
TextureIO.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (c) 2011 JogAmp Community. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistribution in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Sun Microsystems, Inc. or the names of
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * This software is provided "AS IS," without a warranty of any kind. ALL
21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use
34 * in the design, construction, operation or maintenance of any nuclear
35 * facility.
36 *
37 * Sun gratefully acknowledges that this software was originally authored
38 * and developed by Kenneth Bradley Russell and Christopher John Kline.
39 */
40
41package com.jogamp.opengl.util.texture;
42
43import java.io.BufferedInputStream;
44import java.io.BufferedOutputStream;
45import java.io.File;
46import java.io.FileInputStream;
47import java.io.IOException;
48import java.io.InputStream;
49import java.io.OutputStream;
50import java.net.URL;
51import java.nio.Buffer;
52import java.nio.ByteBuffer;
53import java.nio.IntBuffer;
54import java.util.ArrayList;
55import java.util.HashMap;
56import java.util.Iterator;
57import java.util.List;
58import java.util.Map;
59
60import com.jogamp.nativewindow.util.Dimension;
61import com.jogamp.nativewindow.util.DimensionImmutable;
62import com.jogamp.nativewindow.util.PixelFormat;
63import com.jogamp.opengl.GL;
64import com.jogamp.opengl.GL2;
65import com.jogamp.opengl.GL2ES3;
66import com.jogamp.opengl.GL2GL3;
67import com.jogamp.opengl.GLContext;
68import com.jogamp.opengl.GLException;
69import com.jogamp.opengl.GLProfile;
70
71import jogamp.opengl.Debug;
72
73import com.jogamp.common.util.IOUtil;
74import com.jogamp.opengl.util.GLPixelStorageModes;
75import com.jogamp.opengl.util.PNGPixelRect;
76import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes;
77import com.jogamp.opengl.util.texture.ImageType;
78import com.jogamp.opengl.util.texture.spi.DDSImage;
79import com.jogamp.opengl.util.texture.spi.JPEGImage;
80import com.jogamp.opengl.util.texture.spi.NetPbmTextureWriter;
81import com.jogamp.opengl.util.texture.spi.SGIImage;
82import com.jogamp.opengl.util.texture.spi.TGAImage;
83import com.jogamp.opengl.util.texture.spi.TextureProvider;
84import com.jogamp.opengl.util.texture.spi.TextureWriter;
85
86/** <P> Provides input and output facilities for both loading OpenGL
87 textures from disk and streams as well as writing textures already
88 in memory back to disk. </P>
89
90 <P> The TextureIO class supports an arbitrary number of plug-in
91 readers and writers via TextureProviders and TextureWriters.
92 TextureProviders know how to produce TextureData objects from
93 files, InputStreams and URLs. TextureWriters know how to write
94 TextureData objects to disk in various file formats. The
95 TextureData class represents the raw data of the texture before it
96 has been converted to an OpenGL texture object. The Texture class
97 represents the OpenGL texture object and provides easy facilities
98 for using the texture. </P>
99
100 <P> There are several built-in TextureProviders and TextureWriters
101 supplied with the TextureIO implementation. The most basic
102 provider uses the platform's Image I/O facilities to read in a
103 BufferedImage and convert it to a texture. This is the baseline
104 provider and is registered so that it is the last one consulted.
105 All others are asked first to open a given file. </P>
106
107 <P> There are three other providers registered by default as of
108 the time of this writing. One handles SGI RGB (".sgi", ".rgb")
109 images from both files and streams. One handles DirectDraw Surface
110 (".dds") images read from files, though can not read these images
111 from streams. One handles Targa (".tga") images read from both
112 files and streams. These providers are executed in an arbitrary
113 order. Some of these providers require the file's suffix to either
114 be specified via the newTextureData methods or for the file to be
115 named with the appropriate suffix. In general a file suffix should
116 be provided to the newTexture and newTextureData methods if at all
117 possible. </P>
118
119 <P> Note that additional TextureProviders, if reading images from
120 InputStreams, must use the mark()/reset() methods on InputStream
121 when probing for e.g. magic numbers at the head of the file to
122 make sure not to disturb the state of the InputStream for
123 downstream TextureProviders. </P>
124
125 <P> There are analogous TextureWriters provided for writing
126 textures back to disk if desired. As of this writing, there are
127 four TextureWriters registered by default: one for Targa files,
128 one for SGI RGB files, one for DirectDraw surface (.dds) files,
129 and one for ImageIO-supplied formats such as .jpg and .png. Some
130 of these writers have certain limitations such as only being able
131 to write out textures stored in GL_RGB or GL_RGBA format. The DDS
132 writer supports fetching and writing to disk of texture data in
133 DXTn compressed format. Whether this will occur is dependent on
134 whether the texture's internal format is one of the DXTn
135 compressed formats and whether the target file is .dds format.
136*/
137
138public class TextureIO {
139 /** Constant which can be used as a file suffix to indicate a
140 DirectDraw Surface file, value {@value}.
141 <p>Alias for {@link ImageType#T_DDS}.</p>
142 */
143 public static final String DDS = ImageType.T_DDS;
144
145 /**
146 * Constant which can be used as a file suffix to indicate an SGI RGB file, value {@value}.
147 * <p>
148 * Same semantics as {@link ImageType#SGI_RGB} and {@link #SGI_RGB}.
149 * </p>
150 */
151 public static final String SGI = "sgi";
152
153 /** Constant which can be used as a file suffix to indicate an SGI RGB file, value {@value}.
154 <p>Alias for {@link ImageType#T_SGI_RGB}. </p>
155 */
156 public static final String SGI_RGB = ImageType.T_SGI_RGB;
157
158 /** Constant which can be used as a file suffix to indicate a GIF file, value {@value}.
159 <p>Alias for {@link ImageType#T_GIF}.</p>
160 */
161 public static final String GIF = ImageType.T_GIF;
162
163 /** Constant which can be used as a file suffix to indicate a JPEG file, value {@value}.
164 <p>Alias for {@link ImageType#T_JPG}.</p>
165 */
166 public static final String JPG = ImageType.T_JPG;
167
168 /** Constant which can be used as a file suffix to indicate a PNG file, value {@value}.
169 <p>Alias for {@link ImageType#T_PNG}.</p>
170 */
171 public static final String PNG = ImageType.T_PNG;
172
173 /** Constant which can be used as a file suffix to indicate a Targa file, value {@value}.
174 <p>Alias for {@link ImageType#T_TGA}.</p>
175 */
176 public static final String TGA = ImageType.T_TGA;
177
178 /** Constant which can be used as a file suffix to indicate a TIFF file, value {@value}.
179 <p>Alias for {@link ImageType#T_TIFF}.</p>
180 */
181 public static final String TIFF = ImageType.T_TIFF;
182
183 /** Constant which can be used as a file suffix to indicate a PAM
184 file, NetPbm magic 7 - binary RGB and RGBA. Write support only, value {@value}.
185 <p>Alias for {@link ImageType#T_PAM}.</p>
186 */
187 public static final String PAM = ImageType.T_PAM;
188
189 /** Constant which can be used as a file suffix to indicate a PAM
190 file, NetPbm magic 6 - binary RGB. Write support only, value {@value}.
191 <p>Alias for {@link ImageType#T_PPM}.</p>
192 */
193 public static final String PPM = ImageType.T_PPM;
194
195 private static final boolean DEBUG = Debug.debug("TextureIO");
196
197 // For manually disabling the use of the texture rectangle
198 // extensions so you know the texture target is GL_TEXTURE_2D; this
199 // is useful for shader writers (thanks to Chris Campbell for this
200 // observation)
201 private static boolean texRectEnabled = true;
202
203 //----------------------------------------------------------------------
204 // methods that *do not* require a current context
205 // These methods assume RGB or RGBA textures.
206 // Some texture providers may not recognize the file format unless
207 // the fileSuffix is specified, so it is strongly recommended to
208 // specify it wherever it is known.
209 // Some texture providers may also only support one kind of input,
210 // i.e., reading from a file as opposed to a stream.
211
212 /**
213 * Creates a TextureData from the given file. Does no OpenGL work.
214 *
215 * @param glp the OpenGL Profile this texture data should be
216 * created for.
217 * @param file the file from which to read the texture data
218 * @param mipmap whether mipmaps should be produced for this
219 * texture either by autogenerating them or
220 * reading them from the file. Some file formats
221 * support multiple mipmaps in a single file in
222 * which case those mipmaps will be used rather
223 * than generating them.
224 * @param fileSuffix the suffix of the file name to be used as a
225 * hint of the file format to the underlying
226 * texture provider, or null if none and should be
227 * auto-detected (some texture providers do not
228 * support this)
229 * @return the texture data from the file, or null if none of the
230 * registered texture providers could read the file
231 * @throws IOException if an error occurred while reading the file
232 */
233 public static TextureData newTextureData(final GLProfile glp, final File file,
234 final boolean mipmap,
235 String fileSuffix) throws IOException {
236 if (fileSuffix == null) {
237 fileSuffix = IOUtil.getFileSuffix(file);
238 }
239 return newTextureDataImpl(glp, file, 0, 0, mipmap, fileSuffix);
240 }
241
242 /**
243 * Creates a TextureData from the given stream. Does no OpenGL work.
244 *
245 * @param glp the OpenGL Profile this texture data should be
246 * created for.
247 * @param stream the stream from which to read the texture data
248 * @param mipmap whether mipmaps should be produced for this
249 * texture either by autogenerating them or
250 * reading them from the file. Some file formats
251 * support multiple mipmaps in a single file in
252 * which case those mipmaps will be used rather
253 * than generating them.
254 * @param fileSuffix the suffix of the file name to be used as a
255 * hint of the file format to the underlying
256 * texture provider, or null if none and should be
257 * auto-detected (some texture providers do not
258 * support this)
259 * @return the texture data from the stream, or null if none of the
260 * registered texture providers could read the stream
261 * @throws IOException if an error occurred while reading the stream
262 */
263 public static TextureData newTextureData(final GLProfile glp, final InputStream stream,
264 final boolean mipmap,
265 final String fileSuffix) throws IOException {
266 return newTextureDataImpl(glp, stream, 0, 0, mipmap, fileSuffix);
267 }
268
269 /**
270 * Creates a TextureData from the given URL. Does no OpenGL work.
271 *
272 * @param glp the OpenGL Profile this texture data should be
273 * created for.
274 * @param url the URL from which to read the texture data
275 * @param mipmap whether mipmaps should be produced for this
276 * texture either by autogenerating them or
277 * reading them from the file. Some file formats
278 * support multiple mipmaps in a single file in
279 * which case those mipmaps will be used rather
280 * than generating them.
281 * @param fileSuffix the suffix of the file name to be used as a
282 * hint of the file format to the underlying
283 * texture provider, or null if none and should be
284 * auto-detected (some texture providers do not
285 * support this)
286 * @return the texture data from the URL, or null if none of the
287 * registered texture providers could read the URL
288 * @throws IOException if an error occurred while reading the URL
289 */
290 public static TextureData newTextureData(final GLProfile glp, final URL url,
291 final boolean mipmap,
292 String fileSuffix) throws IOException {
293 if (fileSuffix == null) {
294 fileSuffix = IOUtil.getFileSuffix(url.getPath());
295 }
296 return newTextureDataImpl(glp, url, 0, 0, mipmap, fileSuffix);
297 }
298
299 //----------------------------------------------------------------------
300 // These methods make no assumption about the OpenGL internal format
301 // or pixel format of the texture; they must be specified by the
302 // user. It is not allowed to supply 0 (indicating no preference)
303 // for either the internalFormat or the pixelFormat;
304 // IllegalArgumentException will be thrown in this case.
305
306 /**
307 * Creates a TextureData from the given file, using the specified
308 * OpenGL internal format and pixel format for the texture which
309 * will eventually result. The internalFormat and pixelFormat must
310 * be specified and may not be zero; to use default values, use the
311 * variant of this method which does not take these arguments. Does
312 * no OpenGL work.
313 *
314 * @param glp the OpenGL Profile this texture data should be
315 * created for.
316 * @param file the file from which to read the texture data
317 * @param internalFormat the OpenGL internal format of the texture
318 * which will eventually result from the TextureData
319 * @param pixelFormat the OpenGL pixel format of the texture
320 * which will eventually result from the TextureData
321 * @param mipmap whether mipmaps should be produced for this
322 * texture either by autogenerating them or
323 * reading them from the file. Some file formats
324 * support multiple mipmaps in a single file in
325 * which case those mipmaps will be used rather
326 * than generating them.
327 * @param fileSuffix the suffix of the file name to be used as a
328 * hint of the file format to the underlying
329 * texture provider, or null if none and should be
330 * auto-detected (some texture providers do not
331 * support this)
332 * @return the texture data from the file, or null if none of the
333 * registered texture providers could read the file
334 * @throws IllegalArgumentException if either internalFormat or
335 * pixelFormat was 0
336 * @throws IOException if an error occurred while reading the file
337 */
338 public static TextureData newTextureData(final GLProfile glp, final File file,
339 final int internalFormat,
340 final int pixelFormat,
341 final boolean mipmap,
342 String fileSuffix) throws IOException, IllegalArgumentException {
343 if ((internalFormat == 0) || (pixelFormat == 0)) {
344 throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero");
345 }
346
347 if (fileSuffix == null) {
348 fileSuffix = IOUtil.getFileSuffix(file);
349 }
350
351 return newTextureDataImpl(glp, file, internalFormat, pixelFormat, mipmap, fileSuffix);
352 }
353
354 /**
355 * Creates a TextureData from the given stream, using the specified
356 * OpenGL internal format and pixel format for the texture which
357 * will eventually result. The internalFormat and pixelFormat must
358 * be specified and may not be zero; to use default values, use the
359 * variant of this method which does not take these arguments. Does
360 * no OpenGL work.
361 *
362 * @param glp the OpenGL Profile this texture data should be
363 * created for.
364 * @param stream the stream from which to read the texture data
365 * @param internalFormat the OpenGL internal format of the texture
366 * which will eventually result from the TextureData
367 * @param pixelFormat the OpenGL pixel format of the texture
368 * which will eventually result from the TextureData
369 * @param mipmap whether mipmaps should be produced for this
370 * texture either by autogenerating them or
371 * reading them from the file. Some file formats
372 * support multiple mipmaps in a single file in
373 * which case those mipmaps will be used rather
374 * than generating them.
375 * @param fileSuffix the suffix of the file name to be used as a
376 * hint of the file format to the underlying
377 * texture provider, or null if none and should be
378 * auto-detected (some texture providers do not
379 * support this)
380 * @return the texture data from the stream, or null if none of the
381 * registered texture providers could read the stream
382 * @throws IllegalArgumentException if either internalFormat or
383 * pixelFormat was 0
384 * @throws IOException if an error occurred while reading the stream
385 */
386 public static TextureData newTextureData(final GLProfile glp, final InputStream stream,
387 final int internalFormat,
388 final int pixelFormat,
389 final boolean mipmap,
390 final String fileSuffix) throws IOException, IllegalArgumentException {
391 if ((internalFormat == 0) || (pixelFormat == 0)) {
392 throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero");
393 }
394
395 return newTextureDataImpl(glp, stream, internalFormat, pixelFormat, mipmap, fileSuffix);
396 }
397
398 /**
399 * Creates a TextureData from the given URL, using the specified
400 * OpenGL internal format and pixel format for the texture which
401 * will eventually result. The internalFormat and pixelFormat must
402 * be specified and may not be zero; to use default values, use the
403 * variant of this method which does not take these arguments. Does
404 * no OpenGL work.
405 *
406 * @param glp the OpenGL Profile this texture data should be
407 * created for.
408 * @param url the URL from which to read the texture data
409 * @param internalFormat the OpenGL internal format of the texture
410 * which will eventually result from the TextureData
411 * @param pixelFormat the OpenGL pixel format of the texture
412 * which will eventually result from the TextureData
413 * @param mipmap whether mipmaps should be produced for this
414 * texture either by autogenerating them or
415 * reading them from the file. Some file formats
416 * support multiple mipmaps in a single file in
417 * which case those mipmaps will be used rather
418 * than generating them.
419 * @param fileSuffix the suffix of the file name to be used as a
420 * hint of the file format to the underlying
421 * texture provider, or null if none and should be
422 * auto-detected (some texture providers do not
423 * support this)
424 * @return the texture data from the URL, or null if none of the
425 * registered texture providers could read the URL
426 * @throws IllegalArgumentException if either internalFormat or
427 * pixelFormat was 0
428 * @throws IOException if an error occurred while reading the URL
429 */
430 public static TextureData newTextureData(final GLProfile glp, final URL url,
431 final int internalFormat,
432 final int pixelFormat,
433 final boolean mipmap,
434 String fileSuffix) throws IOException, IllegalArgumentException {
435 if ((internalFormat == 0) || (pixelFormat == 0)) {
436 throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero");
437 }
438
439 if (fileSuffix == null) {
440 fileSuffix = IOUtil.getFileSuffix(url.getPath());
441 }
442
443 return newTextureDataImpl(glp, url, internalFormat, pixelFormat, mipmap, fileSuffix);
444 }
445
446 //----------------------------------------------------------------------
447 // methods that *do* require a current context
448 //
449
450 /**
451 * Creates an OpenGL texture object from the specified TextureData
452 * using the current OpenGL context.
453 *
454 * @param data the texture data to turn into an OpenGL texture
455 * @throws GLException if no OpenGL context is current or if an
456 * OpenGL error occurred
457 * @throws IllegalArgumentException if the passed TextureData was null
458 */
459 public static Texture newTexture(final TextureData data) throws GLException, IllegalArgumentException {
460 return newTexture(GLContext.getCurrentGL(), data);
461 }
462
463 /**
464 * Creates an OpenGL texture object from the specified TextureData
465 * using the given OpenGL context.
466 *
467 * @param data the texture data to turn into an OpenGL texture
468 * @throws GLException if no OpenGL context is current or if an
469 * OpenGL error occurred
470 * @throws IllegalArgumentException if the passed TextureData was null
471 */
472 public static Texture newTexture(final GL gl, final TextureData data) throws GLException, IllegalArgumentException {
473 if (data == null) {
474 throw new IllegalArgumentException("Null TextureData");
475 }
476 return new Texture(gl, data);
477 }
478
479 /**
480 * Creates an OpenGL texture object from the specified file using
481 * the current OpenGL context.
482 *
483 * @param file the file from which to read the texture data
484 * @param mipmap whether mipmaps should be produced for this
485 * texture either by autogenerating them or
486 * reading them from the file. Some file formats
487 * support multiple mipmaps in a single file in
488 * which case those mipmaps will be used rather
489 * than generating them.
490 * @throws IOException if an error occurred while reading the file
491 * @throws GLException if no OpenGL context is current or if an
492 * OpenGL error occurred
493 */
494 public static Texture newTexture(final File file, final boolean mipmap) throws IOException, GLException {
495 final GL gl = GLContext.getCurrentGL();
496 final GLProfile glp = gl.getGLProfile();
497 final TextureData data = newTextureData(glp, file, mipmap, IOUtil.getFileSuffix(file));
498 final Texture texture = newTexture(gl, data);
499 data.flush();
500 return texture;
501 }
502
503 /**
504 * Creates an OpenGL texture object from the specified stream using
505 * the current OpenGL context.
506 *
507 * @param stream the stream from which to read the texture data
508 * @param mipmap whether mipmaps should be produced for this
509 * texture either by autogenerating them or
510 * reading them from the file. Some file formats
511 * support multiple mipmaps in a single file in
512 * which case those mipmaps will be used rather
513 * than generating them.
514 * @param fileSuffix the suffix of the file name to be used as a
515 * hint of the file format to the underlying
516 * texture provider, or null if none and should be
517 * auto-detected (some texture providers do not
518 * support this)
519 * @throws IOException if an error occurred while reading the stream
520 * @throws GLException if no OpenGL context is current or if an
521 * OpenGL error occurred
522 */
523 public static Texture newTexture(final InputStream stream, final boolean mipmap, final String fileSuffix) throws IOException, GLException {
524 final GL gl = GLContext.getCurrentGL();
525 final GLProfile glp = gl.getGLProfile();
526 final TextureData data = newTextureData(glp, stream, mipmap, fileSuffix);
527 final Texture texture = newTexture(gl, data);
528 data.flush();
529 return texture;
530 }
531
532 /**
533 * Creates an OpenGL texture object from the specified URL using the
534 * current OpenGL context.
535 *
536 * @param url the URL from which to read the texture data
537 * @param mipmap whether mipmaps should be produced for this
538 * texture either by autogenerating them or
539 * reading them from the file. Some file formats
540 * support multiple mipmaps in a single file in
541 * which case those mipmaps will be used rather
542 * than generating them.
543 * @param fileSuffix the suffix of the file name to be used as a
544 * hint of the file format to the underlying
545 * texture provider, or null if none and should be
546 * auto-detected (some texture providers do not
547 * support this)
548 * @throws IOException if an error occurred while reading the URL
549 * @throws GLException if no OpenGL context is current or if an
550 * OpenGL error occurred
551 */
552 public static Texture newTexture(final URL url, final boolean mipmap, String fileSuffix) throws IOException, GLException {
553 if (fileSuffix == null) {
554 fileSuffix = IOUtil.getFileSuffix(url.getPath());
555 }
556 final GL gl = GLContext.getCurrentGL();
557 final GLProfile glp = gl.getGLProfile();
558 final TextureData data = newTextureData(glp, url, mipmap, fileSuffix);
559 final Texture texture = newTexture(gl, data);
560 data.flush();
561 return texture;
562 }
563
564 /**
565 * Creates an OpenGL texture object associated with the given OpenGL
566 * texture target. The texture has
567 * no initial data. This is used, for example, to construct cube
568 * maps out of multiple TextureData objects.
569 *
570 * @param target the OpenGL target type, eg GL.GL_TEXTURE_2D,
571 * GL.GL_TEXTURE_RECTANGLE_ARB
572 */
573 public static Texture newTexture(final int target) {
574 return new Texture(target);
575 }
576
577 /**
578 * Writes the given texture to a file. The type of the file is
579 * inferred from its suffix. An OpenGL context must be current in
580 * order to fetch the texture data back from the OpenGL pipeline.
581 * This method causes the specified Texture to be bound to the
582 * GL_TEXTURE_2D state. If no suitable writer for the requested file
583 * format was found, throws an IOException. <P>
584 *
585 * Reasonable attempts are made to produce good results in the
586 * resulting images. The Targa, SGI and ImageIO writers produce
587 * results in the correct vertical orientation for those file
588 * formats. The DDS writer performs no vertical flip of the data,
589 * even in uncompressed mode. (It is impossible to perform such a
590 * vertical flip with compressed data.) Applications should keep
591 * this in mind when using this routine to save textures to disk for
592 * later re-loading. <P>
593 *
594 * Any mipmaps for the specified texture are currently discarded
595 * when it is written to disk, regardless of whether the underlying
596 * file format supports multiple mipmaps in a given file.
597 *
598 * <p>
599 * Method required a {@link GL2GL3} {@link GLProfile#GL2GL3 profile}.
600 * </p>
601 *
602 * @throws IOException if an error occurred during writing or no
603 * suitable writer was found
604 * @throws GLException if no OpenGL context was current or an
605 * OpenGL-related error occurred
606 */
607 public static void write(final Texture texture, final File file) throws IOException, GLException {
608 if (texture.getTarget() != GL.GL_TEXTURE_2D) {
609 throw new GLException("Only GL_TEXTURE_2D textures are supported");
610 }
611
612 // First fetch the texture data
613 final GL _gl = GLContext.getCurrentGL();
614 if (!_gl.isGL2GL3()) {
615 throw new GLException("Implementation only supports GL2GL3 (Use GLReadBufferUtil and the TextureData variant), have: " + _gl);
616 }
617 final GL2GL3 gl = _gl.getGL2GL3();
618
619 texture.bind(gl);
620 final int internalFormat = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2ES3.GL_TEXTURE_INTERNAL_FORMAT);
621 final int width = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2ES3.GL_TEXTURE_WIDTH);
622 final int height = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2ES3.GL_TEXTURE_HEIGHT);
623 final int border = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_BORDER);
624 TextureData data = null;
625 if (internalFormat == GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
626 internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
627 internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
628 internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) {
629 // Fetch using glGetCompressedTexImage
630 final int size = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
631 final ByteBuffer res = ByteBuffer.allocate(size);
633 data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, internalFormat, GL.GL_UNSIGNED_BYTE,
634 false, true, true, res, null);
635 } else {
636 int bytesPerPixel = 0;
637 int fetchedFormat = 0;
638 switch (internalFormat) {
639 case GL.GL_RGB:
640 case GL.GL_BGR:
641 case GL.GL_RGB8:
642 bytesPerPixel = 3;
643 fetchedFormat = GL.GL_RGB;
644 break;
645 case GL.GL_RGBA:
646 case GL.GL_BGRA:
647 case GL2.GL_ABGR_EXT:
648 case GL.GL_RGBA8:
649 bytesPerPixel = 4;
650 fetchedFormat = GL.GL_RGBA;
651 break;
652 default:
653 throw new IOException("Unsupported texture internal format 0x" + Integer.toHexString(internalFormat));
654 }
655
656 // Fetch using glGetTexImage
658 psm.setPackAlignment(gl, 1);
659
660 final ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) *
661 (height + (2 * border)) *
662 bytesPerPixel);
663 if (DEBUG) {
664 System.out.println("Allocated buffer of size " + res.remaining() + " for fetched image (" +
665 ((fetchedFormat == GL.GL_RGB) ? "GL_RGB" : "GL_RGBA") + ")");
666 }
667 gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, fetchedFormat, GL.GL_UNSIGNED_BYTE, res);
668
669 psm.restore(gl);
670
671 data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE,
672 false, false, false, res, null);
673
674 if (DEBUG) {
675 System.out.println("data.getPixelFormat() = " +
676 ((data.getPixelFormat() == GL.GL_RGB) ? "GL_RGB" : "GL_RGBA"));
677 }
678 }
679
680 write(data, file);
681 }
682
683 public static void write(final TextureData data, final File file) throws IOException, GLException {
684 for (final Iterator<TextureWriter> iter = textureWriters.iterator(); iter.hasNext(); ) {
685 final TextureWriter writer = iter.next();
686 if (writer.write(file, data)) {
687 return;
688 }
689 }
690
691 throw new IOException("No suitable texture writer found for "+file.getAbsolutePath());
692 }
693
694 //----------------------------------------------------------------------
695 // SPI support
696 //
697
698 /**
699 * Adds a {@link TextureProvider} to support reading of a new file format.
700 * <p>
701 * The last provider added, will be the first provider to be tested.
702 * </p>
703 * <p>
704 * The {@link TextureProvider} is being mapped to its supporting {@link ImageType}s
705 * allowing an O(1) association, if {@link TextureProvider#}
706 * </p>
707 */
708 public static void addTextureProvider(final TextureProvider provider) {
709 // Must always add at the front so the ImageIO provider is last,
710 // so we don't accidentally use it instead of a user's possibly
711 // more optimal provider
712 textureProviders.add(0, provider);
713
714 final ImageType[] imageTypes = provider.getImageTypes();
715 if( null != imageTypes ) {
716 for(int i=0; i<imageTypes.length; i++) {
717 imageType2TextureProvider.put(imageTypes[i], provider);
718 }
719 }
720 }
721
722 /**
723 * Adds a TextureWriter to support writing of a new file format.
724 * <p>
725 * The last provider added, will be the first provider to be tested.
726 * </p>
727 */
728 public static void addTextureWriter(final TextureWriter writer) {
729 // Must always add at the front so the ImageIO writer is last,
730 // so we don't accidentally use it instead of a user's possibly
731 // more optimal writer
732 textureWriters.add(0, writer);
733 }
734
735 //---------------------------------------------------------------------------
736 // Global disabling of texture rectangle extension
737 //
738
739 /** Toggles the use of the GL_ARB_texture_rectangle extension by the
740 TextureIO classes. By default, on hardware supporting this
741 extension, the TextureIO classes may use the
742 GL_ARB_texture_rectangle extension for non-power-of-two
743 textures. (If the hardware supports the
744 GL_ARB_texture_non_power_of_two extension, that one is
745 preferred.) In some situations, for example when writing
746 shaders, it is advantageous to force the texture target to
747 always be GL_TEXTURE_2D in order to have one version of the
748 shader, even at the expense of texture memory in the case where
749 NPOT textures are not supported. This method allows the use of
750 the GL_ARB_texture_rectangle extension to be turned off globally
751 for this purpose. The default is that the use of the extension
752 is enabled. */
753 public static void setTexRectEnabled(final boolean enabled) {
754 texRectEnabled = enabled;
755 }
756
757 /** Indicates whether the GL_ARB_texture_rectangle extension is
758 allowed to be used for non-power-of-two textures; see {@link
759 #setTexRectEnabled setTexRectEnabled}. */
760 public static boolean isTexRectEnabled() {
761 return texRectEnabled;
762 }
763
764 //----------------------------------------------------------------------
765 // Internals only below this point
766 //
767
768 private static List<TextureProvider> textureProviders = new ArrayList<TextureProvider>();
769 private static Map<ImageType,TextureProvider> imageType2TextureProvider = new HashMap<ImageType,TextureProvider>();
770 private static List<TextureWriter> textureWriters = new ArrayList<TextureWriter>();
771
772 static {
773 // ImageIO provider, the fall-back, must be the first one added
774 if(GLProfile.isAWTAvailable()) {
775 try {
776 // Use reflection to avoid compile-time dependencies on AWT-related classes
777 final TextureProvider provider = (TextureProvider)
778 Class.forName("com.jogamp.opengl.util.texture.spi.awt.IIOTextureProvider").newInstance();
779 addTextureProvider(provider);
780 } catch (final Exception e) {
781 if (DEBUG) {
782 e.printStackTrace();
783 }
784 }
785 }
786
787 // Other special-case providers
788 addTextureProvider(new DDSTextureProvider());
789 addTextureProvider(new SGITextureProvider());
790 addTextureProvider(new TGATextureProvider());
791 addTextureProvider(new JPGTextureProvider());
792 addTextureProvider(new PNGTextureProvider());
793
794 // ImageIO writer, the fall-back, must be the first one added
795 if(GLProfile.isAWTAvailable()) {
796 try {
797 // Use reflection to avoid compile-time dependencies on AWT-related classes
798 final TextureWriter writer = (TextureWriter)
799 Class.forName("com.jogamp.opengl.util.texture.spi.awt.IIOTextureWriter").newInstance();
800 addTextureWriter(writer);
801 } catch (final Exception e) {
802 if (DEBUG) {
803 e.printStackTrace();
804 }
805 } catch (final Error e) {
806 if (DEBUG) {
807 e.printStackTrace();
808 }
809 }
810 }
811
812 // Other special-case writers
813 addTextureWriter(new DDSTextureWriter());
814 addTextureWriter(new SGITextureWriter());
815 addTextureWriter(new TGATextureWriter());
816 addTextureWriter(new NetPbmTextureWriter());
817 addTextureWriter(new PNGTextureWriter());
818 }
819
820 // Implementation methods
821 private static TextureData newTextureDataImpl(final GLProfile glp, InputStream stream,
822 final int internalFormat,
823 final int pixelFormat,
824 final boolean mipmap,
825 String fileSuffix) throws IOException {
826 if (stream == null) {
827 throw new IOException("Stream was null");
828 }
829
830 // Note: use of BufferedInputStream works around 4764639/4892246
831 if (!(stream instanceof BufferedInputStream)) {
832 stream = new BufferedInputStream(stream);
833 }
834
835 // First attempt to use an ImageType mapped TextureProvider for O(1)
836 // using stream parsed data, ignoring the given fileSuffix!
837 try {
838 final ImageType imageType = new ImageType(stream);
839 if( imageType.isDefined() ) {
840 final TextureProvider mappedProvider = imageType2TextureProvider.get(imageType);
841 if( null != mappedProvider ) {
842 final TextureData data = mappedProvider.newTextureData(glp, stream,
843 internalFormat,
844 pixelFormat,
845 mipmap,
846 imageType.type);
847 if (data != null) {
848 data.srcImageType = imageType;
849 return data;
850 }
851 }
852 }
853 } catch (final IOException ioe) {
854 if(DEBUG) {
855 System.err.println("Caught "+ioe.getMessage());
856 ioe.printStackTrace();
857 }
858 }
859
860 fileSuffix = toLowerCase(fileSuffix);
861
862 for (final Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) {
863 final TextureProvider provider = iter.next();
864 final TextureData data = provider.newTextureData(glp, stream,
865 internalFormat,
866 pixelFormat,
867 mipmap,
868 fileSuffix);
869 if (data != null) {
870 final ImageType[] imageTypes = provider.getImageTypes();
871 data.srcImageType = null != imageTypes ? imageTypes[0] : null;
872 return data;
873 }
874 }
875
876 throw new IOException("No suitable reader for given stream");
877 }
878 private static TextureData newTextureDataImpl(final GLProfile glp, final File file,
879 final int internalFormat,
880 final int pixelFormat,
881 final boolean mipmap,
882 final String fileSuffix) throws IOException {
883 if (file == null) {
884 throw new IOException("File was null");
885 }
886 final InputStream stream = new BufferedInputStream(new FileInputStream(file));
887 try {
888 return newTextureDataImpl( glp, stream, internalFormat, pixelFormat, mipmap,
889 (fileSuffix != null) ? fileSuffix : IOUtil.getFileSuffix(file) );
890 } catch(final IOException ioe) {
891 throw new IOException(ioe.getMessage()+", given file "+file.getAbsolutePath(), ioe);
892 } finally {
893 stream.close();
894 }
895 }
896 private static TextureData newTextureDataImpl(final GLProfile glp, final URL url,
897 final int internalFormat,
898 final int pixelFormat,
899 final boolean mipmap,
900 final String fileSuffix) throws IOException {
901 if (url == null) {
902 throw new IOException("URL was null");
903 }
904 final InputStream stream = new BufferedInputStream(url.openStream());
905 try {
906 return newTextureDataImpl(glp, stream, internalFormat, pixelFormat, mipmap, fileSuffix);
907 } catch(final IOException ioe) {
908 throw new IOException(ioe.getMessage()+", given URL "+url, ioe);
909 } finally {
910 stream.close();
911 }
912 }
913
914 //----------------------------------------------------------------------
915 // DDS image provider
916 static class DDSTextureProvider implements TextureProvider {
917 private static final ImageType[] imageTypes = new ImageType[] { new ImageType(ImageType.T_DDS) };
918 @Override
919 public final ImageType[] getImageTypes() {
920 return imageTypes;
921 }
922
923 @Override
924 public TextureData newTextureData(final GLProfile glp, final InputStream stream,
925 final int internalFormat,
926 final int pixelFormat,
927 final boolean mipmap,
928 final String fileSuffix) throws IOException {
929 if (ImageType.T_DDS.equals(fileSuffix) ||
930 ImageType.T_DDS.equals(ImageType.Util.getFileSuffix(stream))) {
931 final byte[] data = IOUtil.copyStream2ByteArray(stream);
932 final ByteBuffer buf = ByteBuffer.wrap(data);
933 final DDSImage image = DDSImage.read(buf);
934 return newTextureData(glp, image, internalFormat, pixelFormat, mipmap);
935 }
936
937 return null;
938 }
939
940 private TextureData newTextureData(final GLProfile glp, final DDSImage image,
941 int internalFormat,
942 int pixelFormat,
943 boolean mipmap) {
944 final DDSImage.ImageInfo info = image.getMipMap(0);
945 if (pixelFormat == 0) {
946 switch (image.getPixelFormat()) {
947 case DDSImage.D3DFMT_R8G8B8:
948 pixelFormat = GL.GL_RGB;
949 break;
950 default:
951 pixelFormat = GL.GL_RGBA;
952 break;
953 }
954 }
955 if (info.isCompressed()) {
956 switch (info.getCompressionFormat()) {
957 case DDSImage.D3DFMT_DXT1:
958 internalFormat = GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
959 break;
960 case DDSImage.D3DFMT_DXT3:
961 internalFormat = GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
962 break;
963 case DDSImage.D3DFMT_DXT5:
964 internalFormat = GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
965 break;
966 default:
967 throw new RuntimeException("Unsupported DDS compression format \"" +
968 DDSImage.getCompressionFormatName(info.getCompressionFormat()) + "\"");
969 }
970 }
971 if (internalFormat == 0) {
972 switch (image.getPixelFormat()) {
973 case DDSImage.D3DFMT_R8G8B8:
974 pixelFormat = GL.GL_RGB;
975 break;
976 default:
977 pixelFormat = GL.GL_RGBA;
978 break;
979 }
980 }
981 final TextureData.Flusher flusher = new TextureData.Flusher() {
982 @Override
983 public void flush() {
984 image.close();
985 }
986 };
987 TextureData data;
988 if (mipmap && image.getNumMipMaps() > 0) {
989 final Buffer[] mipmapData = new Buffer[image.getNumMipMaps()];
990 for (int i = 0; i < image.getNumMipMaps(); i++) {
991 mipmapData[i] = image.getMipMap(i).getData();
992 }
993 data = new TextureData(glp, internalFormat,
994 info.getWidth(),
995 info.getHeight(),
996 0,
997 pixelFormat,
998 GL.GL_UNSIGNED_BYTE,
999 info.isCompressed(),
1000 true,
1001 mipmapData,
1002 flusher);
1003 } else {
1004 // Fix this up for the end user because we can't generate
1005 // mipmaps for compressed textures
1006 mipmap = false;
1007 data = new TextureData(glp, internalFormat,
1008 info.getWidth(),
1009 info.getHeight(),
1010 0,
1011 pixelFormat,
1012 GL.GL_UNSIGNED_BYTE,
1013 mipmap,
1014 info.isCompressed(),
1015 true,
1016 info.getData(),
1017 flusher);
1018 }
1019 return data;
1020 }
1021 }
1022
1023 //----------------------------------------------------------------------
1024 // SGI RGB image provider
1025 static class SGITextureProvider implements TextureProvider {
1026 private static final ImageType[] imageTypes = new ImageType[] { new ImageType(ImageType.T_SGI_RGB) };
1027 @Override
1028 public final ImageType[] getImageTypes() {
1029 return imageTypes;
1030 }
1031
1032 @Override
1033 public TextureData newTextureData(final GLProfile glp, final InputStream stream,
1034 int internalFormat,
1035 int pixelFormat,
1036 final boolean mipmap,
1037 final String fileSuffix) throws IOException {
1038 if (SGI.equals(fileSuffix) ||
1039 ImageType.T_SGI_RGB.equals(fileSuffix) ||
1040 SGI.equals(ImageType.Util.getFileSuffix(stream)) ||
1041 ImageType.T_SGI_RGB.equals(ImageType.Util.getFileSuffix(stream))) {
1042 final SGIImage image = SGIImage.read(stream);
1043 if (pixelFormat == 0) {
1044 pixelFormat = image.getFormat();
1045 }
1046 if (internalFormat == 0) {
1047 internalFormat = image.getFormat();
1048 }
1049 return new TextureData(glp, internalFormat,
1050 image.getWidth(),
1051 image.getHeight(),
1052 0,
1053 pixelFormat,
1054 GL.GL_UNSIGNED_BYTE,
1055 mipmap,
1056 false,
1057 false,
1058 ByteBuffer.wrap(image.getData()),
1059 null);
1060 }
1061
1062 return null;
1063 }
1064 }
1065
1066 //----------------------------------------------------------------------
1067 // TGA (Targa) image provider
1068 static class TGATextureProvider implements TextureProvider {
1069 private static final ImageType[] imageTypes = new ImageType[] { new ImageType(ImageType.T_TGA) };
1070 @Override
1071 public final ImageType[] getImageTypes() {
1072 return imageTypes;
1073 }
1074
1075 @Override
1076 public TextureData newTextureData(final GLProfile glp, final InputStream stream,
1077 int internalFormat,
1078 int pixelFormat,
1079 final boolean mipmap,
1080 final String fileSuffix) throws IOException {
1081 if (ImageType.T_TGA.equals(fileSuffix)) {
1082 final TGAImage image = TGAImage.read(glp, stream);
1083 if (pixelFormat == 0) {
1084 pixelFormat = image.getGLFormat();
1085 }
1086 if (internalFormat == 0) {
1087 if(glp.isGL2ES3()) {
1088 internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8;
1089 } else {
1090 internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB;
1091 }
1092 }
1093 return new TextureData(glp, internalFormat,
1094 image.getWidth(),
1095 image.getHeight(),
1096 0,
1097 pixelFormat,
1098 GL.GL_UNSIGNED_BYTE,
1099 mipmap,
1100 false,
1101 false,
1102 image.getData(),
1103 null);
1104 }
1105
1106 return null;
1107 }
1108 }
1109
1110 //----------------------------------------------------------------------
1111 // PNG image provider
1112 static class PNGTextureProvider implements TextureProvider {
1113 private static final ImageType[] imageTypes = new ImageType[] { new ImageType(ImageType.T_PNG) };
1114 @Override
1115 public final ImageType[] getImageTypes() {
1116 return imageTypes;
1117 }
1118
1119 @Override
1120 public TextureData newTextureData(final GLProfile glp, final InputStream stream,
1121 int internalFormat,
1122 int pixelFormat,
1123 final boolean mipmap,
1124 final String fileSuffix) throws IOException {
1125 if (ImageType.T_PNG.equals(fileSuffix) ||
1126 ImageType.T_PNG.equals(ImageType.Util.getFileSuffix(stream))) {
1127 final PNGPixelRect image = PNGPixelRect.read(stream, null, true /* directBuffer */, 0 /* destMinStrideInBytes */, true /* destIsGLOriented */);
1128 final GLPixelAttributes glpa = new GLPixelAttributes(glp, image.getPixelformat(), false /* pack */);
1129 if ( 0 == pixelFormat ) {
1130 pixelFormat = glpa.format;
1131 } // else FIXME: Actually not supported w/ preset pixelFormat!
1132 if ( 0 == internalFormat ) {
1133 final boolean hasAlpha = 4 == glpa.pfmt.comp.bytesPerPixel();
1134 if(glp.isGL2ES3()) {
1135 internalFormat = hasAlpha ? GL.GL_RGBA8 : GL.GL_RGB8;
1136 } else {
1137 internalFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB;
1138 }
1139 }
1140 return new TextureData(glp, internalFormat,
1141 image.getSize().getWidth(),
1142 image.getSize().getHeight(),
1143 0,
1144 pixelFormat,
1145 glpa.type,
1146 mipmap,
1147 false,
1148 false,
1149 image.getPixels(),
1150 null);
1151 }
1152
1153 return null;
1154 }
1155 }
1156
1157 //----------------------------------------------------------------------
1158 // JPEG image provider
1159 static class JPGTextureProvider implements TextureProvider {
1160 private static final ImageType[] imageTypes = new ImageType[] { new ImageType(ImageType.T_JPG) };
1161 @Override
1162 public final ImageType[] getImageTypes() {
1163 return imageTypes;
1164 }
1165
1166 @Override
1167 public TextureData newTextureData(final GLProfile glp, final InputStream stream,
1168 int internalFormat,
1169 int pixelFormat,
1170 final boolean mipmap,
1171 final String fileSuffix) throws IOException {
1172 if (ImageType.T_JPG.equals(fileSuffix) ||
1173 ImageType.T_JPG.equals(ImageType.Util.getFileSuffix(stream))) {
1174 final JPEGImage image = JPEGImage.read(/*glp, */ stream);
1175 if (pixelFormat == 0) {
1176 pixelFormat = image.getGLFormat();
1177 }
1178 if (internalFormat == 0) {
1179 if(glp.isGL2ES3()) {
1180 internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8;
1181 } else {
1182 internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB;
1183 }
1184 }
1185 return new TextureData(glp, internalFormat,
1186 image.getWidth(),
1187 image.getHeight(),
1188 0,
1189 pixelFormat,
1190 image.getGLType(),
1191 mipmap,
1192 false,
1193 false,
1194 image.getData(),
1195 null);
1196 }
1197
1198 return null;
1199 }
1200 }
1201
1202 //----------------------------------------------------------------------
1203 // DDS texture writer
1204 //
1205 static class DDSTextureWriter implements TextureWriter {
1206 @Override
1207 public boolean write(final File file,
1208 final TextureData data) throws IOException {
1209 if (ImageType.T_DDS.equals(IOUtil.getFileSuffix(file))) {
1210 // See whether the DDS writer can handle this TextureData
1211 final GLPixelAttributes pixelAttribs = data.getPixelAttributes();
1212 final int pixelFormat = pixelAttribs.format;
1213 final int pixelType = pixelAttribs.type;
1214 if (pixelType != GL.GL_BYTE &&
1215 pixelType != GL.GL_UNSIGNED_BYTE) {
1216 throw new IOException("DDS writer only supports byte / unsigned byte textures");
1217 }
1218
1219 int d3dFormat = 0;
1220 // FIXME: some of these are probably not completely correct and would require swizzling
1221 switch (pixelFormat) {
1222 case GL.GL_RGB: d3dFormat = DDSImage.D3DFMT_R8G8B8; break;
1223 case GL.GL_RGBA: d3dFormat = DDSImage.D3DFMT_A8R8G8B8; break;
1224 case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: d3dFormat = DDSImage.D3DFMT_DXT1; break;
1225 case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: throw new IOException("RGBA DXT1 not yet supported");
1226 case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: d3dFormat = DDSImage.D3DFMT_DXT3; break;
1227 case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: d3dFormat = DDSImage.D3DFMT_DXT5; break;
1228 default: throw new IOException("Unsupported pixel format 0x" + Integer.toHexString(pixelFormat) + " by DDS writer");
1229 }
1230
1231 ByteBuffer[] mipmaps = null;
1232 if (data.getMipmapData() != null) {
1233 mipmaps = new ByteBuffer[data.getMipmapData().length];
1234 for (int i = 0; i < mipmaps.length; i++) {
1235 mipmaps[i] = (ByteBuffer) data.getMipmapData()[i];
1236 }
1237 } else {
1238 mipmaps = new ByteBuffer[] { (ByteBuffer) data.getBuffer() };
1239 }
1240
1241 final DDSImage image = DDSImage.createFromData(d3dFormat,
1242 data.getWidth(),
1243 data.getHeight(),
1244 mipmaps);
1245 image.write(file);
1246 return true;
1247 }
1248
1249 return false;
1250 }
1251 }
1252
1253 //----------------------------------------------------------------------
1254 // SGI (rgb) texture writer
1255 //
1256 static class SGITextureWriter implements TextureWriter {
1257 @Override
1258 public boolean write(final File file,
1259 final TextureData data) throws IOException {
1260 final String fileSuffix = IOUtil.getFileSuffix(file);
1261 if (SGI.equals(fileSuffix) ||
1262 ImageType.T_SGI_RGB.equals(fileSuffix)) {
1263 // See whether the SGI writer can handle this TextureData
1264 final GLPixelAttributes pixelAttribs = data.getPixelAttributes();
1265 final int pixelFormat = pixelAttribs.format;
1266 final int pixelType = pixelAttribs.type;
1267 if ((pixelFormat == GL.GL_RGB ||
1268 pixelFormat == GL.GL_RGBA) &&
1269 (pixelType == GL.GL_BYTE ||
1270 pixelType == GL.GL_UNSIGNED_BYTE)) {
1271 final ByteBuffer buf = ((data.getBuffer() != null) ?
1272 (ByteBuffer) data.getBuffer() :
1273 (ByteBuffer) data.getMipmapData()[0]);
1274 byte[] bytes;
1275 if (buf.hasArray()) {
1276 bytes = buf.array();
1277 } else {
1278 buf.rewind();
1279 bytes = new byte[buf.remaining()];
1280 buf.get(bytes);
1281 buf.rewind();
1282 }
1283
1284 final SGIImage image = SGIImage.createFromData(data.getWidth(),
1285 data.getHeight(),
1286 (pixelFormat == GL.GL_RGBA),
1287 bytes);
1288 image.write(file, false);
1289 return true;
1290 }
1291
1292 throw new IOException("SGI writer doesn't support this pixel format / type (only GL_RGB/A + bytes)");
1293 }
1294
1295 return false;
1296 }
1297 }
1298
1299 //----------------------------------------------------------------------
1300 // TGA (Targa) texture writer
1301
1302 static class TGATextureWriter implements TextureWriter {
1303 @Override
1304 public boolean write(final File file,
1305 final TextureData data) throws IOException {
1306 if (ImageType.T_TGA.equals(IOUtil.getFileSuffix(file))) {
1307 // See whether the TGA writer can handle this TextureData
1308 final GLPixelAttributes pixelAttribs = data.getPixelAttributes();
1309 final int pixelFormat = pixelAttribs.format;
1310 final int pixelType = pixelAttribs.type;
1311 if ((pixelFormat == GL.GL_RGB ||
1312 pixelFormat == GL.GL_RGBA ||
1313 pixelFormat == GL.GL_BGR ||
1314 pixelFormat == GL.GL_BGRA ) &&
1315 (pixelType == GL.GL_BYTE ||
1316 pixelType == GL.GL_UNSIGNED_BYTE)) {
1317
1318 ByteBuffer buf = (ByteBuffer) data.getBuffer();
1319 if (null == buf) {
1320 buf = (ByteBuffer) data.getMipmapData()[0];
1321 }
1322 buf.rewind();
1323
1324 if( pixelFormat == GL.GL_RGB || pixelFormat == GL.GL_RGBA ) {
1325 // Must reverse order of red and blue channels to get correct results
1326 final int skip = ((pixelFormat == GL.GL_RGB) ? 3 : 4);
1327 for (int i = 0; i < buf.remaining(); i += skip) {
1328 final byte red = buf.get(i + 0);
1329 final byte blue = buf.get(i + 2);
1330 buf.put(i + 0, blue);
1331 buf.put(i + 2, red);
1332 }
1333 }
1334
1335 final TGAImage image = TGAImage.createFromData(data.getWidth(),
1336 data.getHeight(),
1337 (pixelFormat == GL.GL_RGBA || pixelFormat == GL.GL_BGRA),
1338 false, buf);
1339 image.write(file);
1340 return true;
1341 }
1342 throw new IOException("TGA writer doesn't support this pixel format 0x"+Integer.toHexString(pixelFormat)+
1343 " / type 0x"+Integer.toHexString(pixelFormat)+" (only GL_RGB/A, GL_BGR/A + bytes)");
1344 }
1345
1346 return false;
1347 }
1348 }
1349
1350 //----------------------------------------------------------------------
1351 // PNG texture writer
1352
1353 static class PNGTextureWriter implements TextureWriter {
1354 @Override
1355 public boolean write(final File file, final TextureData data) throws IOException {
1356 if (ImageType.T_PNG.equals(IOUtil.getFileSuffix(file))) {
1357 // See whether the PNG writer can handle this TextureData
1358 final GLPixelAttributes pixelAttribs = data.getPixelAttributes();
1359 final int pixelFormat = pixelAttribs.format;
1360 final int pixelType = pixelAttribs.type;
1361 final int bytesPerPixel = pixelAttribs.pfmt.comp.bytesPerPixel();
1362 final PixelFormat pixFmt = pixelAttribs.pfmt;
1363 if ( ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel) &&
1364 ( pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) {
1365 Buffer buf0 = data.getBuffer();
1366 if (null == buf0) {
1367 buf0 = data.getMipmapData()[0];
1368 }
1369 if( null == buf0 ) {
1370 throw new IOException("Pixel storage buffer is null");
1371 }
1372 final DimensionImmutable size = new Dimension(data.getWidth(), data.getHeight());
1373 if( buf0 instanceof ByteBuffer ) {
1374 final ByteBuffer buf = (ByteBuffer) buf0;
1375 buf.rewind();
1376 final PNGPixelRect image = new PNGPixelRect(pixFmt, size,
1377 0 /* stride */, !data.getMustFlipVertically() /* isGLOriented */, buf /* pixels */,
1378 -1f, -1f);
1379 final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(file, true /* allowOverwrite */));
1380 image.write(outs, true /* close */);
1381 return true;
1382 } else if( buf0 instanceof IntBuffer ) {
1383 final IntBuffer buf = (IntBuffer) buf0;
1384 buf.rewind();
1385 final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(file, true /* allowOverwrite */));
1386 PNGPixelRect.write(pixFmt, size,
1387 0 /* stride */, !data.getMustFlipVertically() /* isGLOriented */, buf /* pixels */,
1388 -1f, -1f, outs, true /* closeOutstream */);
1389 return true;
1390 } else {
1391 throw new IOException("PNG writer doesn't support pixel storage buffer of type "+buf0.getClass().getName());
1392 }
1393 }
1394 throw new IOException("PNG writer doesn't support this pixel format 0x"+Integer.toHexString(pixelFormat)+
1395 " / type 0x"+Integer.toHexString(pixelFormat)+" (only GL_RGB/A, GL_BGR/A + bytes)");
1396 }
1397 return false;
1398 }
1399 }
1400
1401 //----------------------------------------------------------------------
1402 // Helper routines
1403 //
1404
1405 private static int glGetTexLevelParameteri(final GL2GL3 gl, final int target, final int level, final int pname) {
1406 final int[] tmp = new int[1];
1407 gl.glGetTexLevelParameteriv(target, 0, pname, tmp, 0);
1408 return tmp[0];
1409 }
1410
1411 private static String toLowerCase(final String arg) {
1412 if (arg == null) {
1413 return null;
1414 }
1415
1416 return arg.toLowerCase();
1417 }
1418}
Abstraction for an OpenGL rendering context.
Definition: GLContext.java:74
static GL getCurrentGL()
Returns the GL object bound to this thread current context.
Definition: GLContext.java:500
A generic exception for OpenGL errors used throughout the binding as a substitute for RuntimeExceptio...
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
final int format
The OpenGL pixel data format.
Utility to safely set and restore the PACK and UNPACK pixel storage mode, regardless of the GLProfile...
final void restore(final GL gl)
Restores PACK and UNPACK pixel storage mode previously saved w/ saveAll(GL) or savePack(GL) and saveU...
final void setPackAlignment(final GL gl, final int packAlignment)
Sets the GL#GL_PACK_ALIGNMENT.
Image type classification.
Definition: ImageType.java:42
static final String T_GIF
Constant which can be used as a file suffix to indicate a GIF stream, value {@value}.
Definition: ImageType.java:107
static final String T_TGA
Constant which can be used as a file suffix to indicate a Targa stream, value {@value}.
static final String T_SGI_RGB
Constant which can be used as a file suffix to indicate an SGI RGB stream, value {@value}.
Definition: ImageType.java:209
static final String T_PPM
Constant which can be used as a file suffix to indicate a PAM stream, NetPbm magic 6 - binary RGB.
Definition: ImageType.java:162
static final String T_PNG
Constant which can be used as a file suffix to indicate a PNG stream, value {@value}.
Definition: ImageType.java:63
static final String T_TIFF
Constant which can be used as a file suffix to indicate a TIFF stream, value {@value}.
Definition: ImageType.java:187
static final String T_PAM
Constant which can be used as a file suffix to indicate a Portable Arbitrary Map stream,...
Definition: ImageType.java:228
static final String T_DDS
Constant which can be used as a file suffix to indicate a DirectDraw Surface stream,...
Definition: ImageType.java:220
static final String T_JPG
Constant which can be used as a file suffix to indicate a JPEG stream, value {@value}.
Definition: ImageType.java:55
Represents the data for an OpenGL texture.
GLPixelAttributes getPixelAttributes()
Returns the intended OpenGL GLPixelAttributes of the texture data, i.e.
void flush()
Flushes resources associated with this TextureData by calling Flusher.flush().
int getPixelFormat()
Returns the intended OpenGL pixel format of the texture data using getPixelAttributes().
static TextureData newTextureData(final GLProfile glp, final InputStream stream, final boolean mipmap, final String fileSuffix)
Creates a TextureData from the given stream.
Definition: TextureIO.java:263
static final String TGA
Constant which can be used as a file suffix to indicate a Targa file, value {@value}.
Definition: TextureIO.java:176
static final String PAM
Constant which can be used as a file suffix to indicate a PAM file, NetPbm magic 7 - binary RGB and R...
Definition: TextureIO.java:187
static final String DDS
Constant which can be used as a file suffix to indicate a DirectDraw Surface file,...
Definition: TextureIO.java:143
static final String PNG
Constant which can be used as a file suffix to indicate a PNG file, value {@value}.
Definition: TextureIO.java:171
static boolean isTexRectEnabled()
Indicates whether the GL_ARB_texture_rectangle extension is allowed to be used for non-power-of-two t...
Definition: TextureIO.java:760
static Texture newTexture(final GL gl, final TextureData data)
Creates an OpenGL texture object from the specified TextureData using the given OpenGL context.
Definition: TextureIO.java:472
static final String SGI
Constant which can be used as a file suffix to indicate an SGI RGB file, value {@value}.
Definition: TextureIO.java:151
static TextureData newTextureData(final GLProfile glp, final File file, final int internalFormat, final int pixelFormat, final boolean mipmap, String fileSuffix)
Creates a TextureData from the given file, using the specified OpenGL internal format and pixel forma...
Definition: TextureIO.java:338
static void addTextureWriter(final TextureWriter writer)
Adds a TextureWriter to support writing of a new file format.
Definition: TextureIO.java:728
static Texture newTexture(final File file, final boolean mipmap)
Creates an OpenGL texture object from the specified file using the current OpenGL context.
Definition: TextureIO.java:494
static TextureData newTextureData(final GLProfile glp, final URL url, final int internalFormat, final int pixelFormat, final boolean mipmap, String fileSuffix)
Creates a TextureData from the given URL, using the specified OpenGL internal format and pixel format...
Definition: TextureIO.java:430
static Texture newTexture(final InputStream stream, final boolean mipmap, final String fileSuffix)
Creates an OpenGL texture object from the specified stream using the current OpenGL context.
Definition: TextureIO.java:523
static Texture newTexture(final URL url, final boolean mipmap, String fileSuffix)
Creates an OpenGL texture object from the specified URL using the current OpenGL context.
Definition: TextureIO.java:552
static void write(final TextureData data, final File file)
Definition: TextureIO.java:683
static Texture newTexture(final int target)
Creates an OpenGL texture object associated with the given OpenGL texture target.
Definition: TextureIO.java:573
static void setTexRectEnabled(final boolean enabled)
Toggles the use of the GL_ARB_texture_rectangle extension by the TextureIO classes.
Definition: TextureIO.java:753
static final String PPM
Constant which can be used as a file suffix to indicate a PAM file, NetPbm magic 6 - binary RGB.
Definition: TextureIO.java:193
static final String GIF
Constant which can be used as a file suffix to indicate a GIF file, value {@value}.
Definition: TextureIO.java:161
static void addTextureProvider(final TextureProvider provider)
Adds a TextureProvider to support reading of a new file format.
Definition: TextureIO.java:708
static TextureData newTextureData(final GLProfile glp, final File file, final boolean mipmap, String fileSuffix)
Creates a TextureData from the given file.
Definition: TextureIO.java:233
static TextureData newTextureData(final GLProfile glp, final URL url, final boolean mipmap, String fileSuffix)
Creates a TextureData from the given URL.
Definition: TextureIO.java:290
static final String JPG
Constant which can be used as a file suffix to indicate a JPEG file, value {@value}.
Definition: TextureIO.java:166
static final String TIFF
Constant which can be used as a file suffix to indicate a TIFF file, value {@value}.
Definition: TextureIO.java:181
static final String SGI_RGB
Constant which can be used as a file suffix to indicate an SGI RGB file, value {@value}.
Definition: TextureIO.java:156
static Texture newTexture(final TextureData data)
Creates an OpenGL texture object from the specified TextureData using the current OpenGL context.
Definition: TextureIO.java:459
static void write(final Texture texture, final File file)
Writes the given texture to a file.
Definition: TextureIO.java:607
static TextureData newTextureData(final GLProfile glp, final InputStream stream, final int internalFormat, final int pixelFormat, final boolean mipmap, final String fileSuffix)
Creates a TextureData from the given stream, using the specified OpenGL internal format and pixel for...
Definition: TextureIO.java:386
Represents an OpenGL texture object.
Definition: Texture.java:173
static final int GL_TEXTURE_HEIGHT
GL_ES_VERSION_3_1, GL_VERSION_1_1, GL_VERSION_1_0, GL_QCOM_extended_get Alias for: GL_TEXTURE_HEIGHT_...
Definition: GL2ES3.java:607
static final int GL_TEXTURE_WIDTH
GL_ES_VERSION_3_1, GL_VERSION_1_1, GL_VERSION_1_0, GL_QCOM_extended_get Alias for: GL_TEXTURE_WIDTH_Q...
Definition: GL2ES3.java:676
static final int GL_TEXTURE_INTERNAL_FORMAT
GL_ES_VERSION_3_1, GL_VERSION_1_1, GL_QCOM_extended_get Alias for: GL_TEXTURE_INTERNAL_FORMAT_QCOM D...
Definition: GL2ES3.java:423
void glGetTexImage(int target, int level, int format, int type, Buffer pixels)
Entry point to C language function: void {@native glGetTexImage}(GLenum target, GLint level,...
static final int GL_TEXTURE_COMPRESSED_IMAGE_SIZE
GL_VERSION_1_3, GL_ARB_texture_compression Alias for: GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB Define "G...
Definition: GL2GL3.java:609
void glGetCompressedTexImage(int target, int level, Buffer img)
Entry point to C language function: void {@native glGetCompressedTexImage}(GLenum target,...
static final int GL_ABGR_EXT
GL_EXT_abgr Define "GL_ABGR_EXT" with expression '0x8000', CType: int
Definition: GL2.java:941
static final int GL_TEXTURE_BORDER
GL_VERSION_1_0 Define "GL_TEXTURE_BORDER" with expression '0x1005', CType: int
Definition: GL2.java:694
boolean isGL2GL3()
Indicates whether this GL object conforms to a GL2GL3 compatible profile.
GLProfile getGLProfile()
Returns the GLProfile associated with this GL object.
GL2GL3 getGL2GL3()
Casts this object to the GL2GL3 interface.
static final int GL_RGB8
GL_ES_VERSION_3_0, GL_VERSION_1_1, GL_EXT_texture, GL_OES_rgb8_rgba8, GL_OES_required_internalformat ...
Definition: GL.java:479
static final int GL_BGRA
GL_VERSION_1_2, GL_IMG_read_format, GL_APPLE_texture_format_BGRA8888, GL_EXT_texture_format_BGRA8888,...
Definition: GL.java:404
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_TEXTURE_2D
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_TEXTURE_2D" with expr...
Definition: GL.java:491
static final int GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
GL_EXT_texture_compression_s3tc Define "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT" with expression '0x83F3',...
Definition: GL.java:100
static final int GL_RGBA8
GL_ES_VERSION_3_0, GL_VERSION_1_1, GL_OES_rgb8_rgba8, GL_OES_required_internalformat,...
Definition: GL.java:279
static final int GL_BGR
GL_VERSION_1_2, GL_EXT_bgra Alias for: GL_BGR_EXT Define "GL_BGR" with expression '0x80E0',...
Definition: GL.java:399
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
static final int GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
GL_EXT_texture_compression_s3tc Define "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT" with expression '0x83F2',...
Definition: GL.java:47
static final int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
GL_EXT_texture_compression_s3tc, GL_EXT_texture_compression_dxt1 Define "GL_COMPRESSED_RGBA_S3TC_DXT1...
Definition: GL.java:769
static final int GL_UNSIGNED_BYTE
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_UNSIGNED_BYTE" with e...
Definition: GL.java:284
static final int GL_COMPRESSED_RGB_S3TC_DXT1_EXT
GL_EXT_texture_compression_s3tc, GL_EXT_texture_compression_dxt1 Define "GL_COMPRESSED_RGB_S3TC_DXT1_...
Definition: GL.java:647
Plug-in interface to TextureIO to support reading OpenGL textures from new file formats.
TextureData newTextureData(GLProfile glp, InputStream stream, int internalFormat, int pixelFormat, boolean mipmap, String fileSuffix)
Produces a TextureData object from a stream, or returns null if the file format was not supported by ...
ImageType[] getImageTypes()
Returns the known supported ImageTypes, or null if unknown.
Plug-in interface to TextureIO to support writing OpenGL textures to new file formats.
boolean write(File file, TextureData data)
Writes the given TextureData to the passed file.