Changed Swing Active Renderer to use awt.component instead of awt.Canvas

This commit is contained in:
nathan 2018-06-25 22:38:36 +02:00
parent 5a39cf039a
commit f9915fafcb
2 changed files with 32 additions and 56 deletions

View File

@ -15,10 +15,9 @@
*/ */
package dorkbox.util.swing; package dorkbox.util.swing;
import java.awt.Canvas; import java.awt.Component;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.util.List; import java.util.List;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -68,31 +67,24 @@ class ActiveRenderLoop implements Runnable {
// this needs to be synchronized because we don't want to our canvas 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) {
final List<Canvas> activeRenders = SwingActiveRender.activeRenders; final List<Component> activeRenders = SwingActiveRender.activeRenders;
for (Canvas canvas : activeRenders) { for (Component component : activeRenders) {
if (!canvas.isDisplayable()) { if (!component.isDisplayable()) {
continue; continue;
} }
BufferStrategy buffer = canvas.getBufferStrategy(); // maybe the frame was closed, so we must be in a try/catch issue #11
// maybe the frame was closed
try { try {
graphics = buffer.getDrawGraphics(); graphics = component.getGraphics();
canvas.paint(graphics); component.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 // the canvas can be closed as well. can get a "java.lang.IllegalStateException: Component must have a valid peer" if
// peer" if it's already be closed during the getDrawGraphics call. // it's already be closed during the getDrawGraphics call.
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
if (graphics != null) { if (graphics != null) {
graphics.dispose(); graphics.dispose();
// blit the back buffer to the screen
if (buffer != null && !buffer.contentsLost()) {
buffer.show();
}
} }
} }
} }

View File

@ -15,31 +15,30 @@
*/ */
package dorkbox.util.swing; package dorkbox.util.swing;
import java.awt.Canvas; import java.awt.Component;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.util.ArrayList; import java.util.ArrayList;
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.SwingUtilities; import javax.swing.SwingUtilities;
import dorkbox.util.ActionHandlerLong; import dorkbox.util.ActionHandlerLong;
/** /**
* Contains all of the appropriate logic to setup and render via "Active" rendering (instead of "Passive" rendering). This permits us to * Contains all of the appropriate logic to setup and render via "Active" rendering (instead of "Passive" rendering).
* render Windows (and their contents), OFF of the EDT - even though there are other frames/components that are ON the EDT. <br> Because we *
* still want to react to mouse events, etc on the EDT, we do not completely remove the EDT -- we merely allow us to "synchronize" the EDT * This permits us to render components OFF of the EDT - even though there are other frames/components that are ON the EDT.
* object to our thread. It's a little bit hacky, but it works beautifully, and permits MUCH nicer animations. <br> * <br>
* <p/> * Because we still want to react to mouse events, etc on the EDT, we do not completely remove the EDT -- we merely allow us
* <b>It is also important to REMEMBER -- if you add a component to an actively managed Window, YOU MUST make sure to call {@link * to "synchronize" the EDT object to our thread. It's a little bit hacky, but it works beautifully, and permits MUCH nicer animations.
* JComponent#setIgnoreRepaint(boolean)} otherwise this component will "fight" on the EDT for updates. </b> * <br>
*/ */
public final public final
class SwingActiveRender { class SwingActiveRender {
private static Thread activeRenderThread = null; private static Thread activeRenderThread = null;
static final List<Canvas> activeRenders = new ArrayList<Canvas>(); static final List<Component> activeRenders = new ArrayList<Component>();
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
@ -53,45 +52,30 @@ class SwingActiveRender {
/** /**
* Enables the canvas to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based * Enables the component to to added to an "Active Render" thread, at a target "Frames-per-second". This is to support smooth, swing-based
* animations. * animations.
* <p> * <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. * This works by removing this object from EDT updates and manually calls paint on the component, updating it on our own thread, but
* still remaining synchronized with the EDT.
* *
* @param canvas the canvas to add to the ActiveRender thread. * @param component the component to add to the ActiveRender thread.
*/ */
@SuppressWarnings("Duplicates") @SuppressWarnings("Duplicates")
public static public static
void addActiveRender(final Canvas canvas) { void addActiveRender(final Component component) {
// this should be on the EDT // this should be on the EDT
if (!EventQueue.isDispatchThread()) { if (!EventQueue.isDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public public
void run() { void run() {
addActiveRender(canvas); addActiveRender(component);
} }
}); });
return; return;
} }
// setup double-buffering, so we can properly use Active-Rendering, so the animations will be smooth component.setIgnoreRepaint(true);
try {
canvas.createBufferStrategy(2);
} catch (Exception e) {
// sometimes it's added too early. Postpone the event until later
// note: this is different than SwingUtil, because we MUST invoke it later (and not in the current thread)
SwingUtilities.invokeLater(new Runnable() {
@Override
public
void run() {
addActiveRender(canvas);
}
});
return;
}
canvas.setIgnoreRepaint(true);
synchronized (activeRenders) { synchronized (activeRenders) {
if (!hasActiveRenders) { if (!hasActiveRenders) {
@ -99,31 +83,31 @@ class SwingActiveRender {
} }
hasActiveRenders = true; hasActiveRenders = true;
activeRenders.add(canvas); activeRenders.add(component);
} }
} }
/** /**
* Removes a canvas from the ActiveRender queue. This should happen when the canvas is closed. * Removes a component from the ActiveRender queue. This should happen when the component is closed.
* *
* @param canvas the canvas to remove * @param component the component to remove
*/ */
public static public static
void removeActiveRender(final Canvas canvas) { void removeActiveRender(final Component component) {
// this should be on the EDT // this should be on the EDT
if (!EventQueue.isDispatchThread()) { if (!EventQueue.isDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public public
void run() { void run() {
removeActiveRender(canvas); removeActiveRender(component);
} }
}); });
return; return;
} }
synchronized (activeRenders) { synchronized (activeRenders) {
activeRenders.remove(canvas); activeRenders.remove(component);
final boolean hadActiveRenders = !activeRenders.isEmpty(); final boolean hadActiveRenders = !activeRenders.isEmpty();
hasActiveRenders = hadActiveRenders; hasActiveRenders = hadActiveRenders;
@ -133,7 +117,7 @@ class SwingActiveRender {
} }
} }
canvas.setIgnoreRepaint(false); component.setIgnoreRepaint(false);
} }
/** /**