JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestNewtCanvasJFXGLn.java
Go to the documentation of this file.
1/**
2 * Copyright 2019 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 */
28
29package com.jogamp.opengl.test.junit.jogl.javafx;
30
31import com.jogamp.opengl.GLAutoDrawable;
32import com.jogamp.opengl.GLCapabilities;
33import com.jogamp.opengl.GLCapabilitiesImmutable;
34import com.jogamp.opengl.GLEventListener;
35import com.jogamp.opengl.GLProfile;
36
37import org.junit.Assert;
38import org.junit.Assume;
39import org.junit.Before;
40import org.junit.BeforeClass;
41import org.junit.After;
42import org.junit.AfterClass;
43import org.junit.Test;
44import org.junit.FixMethodOrder;
45import org.junit.runners.MethodSorters;
46
47import com.jogamp.common.util.RunnableTask;
48import com.jogamp.nativewindow.javafx.JFXAccessor;
49import com.jogamp.newt.NewtFactory;
50import com.jogamp.newt.Screen;
51import com.jogamp.newt.event.WindowAdapter;
52import com.jogamp.newt.event.WindowEvent;
53import com.jogamp.newt.javafx.NewtCanvasJFX;
54import com.jogamp.newt.opengl.GLWindow;
55import com.jogamp.newt.opengl.util.NEWTDemoListener;
56import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
57import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2;
58import com.jogamp.opengl.test.junit.newt.parenting.NewtJFXReparentingKeyAdapter;
59import com.jogamp.opengl.test.junit.newt.parenting.NewtReparentingKeyAdapter;
60import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
61import com.jogamp.opengl.test.junit.util.MiscUtils;
62import com.jogamp.opengl.test.junit.util.NewtTestUtil;
63import com.jogamp.opengl.test.junit.util.UITestCase;
64import com.jogamp.opengl.util.Animator;
65import com.jogamp.opengl.util.GLReadBufferUtil;
66import com.jogamp.opengl.util.texture.TextureIO;
67
68import javafx.application.Application;
69import javafx.application.Platform;
70import javafx.beans.value.ChangeListener;
71import javafx.beans.value.ObservableValue;
72import javafx.scene.Group;
73import javafx.scene.Scene;
74import javafx.scene.canvas.Canvas;
75import javafx.scene.canvas.GraphicsContext;
76import javafx.scene.paint.Color;
77import javafx.scene.text.Font;
78import javafx.scene.text.Text;
79import javafx.stage.Stage;
80
81/**
82 * {@link NewtCanvasJFX} basic functional integration test
83 * of its native parented NEWT child {@link GLWindow} attached to JavaFX's {@link Canvas}.
84 * <p>
85 * {@link NewtCanvasJFX} allows utilizing custom {@link GLCapabilities} settings independent from the JavaFX's window
86 * as well as independent rendering from JavaFX's thread.
87 * </p>
88 * <p>
89 * This unit tests also tests {@link NewtCanvasJFX} native parenting operations before and after
90 * it's belonging Group's Scene has been attached to the JavaFX {@link javafx.stage.Window Window}'s actual native window,
91 * i.e. becoming fully realized and visible.
92 * </p>
93 * <p>
94 * Note that {@link JFXAccessor#runOnJFXThread(boolean, Runnable)} is still used to for certain
95 * mandatory JavaFX lifecycle operation on the JavaFX thread.
96 * </p>
97 * <p>
98 * The demo code uses {@link NewtReparentingKeyAdapter} including {@link NEWTDemoListener} functionality.
99 * </p>
100 * <p>
101 * Manual invocation via main allows running a single test, e.g. {@code -test 21}, and setting each tests's duration in milliseconds, e.g.{@code -time 10000}.
102 * </p>
103 */
104@FixMethodOrder(MethodSorters.NAME_ASCENDING)
105public class TestNewtCanvasJFXGLn extends UITestCase {
106
107 static int duration = 5000; // 250;
108 static int manualTestID = -1;
109
110 com.jogamp.newt.Display jfxNewtDisplay = null;
111
112 public static class JFXApp extends Application {
113 static Stage stage;
114
115 final static Object sync = new Object();
116 static volatile boolean isLaunched = false;
117
118 public JFXApp() {
119 }
120
121 @Override public void init() throws Exception {
122 // pre JFX thread
123 System.err.println("JFX init ...: "+Thread.currentThread());
124 }
125
126 @Override public void start(final Stage stage) {
127 System.err.println("JFX start.0 ...: "+Thread.currentThread());
128 synchronized(sync) {
129 try {
130 // on JFX thread
131 final Scene scene = new Scene(new Group(), defWidth, defHeight);
132 stage.setTitle(TestNewtCanvasJFXGLn.class.getSimpleName());
133 stage.setScene(scene);
134 stage.sizeToScene();
135 {
136 final long h = JFXAccessor.getWindowHandle(stage);
137 System.err.println("t1 - Native window: 0x"+Long.toHexString(h));
138 }
139 stage.show();
140 {
141 final long h = JFXAccessor.getWindowHandle(stage);
142 System.err.println("t2 - Native window: 0x"+Long.toHexString(h));
143 }
144 JFXApp.stage = stage;
145 } finally {
146 isLaunched = true;
147 sync.notifyAll();
148 }
149 }
150 System.err.println("JFX start.X ...: "+Thread.currentThread());
151 }
152 @Override public void stop() throws Exception {
153 System.err.println("JFX stop ...: "+Thread.currentThread());
154 }
155 public static void startup() throws InterruptedException {
156 System.out.println( "GLProfile " + GLProfile.glAvailabilityToString() );
157 System.err.println("JFX Available: "+JFXAccessor.isJFXAvailable());
159 Platform.setImplicitExit(false); // FIXME: Default for all NEWT cases?
160 synchronized(sync) {
161 final Thread ct = Thread.currentThread();
162 RunnableTask.invokeOnNewThread(ct.getThreadGroup(), ct.getName()+"JFXLauncher", false,
163 new Runnable() {
164 public void run() {
165 Application.launch(JFXApp.class);
166 }
167 });
168 while(!isLaunched) {
169 sync.wait();
170 }
171 }
172 System.err.println("JFX launched ...");
173 }
174 }
175 public static void shutdown() {
176 JFXAccessor.runOnJFXThread(true, new Runnable() {
177 public void run() {
178 if( null != stage ) {
179 stage.close();
180 }
181 } });
182 }
183 }
184
185 @BeforeClass
186 public static void startup() throws InterruptedException {
187 JFXApp.startup();
188 }
189
190 @AfterClass
191 public static void shutdown() {
193 Platform.exit();
194 }
195
196 @Before
197 public void init() {
198 jfxNewtDisplay = NewtFactory.createDisplay(null, false); // no-reuse
199 }
200
201 @After
202 public void release() {
203 jfxNewtDisplay = null;
204 }
205
206 class WaitAction implements Runnable {
207 private final long sleepMS;
208
209 WaitAction(final long sleepMS) {
210 this.sleepMS = sleepMS;
211 }
212 public void run() {
213 // blocks on linux .. display.sleep();
214 try {
215 Thread.sleep(sleepMS);
216 } catch (final InterruptedException e) { }
217 }
218 }
219 final WaitAction awtRobotWaitAction = new WaitAction(AWTRobotUtil.TIME_SLICE);
220 final WaitAction generalWaitAction = new WaitAction(10);
221
222 static final int defWidth = 800, defHeight = 600;
223
224 static void populateScene(final Scene scene, final boolean postAttach,
225 final GLWindow glWindow,
226 final int width, final int height, final boolean useBorder,
227 final NewtCanvasJFX[] res) {
228 final javafx.stage.Window w = scene.getWindow();
229 final boolean isShowing = null != w && w.isShowing();
230 final Group g = new Group();
231
232 final int cx, cy, cw, ch, bw, bh;
233 if( useBorder ) {
234 bw = width/5; bh = height/5;
235 cx = bw; cy = bh; cw = width-bw-bw; ch = height-bh-bh;
236 } else {
237 bw = 0; bh = 0;
238 cx = 0; cy = 0; cw = width; ch = height;
239 }
240 System.err.println("Scene "+width+"x"+height+", isShowing "+isShowing+", postAttach "+postAttach);
241 System.err.println("Scene.canvas "+cx+"/"+cy+" "+cw+"x"+ch);
242 System.err.println("Scene.border "+bw+"x"+bh);
243
244 if( !postAttach ) {
245 if(isShowing) {
246 JFXAccessor.runOnJFXThread(true, new Runnable() {
247 @Override
248 public void run() {
249 scene.setRoot(g);
250 }});
251 } else {
252 scene.setRoot(g);
253 }
254 }
255
256 final Canvas canvas0;
257 if( null == res ) {
258 canvas0 = new Canvas();
259 } else {
260 res[0] = new NewtCanvasJFX( glWindow );
261 canvas0 = res[0];
262 }
263 canvas0.setWidth(cw);
264 canvas0.setHeight(ch);
265 if( null == res ) {
266 final GraphicsContext gc = canvas0.getGraphicsContext2D();
267 gc.setFill(Color.BLUE);
268 gc.fillRect(0, 0, cw, ch);
269 }
270 canvas0.relocate(cx, cy);
271
272 final Text text0 = new Text(0, 0, "left");
273 {
274 text0.setFont(new Font(40));
275 text0.relocate(0, height/2);
276 }
277 final Text text1 = new Text(0, 0, "above");
278 {
279 text1.setFont(new Font(40));
280 text1.relocate(width/2, bh-40);
281 }
282 final Text text2 = new Text(0, 0, "right");
283 {
284 text2.setFont(new Font(40));
285 text2.relocate(width-bw, height/2);
286 }
287 final Text text3 = new Text(0, 0, "below");
288 {
289 text3.setFont(new Font(40));
290 text3.relocate(width/2, height-bh);
291 }
292 final Runnable attach2Group = new Runnable() {
293 @Override
294 public void run() {
295 g.getChildren().add(text0);
296 g.getChildren().add(text1);
297 g.getChildren().add(canvas0);
298 g.getChildren().add(text2);
299 g.getChildren().add(text3);
300 } };
301 if( !postAttach && isShowing ) {
302 JFXAccessor.runOnJFXThread(true, attach2Group);
303 } else {
304 attach2Group.run();
305 }
306 if( postAttach ) {
307 if(isShowing) {
308 JFXAccessor.runOnJFXThread(true, new Runnable() {
309 @Override
310 public void run() {
311 scene.setRoot(g);
312 }});
313 } else {
314 scene.setRoot(g);
315 }
316 }
317 }
318
319 protected void runTestAGL( final GLCapabilitiesImmutable caps, final GLEventListener demo,
320 final boolean postAttachNewtCanvas, final boolean postAttachGLWindow,
321 final boolean useAnimator ) throws InterruptedException {
322 if( !JFXAccessor.isJFXAvailable() ) {
323 System.err.println("JFX not available");
324 return;
325 }
326 final GLReadBufferUtil screenshot = new GLReadBufferUtil(false, false);
327 final GLWindow glWindow1;
328 if( null == demo ) {
329 glWindow1 = null;
330 } else {
331 final Screen screen = NewtFactory.createScreen(jfxNewtDisplay, 0);
332 glWindow1 = GLWindow.create(screen, caps);
333 Assert.assertNotNull(glWindow1);
334 Assert.assertEquals(false, glWindow1.isVisible());
335 Assert.assertEquals(false, glWindow1.isNativeValid());
336 Assert.assertNull(glWindow1.getParent());
337 glWindow1.addGLEventListener(demo);
338 glWindow1.addGLEventListener(new GLEventListener() {
339 int displayCount = 0;
340 public void init(final GLAutoDrawable drawable) { }
341 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { }
342 public void display(final GLAutoDrawable drawable) {
343 if(displayCount < 3) {
344 snapshot(displayCount++, null, drawable.getGL(), screenshot, TextureIO.PNG, null);
345 }
346 }
347 public void dispose(final GLAutoDrawable drawable) { }
348 });
349 }
350
351 final NewtCanvasJFX[] glCanvas = null==demo? null : new NewtCanvasJFX[]{null};
352
353 final Scene scene = new Scene(new Group(), defWidth, defHeight);
354 if(!postAttachNewtCanvas) {
355 System.err.println("Stage set.A0");
356 JFXAccessor.runOnJFXThread(true, new Runnable() {
357 public void run() {
358 System.err.println("Stage set.A1");
359 JFXApp.stage.setScene(scene);
360 JFXApp.stage.sizeToScene();
361 System.err.println("Stage set.AX");
362 } });
363 }
364 populateScene( scene, postAttachNewtCanvas, postAttachGLWindow?null:glWindow1, defWidth, defHeight, true, glCanvas);
365 if(postAttachNewtCanvas) {
366 System.err.println("Stage set.B0");
367 JFXAccessor.runOnJFXThread(true, new Runnable() {
368 public void run() {
369 System.err.println("Stage set.B1");
370 JFXApp.stage.setScene(scene);
371 JFXApp.stage.sizeToScene();
372 System.err.println("Stage set.BX");
373 } });
374 }
375
376 if(postAttachGLWindow && null != demo) {
377 glCanvas[0].setNEWTChild(glWindow1);
378 }
379
380 if( null != glWindow1 ) {
381 Assert.assertTrue("GLWindow didn't become visible natively!", NewtTestUtil.waitForRealized(glWindow1, true, awtRobotWaitAction));
382 System.err.println("GLWindow LOS.0: "+glWindow1.getLocationOnScreen(null));
383 glWindow1.addWindowListener(new WindowAdapter() {
384 public void windowResized(final WindowEvent e) {
385 System.err.println("window resized: "+glWindow1.getX()+"/"+glWindow1.getY()+" "+glWindow1.getSurfaceWidth()+"x"+glWindow1.getSurfaceHeight());
386 }
387 public void windowMoved(final WindowEvent e) {
388 System.err.println("window moved: "+glWindow1.getX()+"/"+glWindow1.getY()+" "+glWindow1.getSurfaceWidth()+"x"+glWindow1.getSurfaceHeight());
389 }
390 });
391 final NewtReparentingKeyAdapter newtDemoListener = new NewtJFXReparentingKeyAdapter(JFXApp.stage, glCanvas[0], glWindow1);
392 newtDemoListener.quitAdapterEnable(true);
393 glWindow1.addKeyListener(newtDemoListener);
394 glWindow1.addMouseListener(newtDemoListener);
395 glWindow1.addWindowListener(newtDemoListener);
396
397 final ChangeListener<Number> sizeListener = new ChangeListener<Number>() {
398 @Override public void changed(final ObservableValue<? extends Number> observable, final Number oldValue, final Number newValue) {
399 newtDemoListener.setTitle();
400 } };
401 JFXApp.stage.widthProperty().addListener(sizeListener);
402 JFXApp.stage.heightProperty().addListener(sizeListener);
403
404 }
405 if( null != demo ) {
406 System.err.println("NewtCanvasJFX LOS.0: "+glCanvas[0].getNativeWindow().getLocationOnScreen(null));
407 }
408
409 Animator anim;
410 if(useAnimator && null != demo) {
411 anim = new Animator(glWindow1);
412 anim.start();
413 } else {
414 anim = null;
415 }
416
417 final long lStartTime = System.currentTimeMillis();
418 final long lEndTime = lStartTime + duration;
419 try {
420 while( (System.currentTimeMillis() < lEndTime) ) {
421 generalWaitAction.run();
422 }
423 } catch( final Throwable throwable ) {
424 throwable.printStackTrace();
425 Assume.assumeNoException( throwable );
426 }
427 if(null != anim) {
428 anim.stop();
429 }
430
431 JFXAccessor.runOnJFXThread(true, new Runnable() {
432 public void run() {
433 populateScene( JFXApp.stage.getScene(), false, null, defWidth, defHeight, true, null);
434 JFXApp.stage.sizeToScene();
435 } });
436 }
437
438 @Test
439 public void test00() throws InterruptedException {
440 if( 0 > manualTestID || 0 == manualTestID ) {
441 runTestAGL( null, null,
442 false /* postAttachNewtCanvas */, false /* postAttach */, false /* animator */);
443 }
444 }
445
446 @Test
447 public void test11_preAttachNewtGL_NoAnim() throws InterruptedException {
448 if( 0 > manualTestID || 11 == manualTestID ) {
449 runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
450 false /* postAttachNewtCanvas */, false /* postAttachGLWindow */, false /* animator */);
451 }
452 }
453
454 @Test
455 public void test12_postAttachNewt_NoAnim() throws InterruptedException {
456 if( 0 > manualTestID || 12 == manualTestID ) {
457 runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
458 true /* postAttachNewtCanvas */, false /* postAttachGLWindow */, false /* animator */);
459 }
460 }
461
462 @Test
463 public void test13_postAttachGL_NoAnim() throws InterruptedException {
464 if( 0 > manualTestID || 13 == manualTestID ) {
465 runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
466 false /* postAttachNewtCanvas */, true /* postAttachGLWindow */, false /* animator */);
467 }
468 }
469
470 @Test
471 public void test14_postAttachNewtGL_NoAnim() throws InterruptedException {
472 if( 0 > manualTestID || 14 == manualTestID ) {
473 runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
474 true /* postAttachNewtCanvas */, true /* postAttachGLWindow */, false /* animator */);
475 }
476 }
477
478 @Test
479 public void test21_preAttachNewtGL_DoAnim() throws InterruptedException {
480 if( 0 > manualTestID || 21 == manualTestID ) {
481 runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
482 false /* postAttachNewtCanvas */, false /* postAttachGLWindow */, true /* animator */);
483 }
484 }
485
486 @Test
487 public void test22_postAttachNewt_DoAnim() throws InterruptedException {
488 if( 0 > manualTestID || 22 == manualTestID ) {
489 runTestAGL( new GLCapabilities(GLProfile.getGL2ES2()), new GearsES2(),
490 true /* postAttachNewtCanvas */, false /* postAttachGLWindow */, true /* animator */);
491 }
492 }
493
494 @Test
495 public void test30_MultisampleAndAlpha() throws InterruptedException {
496 if( 0 > manualTestID || 30 == manualTestID ) {
498 caps.setSampleBuffers(true);
499 caps.setNumSamples(2);
500 runTestAGL( caps, new MultisampleDemoES2(true),
501 false /* postAttachNewtCanvas */, false /* postAttachGLWindow */, false /* animator */);
502 }
503 }
504
505 public static void main(final String args[]) {
506 for(int i=0; i<args.length; i++) {
507 if(args[i].equals("-time")) {
508 duration = MiscUtils.atoi(args[++i], duration);
509 }
510 if(args[i].equals("-test")) {
511 manualTestID = MiscUtils.atoi(args[++i], -1);
512 }
513 }
514 System.out.println("durationPerTest: "+duration+", test "+manualTestID);
515 org.junit.runner.JUnitCore.main(TestNewtCanvasJFXGLn.class.getName());
516 }
517}
static long getWindowHandle(final Window stageWindow)
static void runOnJFXThread(final boolean wait, final Runnable task)
Runs given task on the JFX Thread if it has not stopped and if caller is not already on the JFX Threa...
static Display createDisplay(final String name)
Create a Display entity.
static Screen createScreen(final Display display, final int index)
Create a Screen entity.
A screen may span multiple MonitorDevices representing their combined virtual size.
Definition: Screen.java:58
NEWT Window events are provided for notification purposes ONLY.
A NEWT based JFX Canvas specialization allowing a NEWT child Window to be attached using native paren...
Window setNEWTChild(final Window newChild)
Sets a new NEWT child, provoking reparenting.
An implementation of GLAutoDrawable and Window interface, using a delegated Window instance,...
Definition: GLWindow.java:121
Point getLocationOnScreen(final Point storage)
Returns the window's top-left client-area position in the screen.
Definition: GLWindow.java:643
final NativeWindow getParent()
Definition: GLWindow.java:282
final int getSurfaceHeight()
Returns the height of this GLDrawable's surface client area in pixel units.
Definition: GLWindow.java:466
final void addMouseListener(final MouseListener l)
Appends the given MouseListener to the end of the list.
Definition: GLWindow.java:927
final int getX()
Returns the current x position of this window, relative to it's parent.
Definition: GLWindow.java:436
final void addKeyListener(final KeyListener l)
Appends the given com.jogamp.newt.event.KeyListener to the end of the list.
Definition: GLWindow.java:902
final int getY()
Returns the current y position of the top-left corner of the client area relative to it's parent in w...
Definition: GLWindow.java:441
final int getSurfaceWidth()
Returns the width of this GLDrawable's surface client area in pixel units.
Definition: GLWindow.java:461
final void addWindowListener(final WindowListener l)
Appends the given com.jogamp.newt.event.WindowListener to the end of the list.
Definition: GLWindow.java:882
static GLWindow create(final GLCapabilitiesImmutable caps)
Creates a new GLWindow attaching a new Window referencing a new default Screen and default Display wi...
Definition: GLWindow.java:169
Specifies a set of OpenGL capabilities.
void setNumSamples(final int numSamples)
If sample buffers are enabled, indicates the number of buffers to be allocated.
void setSampleBuffers(final boolean enable)
Defaults to false.
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
static String glAvailabilityToString(final AbstractGraphicsDevice device)
Definition: GLProfile.java:333
static GLProfile getGL2ES2(final AbstractGraphicsDevice device)
Returns the GL2ES2 profile implementation, hence compatible w/ GL2ES2.
Definition: GLProfile.java:913
NewtCanvasJFX basic functional integration test of its native parented NEWT child GLWindow attached t...
void runTestAGL(final GLCapabilitiesImmutable caps, final GLEventListener demo, final boolean postAttachNewtCanvas, final boolean postAttachGLWindow, final boolean useAnimator)
JavaFX specializing demo functionality of NewtReparentingKeyAdapter, includes NEWTDemoListener.
static int atoi(final String str, final int def)
Definition: MiscUtils.java:57
static boolean waitForRealized(final Screen screen, final boolean realized, final Runnable waitAction)
final synchronized boolean start()
Starts this animator, if not running.
Definition: Animator.java:344
final synchronized boolean stop()
Stops this animator.
Definition: Animator.java:368
Utility to read out the current FB to TextureData, optionally writing the data back to a texture obje...
static final String PNG
Constant which can be used as a file suffix to indicate a PNG file, value {@value}.
Definition: TextureIO.java:171
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
GL getGL()
Returns the GL pipeline object this GLAutoDrawable uses.
void addGLEventListener(GLEventListener listener)
Adds the given listener to the end of this drawable queue.
Specifies an immutable set of OpenGL capabilities.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.