JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
AWTTextureData.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (c) 2010 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
38package com.jogamp.opengl.util.texture.awt;
39
40import java.awt.AlphaComposite;
41import java.awt.Graphics2D;
42import java.awt.Transparency;
43import java.awt.image.BufferedImage;
44import java.awt.image.ComponentColorModel;
45import java.awt.image.ComponentSampleModel;
46import java.awt.image.DataBuffer;
47import java.awt.image.DataBufferByte;
48import java.awt.image.DataBufferDouble;
49import java.awt.image.DataBufferFloat;
50import java.awt.image.DataBufferInt;
51import java.awt.image.DataBufferShort;
52import java.awt.image.DataBufferUShort;
53import java.awt.image.MultiPixelPackedSampleModel;
54import java.awt.image.SampleModel;
55import java.awt.image.SinglePixelPackedSampleModel;
56import java.awt.image.WritableRaster;
57import java.nio.Buffer;
58import java.nio.ByteBuffer;
59import java.nio.FloatBuffer;
60import java.nio.IntBuffer;
61import java.nio.ShortBuffer;
62
63import com.jogamp.opengl.GL;
64import com.jogamp.opengl.GL2;
65import com.jogamp.opengl.GL2GL3;
66import com.jogamp.opengl.GLException;
67import com.jogamp.opengl.GLProfile;
68
69import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes;
70import com.jogamp.opengl.util.texture.TextureData;
71
72public class AWTTextureData extends TextureData {
73 // Mechanism for lazily converting input BufferedImages with custom
74 // ColorModels to standard ones for uploading to OpenGL, as well as
75 // backing off from the optimizations of hoping that either
76 // GL_EXT_abgr or OpenGL 1.2 are present
77 private BufferedImage imageForLazyCustomConversion;
78 private boolean expectingEXTABGR;
79 private boolean expectingGL12;
80
81 private static final java.awt.image.ColorModel rgbaColorModel =
82 new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
83 new int[] {8, 8, 8, 8}, true, true,
84 Transparency.TRANSLUCENT,
85 DataBuffer.TYPE_BYTE);
86 private static final java.awt.image.ColorModel rgbColorModel =
87 new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
88 new int[] {8, 8, 8, 0}, false, false,
89 Transparency.OPAQUE,
90 DataBuffer.TYPE_BYTE);
91
92
93 /**
94 * Constructs a new TextureData object with the specified parameters
95 * and data contained in the given BufferedImage. The resulting
96 * TextureData "wraps" the contents of the BufferedImage, so if a
97 * modification is made to the BufferedImage between the time the
98 * TextureData is constructed and when a Texture is made from the
99 * TextureData, that modification will be visible in the resulting
100 * Texture.
101 *
102 * @param glp the OpenGL Profile this texture data should be
103 * created for.
104 * @param internalFormat the OpenGL internal format for the
105 * resulting texture; may be 0, in which case
106 * it is inferred from the image's type
107 * @param pixelFormat the OpenGL internal format for the
108 * resulting texture; may be 0, in which case
109 * it is inferred from the image's type (note:
110 * this argument is currently always ignored)
111 * @param mipmap indicates whether mipmaps should be
112 * autogenerated (using GLU) for the resulting
113 * texture
114 * @param image the image containing the texture data
115 */
116 public AWTTextureData(final GLProfile glp,
117 final int internalFormat,
118 final int pixelFormat,
119 final boolean mipmap,
120 final BufferedImage image) {
121 super(glp);
122 if (internalFormat == 0) {
123 this.internalFormat = image.getColorModel().hasAlpha() ? GL.GL_RGBA : GL.GL_RGB;
124 } else {
125 this.internalFormat = internalFormat;
126 }
127 createFromImage(glp, image);
128 this.mipmap = mipmap;
129 if (buffer != null) {
131 } else {
132 // In the lazy custom conversion case we don't yet have a buffer
133 if (imageForLazyCustomConversion != null) {
134 estimatedMemorySize = estimatedMemorySize(wrapImageDataBuffer(imageForLazyCustomConversion));
135 }
136 }
137 }
138
139 private void validatePixelAttributes() {
140 if (imageForLazyCustomConversion != null) {
141 if (!((expectingEXTABGR && haveEXTABGR) ||
142 (expectingGL12 && haveGL12))) {
143 revertPixelAttributes();
144 }
145 }
146 }
147
148 @Override
150 validatePixelAttributes();
151 return super.getPixelAttributes();
152 }
153
154 @Override
155 public int getPixelFormat() {
156 validatePixelAttributes();
157 return super.getPixelFormat();
158 }
159 @Override
160 public int getPixelType() {
161 validatePixelAttributes();
162 return super.getPixelType();
163 }
164
165 @Override
166 public Buffer getBuffer() {
167 if (imageForLazyCustomConversion != null) {
168 if (!((expectingEXTABGR && haveEXTABGR) ||
169 (expectingGL12 && haveGL12))) {
170 revertPixelAttributes();
171 // Must present the illusion to the end user that we are simply
172 // wrapping the input BufferedImage
173 createFromCustom(imageForLazyCustomConversion);
174 }
175 }
176 return buffer;
177 }
178
179 private void createFromImage(final GLProfile glp, final BufferedImage image) {
180 pixelAttributes = GLPixelAttributes.UNDEF; // Determine from image
181 mustFlipVertically = true;
182
183 width = image.getWidth();
184 height = image.getHeight();
185
186 int scanlineStride;
187
188 final SampleModel sm = image.getRaster().getSampleModel();
189 if (sm instanceof SinglePixelPackedSampleModel) {
190 scanlineStride =
191 ((SinglePixelPackedSampleModel)sm).getScanlineStride();
192 } else if (sm instanceof MultiPixelPackedSampleModel) {
193 scanlineStride =
194 ((MultiPixelPackedSampleModel)sm).getScanlineStride();
195 } else if (sm instanceof ComponentSampleModel) {
196 scanlineStride =
197 ((ComponentSampleModel)sm).getScanlineStride();
198 } else {
199 // This will only happen for TYPE_CUSTOM anyway
200 setupLazyCustomConversion(image);
201 return;
202 }
203
204 width = image.getWidth();
205 height = image.getHeight();
206
207 if (glp.isGL2GL3()) {
208 switch (image.getType()) {
209 case BufferedImage.TYPE_INT_RGB:
210 pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV);
211 rowLength = scanlineStride;
212 alignment = 4;
213 expectingGL12 = true;
214 setupLazyCustomConversion(image);
215 break;
216 case BufferedImage.TYPE_INT_ARGB_PRE:
217 pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV);
218 rowLength = scanlineStride;
219 alignment = 4;
220 expectingGL12 = true;
221 setupLazyCustomConversion(image);
222 break;
223 case BufferedImage.TYPE_INT_BGR:
224 pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV);
225 rowLength = scanlineStride;
226 alignment = 4;
227 expectingGL12 = true;
228 setupLazyCustomConversion(image);
229 break;
230 case BufferedImage.TYPE_3BYTE_BGR:
231 {
232 // we can pass the image data directly to OpenGL only if
233 // we have an integral number of pixels in each scanline
234 if ((scanlineStride % 3) == 0) {
235 pixelAttributes = new GLPixelAttributes(GL.GL_BGR, GL.GL_UNSIGNED_BYTE);
236 rowLength = scanlineStride / 3;
237 alignment = 1;
238 } else {
239 setupLazyCustomConversion(image);
240 return;
241 }
242 }
243 break;
244 case BufferedImage.TYPE_4BYTE_ABGR_PRE:
245 {
246 // we can pass the image data directly to OpenGL only if
247 // we have an integral number of pixels in each scanline
248 // and only if the GL_EXT_abgr extension is present
249
250 // NOTE: disabling this code path for now as it appears it's
251 // buggy at least on some NVidia drivers and doesn't perform
252 // the necessary byte swapping (FIXME: needs more
253 // investigation)
254 if ((scanlineStride % 4) == 0 && glp.isGL2() && false) {
255 pixelAttributes = new GLPixelAttributes(GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE);
256 rowLength = scanlineStride / 4;
257 alignment = 4;
258
259 // Store a reference to the original image for later in
260 // case it turns out that we don't have GL_EXT_abgr at the
261 // time we're going to do the texture upload to OpenGL
262 setupLazyCustomConversion(image);
263 expectingEXTABGR = true;
264 break;
265 } else {
266 setupLazyCustomConversion(image);
267 return;
268 }
269 }
270 case BufferedImage.TYPE_USHORT_565_RGB:
271 pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
272 rowLength = scanlineStride;
273 alignment = 2;
274 expectingGL12 = true;
275 setupLazyCustomConversion(image);
276 break;
277 case BufferedImage.TYPE_USHORT_555_RGB:
278 pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV);
279 rowLength = scanlineStride;
280 alignment = 2;
281 expectingGL12 = true;
282 setupLazyCustomConversion(image);
283 break;
284 case BufferedImage.TYPE_BYTE_GRAY:
285 pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
286 rowLength = scanlineStride;
287 alignment = 1;
288 break;
289 case BufferedImage.TYPE_USHORT_GRAY:
290 pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_SHORT);
291 rowLength = scanlineStride;
292 alignment = 2;
293 break;
294 // Note: TYPE_INT_ARGB and TYPE_4BYTE_ABGR images go down the
295 // custom code path to satisfy the invariant that images with an
296 // alpha channel always go down with premultiplied alpha.
297 case BufferedImage.TYPE_INT_ARGB:
298 case BufferedImage.TYPE_4BYTE_ABGR:
299 case BufferedImage.TYPE_BYTE_BINARY:
300 case BufferedImage.TYPE_BYTE_INDEXED:
301 case BufferedImage.TYPE_CUSTOM:
302 default:
303 final java.awt.image.ColorModel cm = image.getColorModel();
304 if (cm.equals(rgbColorModel)) {
305 pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
306 rowLength = scanlineStride / 3;
307 alignment = 1;
308 } else if (cm.equals(rgbaColorModel)) {
309 pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
310 rowLength = scanlineStride / 4; // FIXME: correct?
311 alignment = 4;
312 } else {
313 setupLazyCustomConversion(image);
314 return;
315 }
316 break;
317 }
318 } else {
319 switch (image.getType()) {
320 case BufferedImage.TYPE_INT_RGB:
321 pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
322 rowLength = scanlineStride;
323 alignment = 3;
324 expectingGL12 = true;
325 setupLazyCustomConversion(image);
326 break;
327 case BufferedImage.TYPE_INT_ARGB_PRE:
328 throw new GLException("INT_ARGB_PRE n.a.");
329 case BufferedImage.TYPE_INT_BGR:
330 throw new GLException("INT_BGR n.a.");
331 case BufferedImage.TYPE_3BYTE_BGR:
332 throw new GLException("INT_BGR n.a.");
333 case BufferedImage.TYPE_4BYTE_ABGR_PRE:
334 throw new GLException("INT_BGR n.a.");
335 case BufferedImage.TYPE_USHORT_565_RGB:
336 pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
337 rowLength = scanlineStride;
338 alignment = 2;
339 expectingGL12 = true;
340 setupLazyCustomConversion(image);
341 break;
342 case BufferedImage.TYPE_USHORT_555_RGB:
343 pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
344 rowLength = scanlineStride;
345 alignment = 2;
346 expectingGL12 = true;
347 setupLazyCustomConversion(image);
348 break;
349 case BufferedImage.TYPE_BYTE_GRAY:
350 pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
351 rowLength = scanlineStride;
352 alignment = 1;
353 break;
354 case BufferedImage.TYPE_USHORT_GRAY:
355 throw new GLException("USHORT_GRAY n.a.");
356 // Note: TYPE_INT_ARGB and TYPE_4BYTE_ABGR images go down the
357 // custom code path to satisfy the invariant that images with an
358 // alpha channel always go down with premultiplied alpha.
359 case BufferedImage.TYPE_INT_ARGB:
360 case BufferedImage.TYPE_4BYTE_ABGR:
361 case BufferedImage.TYPE_BYTE_BINARY:
362 case BufferedImage.TYPE_BYTE_INDEXED:
363 case BufferedImage.TYPE_CUSTOM:
364 default:
365 final java.awt.image.ColorModel cm = image.getColorModel();
366 if (cm.equals(rgbColorModel)) {
367 pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
368 rowLength = scanlineStride / 3;
369 alignment = 1;
370 } else if (cm.equals(rgbaColorModel)) {
371 pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
372 rowLength = scanlineStride / 4; // FIXME: correct?
373 alignment = 4;
374 } else {
375 setupLazyCustomConversion(image);
376 return;
377 }
378 break;
379 }
380 }
381
382 createNIOBufferFromImage(image);
383 }
384
385 private void setupLazyCustomConversion(final BufferedImage image) {
386 imageForLazyCustomConversion = image;
387 final boolean hasAlpha = image.getColorModel().hasAlpha();
388 int pixelFormat = pixelAttributes.format;
389 int pixelType = pixelAttributes.type;
390 if (pixelFormat == 0) {
391 pixelFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB;
392 }
393 alignment = 1; // FIXME: do we need better?
394 rowLength = width; // FIXME: correct in all cases?
395
396 // Allow previously-selected pixelType (if any) to override that
397 // we can infer from the DataBuffer
398 final DataBuffer data = image.getRaster().getDataBuffer();
399 if (data instanceof DataBufferByte || isPackedInt(image)) {
400 // Don't use GL_UNSIGNED_INT for BufferedImage packed int images
401 if (pixelType == 0) pixelType = GL.GL_UNSIGNED_BYTE;
402 } else if (data instanceof DataBufferDouble) {
403 throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL");
404 } else if (data instanceof DataBufferFloat) {
405 if (pixelType == 0) pixelType = GL.GL_FLOAT;
406 } else if (data instanceof DataBufferInt) {
407 // FIXME: should we support signed ints?
408 if (pixelType == 0) pixelType = GL.GL_UNSIGNED_INT;
409 } else if (data instanceof DataBufferShort) {
410 if (pixelType == 0) pixelType = GL.GL_SHORT;
411 } else if (data instanceof DataBufferUShort) {
412 if (pixelType == 0) pixelType = GL.GL_UNSIGNED_SHORT;
413 } else {
414 throw new RuntimeException("Unexpected DataBuffer type?");
415 }
416 pixelAttributes = new GLPixelAttributes(pixelFormat, pixelType);
417 }
418
419 private void createFromCustom(final BufferedImage image) {
420 final int width = image.getWidth();
421 final int height = image.getHeight();
422
423 // create a temporary image that is compatible with OpenGL
424 final boolean hasAlpha = image.getColorModel().hasAlpha();
425 java.awt.image.ColorModel cm = null;
426 int dataBufferType = image.getRaster().getDataBuffer().getDataType();
427 // Don't use integer components for packed int images
428 if (isPackedInt(image)) {
429 dataBufferType = DataBuffer.TYPE_BYTE;
430 }
431 if (dataBufferType == DataBuffer.TYPE_BYTE) {
432 cm = hasAlpha ? rgbaColorModel : rgbColorModel;
433 } else {
434 if (hasAlpha) {
435 cm = new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
436 null, true, true,
437 Transparency.TRANSLUCENT,
438 dataBufferType);
439 } else {
440 cm = new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB),
441 null, false, false,
442 Transparency.OPAQUE,
443 dataBufferType);
444 }
445 }
446
447 final boolean premult = cm.isAlphaPremultiplied();
448 final WritableRaster raster =
449 cm.createCompatibleWritableRaster(width, height);
450 final BufferedImage texImage = new BufferedImage(cm, raster, premult, null);
451
452 // copy the source image into the temporary image
453 final Graphics2D g = texImage.createGraphics();
454 g.setComposite(AlphaComposite.Src);
455 g.drawImage(image, 0, 0, null);
456 g.dispose();
457
458 // Wrap the buffer from the temporary image
459 createNIOBufferFromImage(texImage);
460 }
461
462 private boolean isPackedInt(final BufferedImage image) {
463 final int imgType = image.getType();
464 return (imgType == BufferedImage.TYPE_INT_RGB ||
465 imgType == BufferedImage.TYPE_INT_BGR ||
466 imgType == BufferedImage.TYPE_INT_ARGB ||
467 imgType == BufferedImage.TYPE_INT_ARGB_PRE);
468 }
469
470 private void revertPixelAttributes() {
471 // Knowing we don't have e.g. OpenGL 1.2 functionality available,
472 // and knowing we're in the process of doing the fallback code
473 // path, re-infer a vanilla pixel format and type compatible with
474 // OpenGL 1.1
475 pixelAttributes = GLPixelAttributes.UNDEF;
476 setupLazyCustomConversion(imageForLazyCustomConversion);
477 }
478
479 private void createNIOBufferFromImage(final BufferedImage image) {
480 buffer = wrapImageDataBuffer(image);
481 }
482
483 private Buffer wrapImageDataBuffer(final BufferedImage image) {
484 //
485 // Note: Grabbing the DataBuffer will defeat Java2D's image
486 // management mechanism (as of JDK 5/6, at least). This shouldn't
487 // be a problem for most JOGL apps, but those that try to upload
488 // the image into an OpenGL texture and then use the same image in
489 // Java2D rendering might find the 2D rendering is not as fast as
490 // it could be.
491 //
492
493 final DataBuffer data = image.getRaster().getDataBuffer();
494 if (data instanceof DataBufferByte) {
495 return ByteBuffer.wrap(((DataBufferByte) data).getData());
496 } else if (data instanceof DataBufferDouble) {
497 throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL");
498 } else if (data instanceof DataBufferFloat) {
499 return FloatBuffer.wrap(((DataBufferFloat) data).getData());
500 } else if (data instanceof DataBufferInt) {
501 return IntBuffer.wrap(((DataBufferInt) data).getData());
502 } else if (data instanceof DataBufferShort) {
503 return ShortBuffer.wrap(((DataBufferShort) data).getData());
504 } else if (data instanceof DataBufferUShort) {
505 return ShortBuffer.wrap(((DataBufferUShort) data).getData());
506 } else {
507 throw new RuntimeException("Unexpected DataBuffer type?");
508 }
509 }
510}
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
final boolean isGL2GL3()
Indicates whether this profile is capable of GL2GL3.
final boolean isGL2()
Indicates whether this profile is capable of GL2 .
final int format
The OpenGL pixel data format.
static final GLPixelAttributes UNDEF
Undefined instance of GLPixelAttributes, having componentCount:=0, format:=0 and type:= 0.
Represents the data for an OpenGL texture.
AWTTextureData(final GLProfile glp, final int internalFormat, final int pixelFormat, final boolean mipmap, final BufferedImage image)
Constructs a new TextureData object with the specified parameters and data contained in the given Buf...
int getPixelType()
Returns the intended OpenGL pixel type of the texture data using getPixelAttributes().
int getPixelFormat()
Returns the intended OpenGL pixel format of the texture data using getPixelAttributes().
GLPixelAttributes getPixelAttributes()
Returns the intended OpenGL GLPixelAttributes of the texture data, i.e.
Buffer getBuffer()
Returns the texture data, or null if it is specified as a set of mipmaps.
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