Changed ActiveRenderer to only permit Canvas now (instead of JFrame)

This commit is contained in:
nathan 2017-08-24 22:00:32 +02:00
parent a6300d8ba5
commit b2e29e38cd
3 changed files with 75 additions and 65 deletions

View File

@ -15,10 +15,11 @@
*/ */
package dorkbox.util.swing; package dorkbox.util.swing;
import java.awt.Canvas;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.Window;
import java.awt.image.BufferStrategy; import java.awt.image.BufferStrategy;
import java.util.List;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -65,28 +66,32 @@ class ActiveRenderLoop implements Runnable {
actionHandlerLong.handle(updateDeltaNanos); actionHandlerLong.handle(updateDeltaNanos);
} }
// this needs to be synchronized because we don't want to our frame removed WHILE we are rendering it. // this needs to be synchronized because we don't want to our canvas removed WHILE we are rendering it.
synchronized (SwingActiveRender.activeRenders) { synchronized (SwingActiveRender.activeRenders) {
for (int i = 0; i < SwingActiveRender.activeRenders.size(); i++) { final List<Canvas> activeRenders = SwingActiveRender.activeRenders;
Window window = SwingActiveRender.activeRenders.get(i);
final BufferStrategy buffer = window.getBufferStrategy(); for (Canvas canvas : activeRenders) {
if (!canvas.isDisplayable()) {
continue;
}
BufferStrategy buffer = canvas.getBufferStrategy();
// maybe the frame was closed // maybe the frame was closed
if (buffer != null) { try {
try { graphics = buffer.getDrawGraphics();
graphics = buffer.getDrawGraphics(); canvas.paint(graphics);
window.paint(graphics); } catch (Exception e) {
} catch (Exception e) { // the frame can be close as well. can get a "java.lang.IllegalStateException: Component must have a valid
e.printStackTrace(); // peer" if it's already be closed during the getDrawGraphics call.
} finally { e.printStackTrace();
if (graphics != null) { } finally {
graphics.dispose(); if (graphics != null) {
graphics.dispose();
// blit the back buffer to the screen // blit the back buffer to the screen
if (!buffer.contentsLost()) { if (!buffer.contentsLost()) {
buffer.show(); buffer.show();
}
} }
} }
} }

View File

@ -15,17 +15,14 @@
*/ */
package dorkbox.util.swing; package dorkbox.util.swing;
import java.awt.Component; import java.awt.Canvas;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Window;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import dorkbox.util.ActionHandlerLong; import dorkbox.util.ActionHandlerLong;
@ -42,7 +39,7 @@ public final
class SwingActiveRender { class SwingActiveRender {
private static Thread activeRenderThread = null; private static Thread activeRenderThread = null;
static final List<Window> activeRenders = new ArrayList<Window>(); static final List<Canvas> activeRenders = new ArrayList<Canvas>();
static final List<ActionHandlerLong> activeRenderEvents = new CopyOnWriteArrayList<ActionHandlerLong>(); static final List<ActionHandlerLong> activeRenderEvents = new CopyOnWriteArrayList<ActionHandlerLong>();
// volatile, so that access triggers thread synchrony, since 1.6. See the Java Language Spec, Chapter 17 // volatile, so that access triggers thread synchrony, since 1.6. See the Java Language Spec, Chapter 17
@ -56,56 +53,77 @@ class SwingActiveRender {
/** /**
* Enables the window to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based * Enables the canvas to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based
* animations. <br> This works by removing this object from EDT updates, and instead manually calls paint(g) on the window, updating it * animations.
* on our own thread. * <p>
* This works by removing this object from EDT updates, and instead manually calls paint(g) on the canvas, updating it on our own thread.
* *
* @param window the window to add to the ActiveRender thread. * @param canvas the canvas to add to the ActiveRender thread.
*/ */
@SuppressWarnings("Duplicates")
public static public static
void addActiveRender(final Window window) { void addActiveRender(final Canvas canvas) {
// this should be on the EDT // this should be on the EDT
if (!EventQueue.isDispatchThread()) { if (!EventQueue.isDispatchThread()) {
throw new RuntimeException("adding a swing Window to be actively rendered must be done on the EDT."); SwingUtilities.invokeLater(new Runnable() {
@Override
public
void run() {
addActiveRender(canvas);
}
});
return;
} }
// setup double-buffering, so we can properly use Active-Rendering, so the animations will be smooth // setup double-buffering, so we can properly use Active-Rendering, so the animations will be smooth
window.createBufferStrategy(2); try {
canvas.createBufferStrategy(2);
// have to specify ALL children in Window to ignore EDT paint requests } catch (Exception e) {
Deque<Component> components = new ArrayDeque<Component>(8); // sometimes it's added too early. Postpone the event until later
components.add(window); // note: this is different than SwingUtil, because we MUST invoke it later (and not in the current thread)
SwingUtilities.invokeLater(new Runnable() {
Component[] c; @Override
Component pop; public
while ((pop = components.poll()) != null) { void run() {
pop.setIgnoreRepaint(true); addActiveRender(canvas);
}
if (pop instanceof JComponent) { });
c = ((JComponent) pop).getComponents(); return;
Collections.addAll(components, c);
}
} }
canvas.setIgnoreRepaint(true);
synchronized (activeRenders) { synchronized (activeRenders) {
if (!hasActiveRenders) { if (!hasActiveRenders) {
setupActiveRenderThread(); setupActiveRenderThread();
} }
hasActiveRenders = true; hasActiveRenders = true;
activeRenders.add(window); activeRenders.add(canvas);
} }
} }
/** /**
* Removes a window from the ActiveRender queue. This should happen when the window is closed. * Removes a canvas from the ActiveRender queue. This should happen when the canvas is closed.
* *
* @param window the window to remove * @param canvas the canvas to remove
*/ */
public static public static
void removeActiveRender(final Window window) { void removeActiveRender(final Canvas canvas) {
// this should be on the EDT
if (!EventQueue.isDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public
void run() {
removeActiveRender(canvas);
}
});
return;
}
synchronized (activeRenders) { synchronized (activeRenders) {
activeRenders.remove(window); activeRenders.remove(canvas);
final boolean hadActiveRenders = !activeRenders.isEmpty(); final boolean hadActiveRenders = !activeRenders.isEmpty();
hasActiveRenders = hadActiveRenders; hasActiveRenders = hadActiveRenders;
@ -115,20 +133,7 @@ class SwingActiveRender {
} }
} }
// have to specify ALL children in window to obey EDT paint requests canvas.setIgnoreRepaint(false);
Deque<Component> components = new ArrayDeque<Component>(8);
components.add(window);
Component[] c;
Component pop;
while ((pop = components.poll()) != null) {
pop.setIgnoreRepaint(false);
if (pop instanceof JComponent) {
c = ((JComponent) pop).getComponents();
Collections.addAll(components, c);
}
}
} }
/** /**

View File

@ -24,7 +24,7 @@ class SynchronizedEventQueue extends EventQueue {
public static final Object MUTEX = new Object(); public static final Object MUTEX = new Object();
private static final SynchronizedEventQueue instance = new SynchronizedEventQueue(); private static final SynchronizedEventQueue instance = new SynchronizedEventQueue();
private static boolean alreadyInUse = false; private static volatile boolean alreadyInUse = false;
public static synchronized public static synchronized
void install() { void install() {