From 33f641d0a4fc4d6d32eb2e0d4e4499168bd8a2ce Mon Sep 17 00:00:00 2001 From: "honfika@gmail.com" Date: Tue, 16 Dec 2014 22:32:43 +0100 Subject: [PATCH] faster switching tag tree nodes (drawables and sounds) --- .../decompiler/flash/gui/ImagePanel.java | 252 ++++++++++-------- .../jpexs/decompiler/flash/gui/MainPanel.java | 8 +- .../decompiler/flash/gui/PreviewPanel.java | 6 + .../decompiler/flash/gui/SoundTagPlayer.java | 62 ++++- 4 files changed, 195 insertions(+), 133 deletions(-) diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 19c0615b0..7d6c9fc27 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -84,7 +84,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis private int selectedDepth = -1; private double zoom = 1.0; - public void selectDepth(int depth) { + public synchronized void selectDepth(int depth) { if (depth != selectedDepth) { this.selectedDepth = depth; } @@ -210,12 +210,14 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis iconPanel.removeMouseMotionListener(l); } - private void updatePos(MouseEvent e, boolean draw) { - if (e == null) { - return; + private void updatePos() { + MouseEvent e; + Timelined timelined; + synchronized (ImagePanel.class) { + e = lastMouseEvent; + timelined = this.timelined; } - - lastMouseEvent = e; + boolean handCursor = false; DepthState newStateUnderCursor = null; if (timelined != null) { @@ -231,16 +233,22 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis Matrix m = new Matrix(); m.translate(-rect.Xmin, -rect.Ymin); m.scale(scale); - Point p = e.getPoint(); - p = iconPanel.toImagePoint(p); + + Point p = e == null ? null : e.getPoint(); List objs = new ArrayList<>(); String ret = ""; - if (p != null) { - int x = p.x; - int y = p.y; - objs = iconPanel.getObjectsUnderPoint(p); - ret += " [" + x + "," + y + "] : "; + synchronized (ImagePanel.class) { + if (timelined == this.timelined) { + p = p == null ? null : iconPanel.toImagePoint(p); + if (p != null) { + int x = p.x; + int y = p.y; + objs = iconPanel.getObjectsUnderPoint(p); + + ret += " [" + x + "," + y + "] : "; + } + } } boolean first = true; @@ -263,16 +271,20 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis if (first) { ret += " - "; } - debugLabel.setText(ret); + + synchronized (ImagePanel.class) { + if (timelined == this.timelined) { + debugLabel.setText(ret); - if (handCursor) { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - if (newStateUnderCursor != stateUnderCursor) { - stateUnderCursor = newStateUnderCursor; - drawFrame(); + if (handCursor) { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + iconPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + if (newStateUnderCursor != stateUnderCursor) { + stateUnderCursor = newStateUnderCursor; + } + } } } } @@ -312,26 +324,24 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseEntered(MouseEvent e) { synchronized (ImagePanel.class) { - drawFrame(); + lastMouseEvent = e; } } @Override public void mouseExited(MouseEvent e) { - stateUnderCursor = null; - lastMouseEvent = null; synchronized (ImagePanel.class) { - drawFrame(); + stateUnderCursor = null; + lastMouseEvent = null; + hideMouseSelection(); } - hideMouseSelection(); } @Override public void mousePressed(MouseEvent e) { - mouseButton = e.getButton(); synchronized (ImagePanel.class) { - updatePos(e, true); - drawFrame(); + mouseButton = e.getButton(); + lastMouseEvent = e; if (stateUnderCursor != null) { ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); DefineButtonSoundTag sounds = b.getSounds(); @@ -344,10 +354,9 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseReleased(MouseEvent e) { - mouseButton = 0; synchronized (ImagePanel.class) { - updatePos(e, true); - drawFrame(); + mouseButton = 0; + lastMouseEvent = e; if (stateUnderCursor != null) { ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); DefineButtonSoundTag sounds = b.getSounds(); @@ -362,12 +371,12 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis iconPanel.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { - DepthState lastUnderCur = stateUnderCursor; synchronized (ImagePanel.class) { - updatePos(e, true); + lastMouseEvent = e; + DepthState lastUnderCur = stateUnderCursor; if (stateUnderCursor != null) { if (lastUnderCur == null || lastUnderCur.instanceId != stateUnderCursor.instanceId) { - //New mouse entered + // New mouse entered ButtonTag b = (ButtonTag) swf.characters.get(stateUnderCursor.characterId); DefineButtonSoundTag sounds = b.getSounds(); if (sounds != null && sounds.buttonSoundChar1 != 0) { //IddleToOverUp @@ -377,7 +386,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } if (lastUnderCur != null) { if (stateUnderCursor == null || stateUnderCursor.instanceId != lastUnderCur.instanceId) { - //Old mouse leave + // Old mouse leave ButtonTag b = (ButtonTag) swf.characters.get(lastUnderCur.characterId); DefineButtonSoundTag sounds = b.getSounds(); if (sounds != null && sounds.buttonSoundChar0 != 0) { //OverUpToIddle @@ -391,7 +400,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void mouseDragged(MouseEvent e) { synchronized (ImagePanel.class) { - updatePos(e, true); + lastMouseEvent = e; } } @@ -399,11 +408,8 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } @Override - public void zoom(double zoom) { + public synchronized void zoom(double zoom) { this.zoom = zoom; - synchronized (ImagePanel.class) { - drawFrame(); - } } @Override @@ -412,12 +418,12 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } @Override - public BufferedImage printScreen() { + public synchronized BufferedImage printScreen() { return iconPanel.getLastImage(); } @Override - public double getZoomToFit() { + public synchronized double getZoomToFit() { if (timelined instanceof BoundedTag) { RECT bounds = ((BoundedTag) timelined).getRect(new HashSet()); double w1 = bounds.getWidth() / SWF.unitDivisor; @@ -441,22 +447,15 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } public void setImage(byte[] data) { - setBackground(View.swfBackgroundColor); - if (timer != null) { - timer.cancel(); - } - timelined = null; - loaded = true; try { - iconPanel.setImg(new SerializableImage(ImageIO.read(new ByteArrayInputStream(data)))); - iconPanel.setOutlines(new ArrayList(), new ArrayList()); + setImage(new SerializableImage(ImageIO.read(new ByteArrayInputStream(data)))); } catch (IOException ex) { Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex); } } @Override - public boolean zoomAvailable() { + public synchronized boolean zoomAvailable() { return timelined != null; } @@ -476,20 +475,24 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } loaded = true; + iconPanel.setImg(null); + iconPanel.setOutlines(new ArrayList(), new ArrayList()); + if (drawable.getTimeline().getFrames().isEmpty()) { - iconPanel.setImg(null); - iconPanel.setOutlines(new ArrayList(), new ArrayList()); return; } + time = 0; play(); } - public void setImage(SerializableImage image) { + public synchronized void setImage(SerializableImage image) { setBackground(View.swfBackgroundColor); if (timer != null) { timer.cancel(); + timer = null; } + timelined = null; loaded = true; stillFrame = true; @@ -498,12 +501,12 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } @Override - public int getCurrentFrame() { + public synchronized int getCurrentFrame() { return frame; } @Override - public int getTotalFrames() { + public synchronized int getTotalFrames() { if (timelined == null) { return 0; } @@ -515,14 +518,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis @Override public void pause() { - if (timer != null) { - timer.cancel(); - timer = null; - } - - synchronized (ImagePanel.class) { - stopAllSounds(); - } + stop(); } private void stopAllSounds() { @@ -535,18 +531,22 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis private void nextFrame() { drawFrame(); - int newframe = (frame + 1) % timelined.getTimeline().getFrameCount(); - if (stillFrame) { - newframe = frame; - } - if (newframe != frame) { - if (newframe == 0) { - stopAllSounds(); + synchronized (ImagePanel.class) { + if (timelined != null) { + int newframe = (frame + 1) % timelined.getTimeline().getFrameCount(); + if (stillFrame) { + newframe = frame; + } + if (newframe != frame) { + if (newframe == 0) { + stopAllSounds(); + } + frame = newframe; + time = 0; + } else { + time++; + } } - frame = newframe; - time = 0; - } else { - time++; } } @@ -614,6 +614,26 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } private void drawFrame() { + Timelined timelined; + int frame; + int time; + DepthState stateUnderCursor; + int mouseButton; + int selectedDepth; + double zoom; + SWF swf; + + synchronized (ImagePanel.class) { + timelined = this.timelined; + frame = this.frame; + time = this.time; + stateUnderCursor = this.stateUnderCursor; + mouseButton = this.mouseButton; + selectedDepth = this.selectedDepth; + zoom = this.zoom; + swf = this.swf; + } + if (timelined == null) { return; } @@ -622,11 +642,11 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis return; } - getOutlines(); + getOutlines(timelined, frame, time, zoom, stateUnderCursor, mouseButton); Matrix mat = new Matrix(); mat.translateX = swf.displayRect.Xmin; mat.translateY = swf.displayRect.Ymin; - updatePos(lastMouseEvent, false); + updatePos(); SerializableImage img = getFrame(swf, frame, time, timelined, stateUnderCursor, mouseButton, selectedDepth, zoom); List sounds = new ArrayList<>(); List soundClasses = new ArrayList<>(); @@ -648,17 +668,17 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } } - iconPanel.setImg(img); + synchronized (ImagePanel.class) { + if (timelined == this.timelined) { + iconPanel.setImg(img); + } + } } private void playSound(SoundTag st) { final SoundTagPlayer sp; try { - sp = new SoundTagPlayer(st, 1); - - synchronized (ImagePanel.class) { - soundPlayers.add(sp); - } + sp = new SoundTagPlayer(st, 1, false); sp.addListener(new PlayerListener() { @Override @@ -668,55 +688,68 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } } }); - sp.play(); + + synchronized (ImagePanel.class) { + if (timer != null) { + soundPlayers.add(sp); + sp.play(); + } + } } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, "Error during playing sound", ex); } } - private void getOutlines() { + private void getOutlines(Timelined timelined, int frame, int time, double zoom, DepthState stateUnderCursor, int mouseButton) { List objs = new ArrayList<>(); List outlines = new ArrayList<>(); Matrix m = new Matrix(); - RECT rect = timelined.getTimeline().displayRect; + Timeline timeline = timelined.getTimeline(); + RECT rect = timeline.displayRect; m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); m.scale(zoom); - timelined.getTimeline().getObjectsOutlines(frame, time, frame, stateUnderCursor, mouseButton, m, objs, outlines); + timeline.getObjectsOutlines(frame, time, frame, stateUnderCursor, mouseButton, m, objs, outlines); for (int i = 0; i < outlines.size(); i++) { outlines.set(i, SHAPERECORD.twipToPixelShape(outlines.get(i))); } - iconPanel.setOutlines(objs, outlines); + + synchronized (ImagePanel.class) { + if (timelined == this.timelined) { + iconPanel.setOutlines(objs, outlines); + } + } } - public void stop() { + public synchronized void stop() { if (timer != null) { timer.cancel(); timer = null; } - synchronized (ImagePanel.class) { - stopAllSounds(); - } + stopAllSounds(); } @Override - public void play() { + public synchronized void play() { pause(); if (timelined != null) { timer = new Timer(); int frameRate = timelined.getTimeline().frameRate; - int framesPerSec = frameRate == 0 ? 0 : 1000 / frameRate; + int msPerFrame = frameRate == 0 ? 0 : 1000 / frameRate; + if (msPerFrame < 1000) { + msPerFrame = 1000; + } timer.schedule(new TimerTask() { boolean first = true; @Override public void run() { - synchronized (ImagePanel.class) { + try { if (timer == null) { return; } - + Timeline timeline = timelined.getTimeline(); if (timeline.getFrameCount() <= 1 && timeline.isSingleFrame()) { if (first) { @@ -726,22 +759,21 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } else { nextFrame(); } + } catch (Exception ex) { + Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex); } } - }, 0, framesPerSec == 0 ? Integer.MAX_VALUE : framesPerSec); + }, 0, msPerFrame); } } @Override - public void rewind() { + public synchronized void rewind() { frame = 0; - synchronized (ImagePanel.class) { - drawFrame(); - } } @Override - public boolean isPlaying() { + public synchronized boolean isPlaying() { if (timelined == null) { return false; } @@ -752,7 +784,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } @Override - public void gotoFrame(int frame) { + public synchronized void gotoFrame(int frame) { if (timelined == null) { return; } @@ -764,13 +796,10 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis return; } this.frame = frame; - synchronized (ImagePanel.class) { - drawFrame(); - } } @Override - public int getFrameRate() { + public synchronized int getFrameRate() { if (timelined == null) { return 1; } @@ -781,7 +810,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } @Override - public boolean isLoaded() { + public synchronized boolean isLoaded() { return loaded; } @@ -791,8 +820,7 @@ public final class ImagePanel extends JPanel implements ActionListener, MediaDis } @Override - public double getZoom() { + public synchronized double getZoom() { return zoom; } - } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index 1b1851d3a..7240311c4 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -234,8 +234,6 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec private CancellableWorker setSourceWorker; public TreeItem oldItem; - private SoundTagPlayer soundThread = null; - public static final String ACTION_SELECT_BKCOLOR = "SELECTCOLOR"; public static final String ACTION_REPLACE = "REPLACE"; @@ -2152,9 +2150,6 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec } folderPreviewPanel.setItems(new ArrayList()); previewPanel.clear(); - if (soundThread != null) { - soundThread.pause(); - } stopFlashPlayer(); if (treeItem instanceof ScriptPack) { final ScriptPack scriptLeaf = (ScriptPack) treeItem; @@ -2296,9 +2291,8 @@ public final class MainPanel extends JPanel implements ActionListener, TreeSelec previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); previewPanel.setImageReplaceButtonVisible(treeItem instanceof DefineSoundTag); try { - soundThread = new SoundTagPlayer((SoundTag) treeItem, Integer.MAX_VALUE); + SoundTagPlayer soundThread = new SoundTagPlayer((SoundTag) treeItem, Integer.MAX_VALUE, true); previewPanel.setMedia(soundThread); - soundThread.play(); } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { logger.log(Level.SEVERE, null, ex); } diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index c5afea211..0be47aede 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -124,6 +124,7 @@ public class PreviewPanel extends JSplitPane implements ActionListener { private ImagePanel imagePanel; private PlayerControls imagePlayControls; + private MediaDisplay media; private BinaryPanel binaryPanel; private GenericTagPanel genericTagPanel; @@ -389,6 +390,7 @@ public class PreviewPanel extends JSplitPane implements ActionListener { } public void setMedia(MediaDisplay media) { + this.media = media; imagePlayControls.setMedia(media); } @@ -439,6 +441,10 @@ public class PreviewPanel extends JSplitPane implements ActionListener { public void clear() { imagePanel.stop(); + if (media != null) { + media.pause(); + } + binaryPanel.setBinaryData(null); genericTagPanel.clear(); fontPanel.clear(); diff --git a/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java b/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java index 3ac08a775..974b777c8 100644 --- a/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java +++ b/src/com/jpexs/decompiler/flash/gui/SoundTagPlayer.java @@ -28,6 +28,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.Line; @@ -45,7 +47,7 @@ public class SoundTagPlayer implements MediaDisplay { private final Clip clip; private int loopCount; - private boolean paused = true; + private boolean paused = false; private final Object playLock = new Object(); private final SoundTag tag; private final List listeners = new ArrayList<>(); @@ -66,20 +68,10 @@ public class SoundTagPlayer implements MediaDisplay { private static final int FRAME_DIVISOR = 8000; - public SoundTagPlayer(SoundTag tag, int loops) throws LineUnavailableException, IOException, UnsupportedAudioFileException { + public SoundTagPlayer(final SoundTag tag, int loops, boolean async) throws LineUnavailableException, IOException, UnsupportedAudioFileException { this.tag = tag; this.loopCount = loops; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - List soundData = tag.getRawSoundData(); - SWF swf = ((Tag) tag).getSwf(); - List siss = new ArrayList<>(); - for (byte[] data : soundData) { - siss.add(new SWFInputStream(swf, data)); - } - tag.getSoundFormat().createWav(siss, baos); clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class)); - clip.open(AudioSystem.getAudioInputStream(new ByteArrayInputStream(baos.toByteArray()))); - clip.addLineListener(new LineListener() { @Override @@ -101,8 +93,48 @@ public class SoundTagPlayer implements MediaDisplay { } } }); + + if (!async) { + paused = true; + openSound(tag); + } else { + new Thread() { + + @Override + public void run() { + try { + openSound(tag); + } catch (IOException | LineUnavailableException | UnsupportedAudioFileException ex) { + Logger.getLogger(SoundTagPlayer.class.getName()).log(Level.SEVERE, null, ex); + } + synchronized (playLock) { + if (!paused) { + play(); + } + } + } + + }.start(); + } } + private void openSound(SoundTag tag) throws IOException, LineUnavailableException, UnsupportedAudioFileException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + List soundData = tag.getRawSoundData(); + SWF swf = ((Tag) tag).getSwf(); + List siss = new ArrayList<>(); + for (byte[] data : soundData) { + siss.add(new SWFInputStream(swf, data)); + } + + tag.getSoundFormat().createWav(siss, baos); + byte[] wavData = baos.toByteArray(); + + synchronized (playLock) { + clip.open(AudioSystem.getAudioInputStream(new ByteArrayInputStream(wavData))); + } + } + @Override public int getCurrentFrame() { @@ -142,7 +174,7 @@ public class SoundTagPlayer implements MediaDisplay { } } } - + @Override public void rewind() { gotoFrame(0); @@ -150,7 +182,9 @@ public class SoundTagPlayer implements MediaDisplay { @Override public boolean isPlaying() { - return clip.isActive(); + synchronized (playLock) { + return clip.isActive(); + } } @Override