JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
AWTGLPixelBuffer.java
Go to the documentation of this file.
1/**
2 * Copyright 2013 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.opengl.util.awt;
29
30import java.awt.image.BufferedImage;
31import java.awt.image.ColorModel;
32import java.awt.image.DataBuffer;
33import java.awt.image.DataBufferInt;
34import java.awt.image.Raster;
35import java.awt.image.SinglePixelPackedSampleModel;
36import java.awt.image.WritableRaster;
37import java.nio.Buffer;
38import java.nio.IntBuffer;
39import java.util.Iterator;
40
41import com.jogamp.nativewindow.util.PixelFormat;
42import com.jogamp.opengl.GL;
43import com.jogamp.opengl.GLProfile;
44
45import com.jogamp.common.nio.Buffers;
46import com.jogamp.common.util.IntObjectHashMap;
47import com.jogamp.opengl.util.GLPixelBuffer;
48
49/**
50 * AWT {@link GLPixelBuffer} backed by an {@link BufferedImage} of type
51 * {@link BufferedImage#TYPE_INT_ARGB} or {@link BufferedImage#TYPE_INT_RGB}.
52 * <p>
53 * Implementation uses an array backed {@link IntBuffer}.
54 * </p>
55 * <p>
56 * {@link AWTGLPixelBuffer} can be produced via {@link AWTGLPixelBufferProvider}'s
57 * {@link AWTGLPixelBufferProvider#allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocate(..)}.
58 * </p>
59 * <p>
60 * See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)} for {@link #allowRowStride} details.
61 * </p>
62 * <p>
63 * If using <code>allowRowStride == true</code>, user may needs to get the {@link #getAlignedImage(int, int) aligned image}
64 * since {@link #requiresNewBuffer(GL, int, int, int)} will allow different width in this case.
65 * </p>
66 */
67public class AWTGLPixelBuffer extends GLPixelBuffer {
68 /**
69 * Ignoring componentCount, since otherwise no AWT/GL matching types are found.
70 * <p>
71 * Due to using RGBA and BGRA, pack/unpack usage has makes no difference.
72 * </p>
73 */
74 private static final GLPixelAttributes awtPixelAttributesIntBGRA = new GLPixelAttributes(GL.GL_BGRA, GL.GL_UNSIGNED_BYTE);
75 private static final GLPixelAttributes awtPixelAttributesIntRGBA = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
76
77 /** The underlying {@link BufferedImage}. */
78 public final BufferedImage image;
79
80 private final PixelFormat.Composition hostPixelComp;
81 private final int awtFormat;
82
83 /**
84 * @param hostPixelComp the host {@link PixelFormat.Composition}
85 * @param pixelAttributes the desired {@link GLPixelAttributes}
86 * @param pack {@code true} for read mode GPU -> CPU, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, Buffer) glReadPixels}.
87 * {@code false} for write mode CPU -> GPU, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, Buffer) glTexImage2D}.
88 * @param awtFormat the used AWT format, i.e. {@link AWTGLPixelBufferProvider#getAWTFormat(GLProfile, int)}
89 * @param width in pixels
90 * @param height in pixels
91 * @param depth in pixels
92 * @param image the AWT image
93 * @param buffer the backing array
94 * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link #requiresNewBuffer(GL, int, int, int)}.
95 * If <code>true</code>, user shall decide whether to use a {@link #getAlignedImage(int, int) width-aligned image}.
96 */
97 public AWTGLPixelBuffer(final PixelFormat.Composition hostPixelComp,
99 final boolean pack,
100 final int awtFormat, final int width, final int height, final int depth,
101 final BufferedImage image, final Buffer buffer, final boolean allowRowStride) {
103 this.image = image;
104 this.hostPixelComp = hostPixelComp;
105 this.awtFormat = awtFormat;
106 }
107
108 public final PixelFormat.Composition getHostPixelComp() { return hostPixelComp; }
109 public final int getAWTFormat() { return awtFormat; }
110
111 @Override
112 public void dispose() {
113 image.flush();
114 super.dispose();
115 }
116
117 /**
118 * Returns a width- and height-aligned image representation sharing data w/ {@link #image}.
119 * @param width
120 * @param height
121 * @return
122 * @throws IllegalArgumentException if requested size exceeds image size
123 */
124 public BufferedImage getAlignedImage(final int width, final int height) throws IllegalArgumentException {
125 if( width * height > image.getWidth() * image.getHeight() ) {
126 throw new IllegalArgumentException("Requested size exceeds image size: "+width+"x"+height+" > "+image.getWidth()+"x"+image.getHeight());
127 }
128 if( width == image.getWidth() && height == image.getHeight() ) {
129 return image;
130 } else {
131 final ColorModel cm = image.getColorModel();
132 final WritableRaster raster0 = image.getRaster();
133 final DataBuffer dataBuffer = raster0.getDataBuffer();
134 final SinglePixelPackedSampleModel sppsm0 = (SinglePixelPackedSampleModel) raster0.getSampleModel();
135 final SinglePixelPackedSampleModel sppsm1 = new SinglePixelPackedSampleModel(dataBuffer.getDataType(),
136 width, height, width /* scanLineStride */, sppsm0.getBitMasks());
137 final WritableRaster raster1 = Raster.createWritableRaster(sppsm1, dataBuffer, null);
138 return new BufferedImage (cm, raster1, cm.isAlphaPremultiplied(), null);
139 }
140 }
141
142 public final boolean isDataBufferSource(final BufferedImage imageU) {
143 final DataBuffer dataBuffer0 = image.getRaster().getDataBuffer();
144 final DataBuffer dataBufferU = imageU.getRaster().getDataBuffer();
145 return dataBufferU == dataBuffer0;
146 }
147
148 @Override
149 public StringBuilder toString(StringBuilder sb) {
150 sb = super.toString(sb);
151 sb.append(", allowRowStride ").append(allowRowStride).append(", image [").append(image.getWidth()).append("x").append(image.getHeight()).append(", ").append(image.toString()).append("]");
152 return sb;
153 }
154 @Override
155 public String toString() {
156 return "AWTGLPixelBuffer["+toString(null).toString()+"]";
157 }
158
159 /**
160 * Provider for {@link AWTGLPixelBuffer} instances.
161 */
162 public static class AWTGLPixelBufferProvider implements GLPixelBufferProvider {
163 private final boolean allowRowStride;
164
165 /**
166 * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not.
167 * See {@link #getAllowRowStride()} and {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}.
168 * If <code>true</code>, user shall decide whether to use a {@link AWTGLPixelBuffer#getAlignedImage(int, int) width-aligned image}.
169 */
170 public AWTGLPixelBufferProvider(final boolean allowRowStride) {
171 this.allowRowStride = allowRowStride;
172 }
173
174 @Override
175 public boolean getAllowRowStride() { return allowRowStride; }
176
177 @Override
178 public GLPixelAttributes getAttributes(final GL gl, final int componentCount, final boolean pack) {
179 return gl.isGLES() ? awtPixelAttributesIntRGBA : awtPixelAttributesIntBGRA;
180 }
181
182 public GLPixelAttributes getAttributes(final GLProfile glp, final int componentCount) {
183 return glp.isGLES() ? awtPixelAttributesIntRGBA : awtPixelAttributesIntBGRA;
184 }
185
186 /**
187 * {@inheritDoc}
188 * <p>
189 * Returns a valid {@link PixelFormat.Composition} instance from {@link #getAWTPixelFormat(GLProfile, int)}.
190 * </p>
191 */
192 @Override
193 public PixelFormat.Composition getHostPixelComp(final GLProfile glp, final int componentCount) {
194 return getAWTPixelFormat(glp, componentCount).comp;
195 }
196
197 /**
198 * Returns one of
199 * <ul>
200 * <li>GL__, 4c -> 4c: {@link BufferedImage#TYPE_INT_ARGB} <-> {@link GL#GL_BGRA}</li>
201 * <li>GLES, 4c -> 4c: {@link BufferedImage#TYPE_INT_BGR} <-> {@link GL#GL_RGBA}</li>
202 * <li>GL__, 3c -> 4c: {@link BufferedImage#TYPE_INT_RGB} <-> {@link GL#GL_BGRA}</li>
203 * <li>GLES, 3c -> 4c: {@link BufferedImage#TYPE_INT_BGR} <-> {@link GL#GL_RGBA}</li>
204 * </ul>
205 * @param glp
206 * @param componentCount
207 * @return
208 */
209 public int getAWTFormat(final GLProfile glp, final int componentCount) {
210 if( 4 == componentCount ) {
211 // FIXME: 4 component solution BufferedImage.TYPE_INT_ARGB: GLES format missing (i.e. GL_BGRA)
212 return glp.isGLES() ? BufferedImage.TYPE_INT_BGR : BufferedImage.TYPE_INT_ARGB;
213 } else {
214 return glp.isGLES() ? BufferedImage.TYPE_INT_BGR : BufferedImage.TYPE_INT_RGB;
215 }
216 }
217
218 public PixelFormat getAWTPixelFormat(final GLProfile glp, final int componentCount) {
219 if( 4 == componentCount ) {
220 return glp.isGLES() ? PixelFormat.RGBx8888 : PixelFormat.BGRA8888;
221 } else {
222 return glp.isGLES() ? PixelFormat.RGBx8888 : PixelFormat.BGRx8888;
223 }
224 }
225
226 /**
227 * {@inheritDoc}
228 * <p>
229 * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>.
230 * </p>
231 */
232 @Override
233 public AWTGLPixelBuffer allocate(final GL gl, final PixelFormat.Composition hostPixComp, final GLPixelAttributes pixelAttributes, final boolean pack,
234 final int width, final int height, final int depth, final int minByteSize) {
235 if( null == hostPixComp ) {
236 throw new IllegalArgumentException("Null hostPixComp");
237 }
238 final int awtFormat = getAWTFormat(gl.getGLProfile(), hostPixComp.componentCount());
239 final BufferedImage image = new BufferedImage(width, height, awtFormat);
240 final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
241 final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer );
242 return new AWTGLPixelBuffer(hostPixComp, pixelAttributes, pack,
243 awtFormat, width, height, depth, image, ibuffer, allowRowStride);
244 }
245 }
246
247 /**
248 * Provider for singleton {@link AWTGLPixelBuffer} instances.
249 * <p>
250 * Provider instance holds the last {@link AWTGLPixelBuffer} instance
251 * {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated}.
252 * A new {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocation}
253 * will return same instance, if a new buffer is not {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int) required}.
254 * The latter is true if size are compatible, hence <code>allowRowStride</code> should be enabled, if possible.
255 * </p>
256 */
258 private final IntObjectHashMap bufferMap = new IntObjectHashMap(8);
259
260 private static int getHashCode(final PixelFormat.Composition hostPixelComp, final GLPixelAttributes pixelAttributes, final boolean pack) {
261 // 31 * x == (x << 5) - x
262 int hash = hostPixelComp.hashCode();
263 hash = ((hash << 5) - hash) + pixelAttributes.hashCode();
264 // hash = ((hash << 5) - hash) + (pack ? 100 : 0); // no difference due to RGBA/BGRA only modes.
265 return hash;
266 }
267
268 /**
269 * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}.
270 */
271 public SingleAWTGLPixelBufferProvider(final boolean allowRowStride) {
272 super(allowRowStride);
273 }
274
275 /**
276 * {@inheritDoc}
277 * <p>
278 * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>.
279 * </p>
280 */
281 @Override
283 final boolean pack, final int width, final int height, final int depth, final int minByteSize) {
284 if( null == hostPixComp ) {
285 hostPixComp = pixelAttributes.pfmt.comp;
286 }
287 final int bufferKey = getHashCode(hostPixComp, pixelAttributes, pack);
288 AWTGLPixelBuffer r = (AWTGLPixelBuffer) bufferMap.get(bufferKey);
289 if( null == r || r.requiresNewBuffer(gl, width, height, minByteSize) ) {
290 if( null != r ) {
291 r.dispose();
292 }
293 r = allocateImpl(hostPixComp, pixelAttributes, pack,
294 getAWTFormat(gl.getGLProfile(), hostPixComp.componentCount()), width, height, depth, minByteSize);
295 bufferMap.put(bufferKey, r);
296 }
297 return r;
298 }
299
300 private AWTGLPixelBuffer allocateImpl(final PixelFormat.Composition hostPixComp,
302 final boolean pack,
303 final int awtFormat, final int width, final int height, final int depth,
304 final int minByteSize) {
305 final BufferedImage image = new BufferedImage(width, height, awtFormat);
306 final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
307 final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer );
308 return new AWTGLPixelBuffer(hostPixComp, pixelAttributes, pack,
309 awtFormat, width, height, depth, image, ibuffer, getAllowRowStride());
310 }
311
312 /**
313 * Return the last {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated}
314 * {@link AWTGLPixelBuffer}, if compatible w/ the given {@link PixelFormat.Composition} and {@link GLPixelAttributes}.
315 **/
316 @Override
318 return (AWTGLPixelBuffer) bufferMap.get(getHashCode(hostPixelComp, pixelAttributes, pack));
319 }
320
321 /**
322 * Initializes the single {@link AWTGLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, PixelFormat.Composition, GLPixelAttributes, boolean, int, int, int, int) allocated}.
323 * @return the newly initialized single {@link AWTGLPixelBuffer}, or null if already allocated.
324 */
325 @Override
326 public AWTGLPixelBuffer initSingleton(final GLProfile glp, final int componentCount,
327 final boolean pack, final int width, final int height, final int depth) {
328 final GLPixelAttributes pixelAttributes = getAttributes(glp, componentCount);
329 final PixelFormat awtPixelFormat = getAWTPixelFormat(glp, componentCount);
330 final int awtFormat = getAWTFormat(glp, componentCount);
331 final int bufferKey = getHashCode(awtPixelFormat.comp, pixelAttributes, pack);
332 AWTGLPixelBuffer r = (AWTGLPixelBuffer) bufferMap.get(bufferKey);
333 if( null != r ) {
334 return null;
335 }
336 r = allocateImpl(awtPixelFormat.comp, pixelAttributes, pack, awtFormat, width, height, depth, 0);
337 bufferMap.put(bufferKey, r);
338 return r;
339 }
340
341 @Override
342 public void dispose() {
343 for(final Iterator<IntObjectHashMap.Entry> i=bufferMap.iterator(); i.hasNext(); ) {
344 final AWTGLPixelBuffer b = (AWTGLPixelBuffer)i.next().value;
345 b.dispose();
346 }
347 bufferMap.clear();
348 }
349 }
350}
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
final boolean isGLES()
Indicates whether this profile is capable of GLES.
final PixelFormat pfmt
PixelFormat describing the component layout
OpenGL pixel data buffer, allowing user to provide buffers via their GLPixelBufferProvider implementa...
final int width
Width in pixels, representing buffer's byteSize.
final boolean allowRowStride
Allow GL2ES3#GL_PACK_ROW_LENGTH, or GL2ES2#GL_UNPACK_ROW_LENGTH.
final int depth
Depth in pixels.
final int height
Height in pixels, representing buffer's byteSize.
final GLPixelAttributes pixelAttributes
The GLPixelAttributes.
boolean requiresNewBuffer(final GL gl, final int newWidth, final int newHeight, int newByteSize)
Returns true, if invalid or implementation requires a new buffer based on the new size due to pixel a...
final boolean pack
Data packing direction.
final Buffer buffer
Buffer holding the pixel data.
AWTGLPixelBuffer allocate(final GL gl, final PixelFormat.Composition hostPixComp, final GLPixelAttributes pixelAttributes, final boolean pack, final int width, final int height, final int depth, final int minByteSize)
Allocates a new GLPixelBuffer object.The minimum required remaining byte size equals to minByteSize,...
GLPixelAttributes getAttributes(final GLProfile glp, final int componentCount)
GLPixelAttributes getAttributes(final GL gl, final int componentCount, final boolean pack)
Returns RGB[A] GLPixelAttributes matching GL, componentCount and pack.
int getAWTFormat(final GLProfile glp, final int componentCount)
Returns one of.
PixelFormat.Composition getHostPixelComp(final GLProfile glp, final int componentCount)
Returns the host PixelFormat.Composition matching GL and componentCount if required by implementation...
PixelFormat getAWTPixelFormat(final GLProfile glp, final int componentCount)
boolean getAllowRowStride()
Allow GL2ES3#GL_PACK_ROW_LENGTH, or GL2ES2#GL_UNPACK_ROW_LENGTH.
AWTGLPixelBuffer allocate(final GL gl, PixelFormat.Composition hostPixComp, final GLPixelAttributes pixelAttributes, final boolean pack, final int width, final int height, final int depth, final int minByteSize)
Allocates a new GLPixelBuffer object.The minimum required remaining byte size equals to minByteSize,...
AWTGLPixelBuffer initSingleton(final GLProfile glp, final int componentCount, final boolean pack, final int width, final int height, final int depth)
Initializes the single AWTGLPixelBuffer w/ a given size, if not yet allocated.
AWTGLPixelBuffer getSingleBuffer(final PixelFormat.Composition hostPixelComp, final GLPixelAttributes pixelAttributes, final boolean pack)
Return the last allocated AWTGLPixelBuffer, if compatible w/ the given PixelFormat....
AWT GLPixelBuffer backed by an BufferedImage of type BufferedImage#TYPE_INT_ARGB or BufferedImage#TYP...
final BufferedImage image
The underlying BufferedImage.
BufferedImage getAlignedImage(final int width, final int height)
Returns a width- and height-aligned image representation sharing data w/ image.
StringBuilder toString(StringBuilder sb)
final PixelFormat.Composition getHostPixelComp()
AWTGLPixelBuffer(final PixelFormat.Composition hostPixelComp, final GLPixelAttributes pixelAttributes, final boolean pack, final int awtFormat, final int width, final int height, final int depth, final BufferedImage image, final Buffer buffer, final boolean allowRowStride)
final boolean isDataBufferSource(final BufferedImage imageU)
BGRA8888
Stride is 32 bits, 32 bits per pixel, 4 uniform components of 8 bits.
final Composition comp
Unique Pixel Composition, i.e.
BGRx8888
Stride is 32 bits, 24 bits per pixel, 3 uniform components of 8 bits.
boolean isGLES()
Indicates whether this GL object conforms to one of the OpenGL ES profiles, see isGLES1(),...
GLProfile getGLProfile()
Returns the GLProfile associated with this GL object.
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_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_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
Allows user to interface with another toolkit to define GLPixelAttributes and memory buffer to produc...