diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java
index b2c098ec7..4299dbb96 100644
--- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java
+++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java
@@ -1,1431 +1,1434 @@
-/*
- * Copyright (C) 2010-2016 JPEXS
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.jpexs.decompiler.flash.gui;
-
-import com.jpexs.decompiler.flash.SWF;
-import com.jpexs.decompiler.flash.SWFInputStream;
-import com.jpexs.decompiler.flash.action.Action;
-import com.jpexs.decompiler.flash.action.LocalDataArea;
-import com.jpexs.decompiler.flash.action.Stage;
-import com.jpexs.decompiler.flash.configuration.Configuration;
-import com.jpexs.decompiler.flash.ecma.Undefined;
-import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
-import com.jpexs.decompiler.flash.gui.player.MediaDisplay;
-import com.jpexs.decompiler.flash.gui.player.MediaDisplayListener;
-import com.jpexs.decompiler.flash.gui.player.Zoom;
-import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag;
-import com.jpexs.decompiler.flash.tags.DoActionTag;
-import com.jpexs.decompiler.flash.tags.base.BoundedTag;
-import com.jpexs.decompiler.flash.tags.base.ButtonTag;
-import com.jpexs.decompiler.flash.tags.base.CharacterTag;
-import com.jpexs.decompiler.flash.tags.base.DrawableTag;
-import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
-import com.jpexs.decompiler.flash.tags.base.RenderContext;
-import com.jpexs.decompiler.flash.tags.base.SoundTag;
-import com.jpexs.decompiler.flash.tags.base.TextTag;
-import com.jpexs.decompiler.flash.timeline.DepthState;
-import com.jpexs.decompiler.flash.timeline.Frame;
-import com.jpexs.decompiler.flash.timeline.Timeline;
-import com.jpexs.decompiler.flash.timeline.Timelined;
-import com.jpexs.decompiler.flash.types.ConstantColorColorTransform;
-import com.jpexs.decompiler.flash.types.RECT;
-import com.jpexs.decompiler.flash.types.SOUNDINFO;
-import com.jpexs.helpers.ByteArrayRange;
-import com.jpexs.helpers.Cache;
-import com.jpexs.helpers.SerializableImage;
-import com.jpexs.helpers.Stopwatch;
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Cursor;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.Shape;
-import java.awt.Transparency;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionAdapter;
-import java.awt.event.MouseMotionListener;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.awt.image.VolatileImage;
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.sound.sampled.LineUnavailableException;
-import javax.sound.sampled.UnsupportedAudioFileException;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-
-/**
- *
- * @author JPEXS
- */
-public final class ImagePanel extends JPanel implements MediaDisplay {
-
- private static final Logger logger = Logger.getLogger(ImagePanel.class.getName());
-
- private final List listeners = new ArrayList<>();
-
- private Timelined timelined;
-
- private boolean stillFrame = false;
-
- private Timer timer;
-
- private int frame = -1;
-
- private boolean loop;
-
- private LocalDataArea lda;
-
- private boolean zoomAvailable = false;
-
- private SWF swf;
-
- private boolean loaded;
-
- private int mouseButton;
-
- private final JLabel debugLabel = new JLabel("-");
-
- private Point cursorPosition = null;
-
- private MouseEvent lastMouseEvent = null;
-
- private final List soundPlayers = new ArrayList<>();
-
- private final Cache displayObjectCache = Cache.getInstance(false, false, "displayObject");
-
- private final IconPanel iconPanel;
-
- private int time = 0;
-
- private int selectedDepth = -1;
-
- private Zoom zoom = new Zoom();
-
- private final Object delayObject = new Object();
-
- private boolean drawReady;
-
- private final int drawWaitLimit = 50; // ms
-
- private TextTag textTag;
-
- private TextTag newTextTag;
-
- private int msPerFrame;
-
- private final boolean lowQuality = false;
-
- private final double LQ_FACTOR = 2;
-
- public synchronized void selectDepth(int depth) {
- if (depth != selectedDepth) {
- this.selectedDepth = depth;
- }
-
- hideMouseSelection();
- }
-
- public void fireMediaDisplayStateChanged() {
- for (MediaDisplayListener l : listeners) {
- l.mediaDisplayStateChanged(this);
- }
- }
-
- @Override
- public void addEventListener(MediaDisplayListener listener) {
- listeners.add(listener);
- }
-
- @Override
- public void removeEventListener(MediaDisplayListener listener) {
- listeners.remove(listener);
- }
-
- private class IconPanel extends JPanel {
-
- private SerializableImage _img;
-
- private Rectangle _rect = null;
-
- private ButtonTag mouseOverButton = null;
-
- private boolean autoFit = false;
-
- private boolean allowMove = true;
-
- private Point dragStart = null;
-
- private Point offsetPoint = new Point(0, 0);
-
- private synchronized SerializableImage getImg() {
- return _img;
- }
-
- public synchronized Rectangle getRect() {
- return _rect;
- }
-
- public boolean hasAllowMove() {
- return allowMove;
- }
-
- VolatileImage renderImage;
-
- public void render() {
- SerializableImage img = getImg();
- Rectangle rect = getRect();
- if (img == null) {
- return;
- }
- Graphics2D g2 = null;
- do {
-
- int valid = renderImage.validate(View.getDefaultConfiguration());
-
- if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {
- renderImage = View.createRenderImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
- }
-
- try {
- g2 = renderImage.createGraphics();
- g2.setPaint(View.transparentPaint);
- g2.fill(new Rectangle(0, 0, getWidth(), getHeight()));
- g2.setComposite(AlphaComposite.SrcOver);
- g2.setPaint(View.getSwfBackgroundColor());
- g2.fill(new Rectangle(0, 0, getWidth(), getHeight()));
-
- g2.setComposite(AlphaComposite.SrcOver);
- if (rect != null) {
- g2.drawImage(img.getBufferedImage(), rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, 0, 0, img.getWidth(), img.getHeight(), null);
- }
- } finally {
- if (g2 != null) {
- g2.dispose();
- }
- }
-
- } while (renderImage.contentsLost());
- }
-
- public IconPanel() {
- addComponentListener(new ComponentAdapter() {
- @Override
- public void componentResized(ComponentEvent e) {
- renderImage = View.createRenderImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
- if (_img != null) {
- calcRect();
- render();
- }
- repaint();
- }
-
- });
- addMouseListener(new MouseAdapter() {
- @Override
- public void mousePressed(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON1) {
- dragStart = e.getPoint();
- }
- }
-
- @Override
- public void mouseReleased(MouseEvent e) {
- if (e.getButton() == MouseEvent.BUTTON1) {
- dragStart = null;
- }
- }
-
- });
- addMouseMotionListener(new MouseMotionAdapter() {
- @Override
- public void mouseDragged(MouseEvent e) {
- if (dragStart != null && allowMove) {
- Point dragEnd = e.getPoint();
- Point delta = new Point(dragEnd.x - dragStart.x, dragEnd.y - dragStart.y);
- offsetPoint.x += delta.x;
- offsetPoint.y += delta.y;
- dragStart = dragEnd;
- repaint();
- }
- }
-
- });
- }
-
- public void setAutoFit(boolean autoFit) {
- this.autoFit = autoFit;
- repaint();
- }
-
- public synchronized BufferedImage getLastImage() {
- if (_img == null) {
- return null;
- }
- return _img.getBufferedImage();
- }
-
- public synchronized void setImg(SerializableImage img) {
- this._img = img;
- if (img != null) {
- calcRect();
- render();
- }
- repaint();
- }
-
- public synchronized Point toImagePoint(Point p) {
- if (_img == null) {
- return null;
- }
-
- return new Point((p.x - _rect.x) * _img.getWidth() / _rect.width, (p.y - _rect.y) * _img.getHeight() / _rect.height);
- }
-
- private void setAllowMove(boolean allowMove) {
- this.allowMove = allowMove;
- if (!allowMove) {
- offsetPoint = new Point();
- }
- }
-
- private synchronized void calcRect() {
- if (_img != null) {
- int w1 = (int) (_img.getWidth() * (lowQuality ? LQ_FACTOR : 1));
- int h1 = (int) (_img.getHeight() * (lowQuality ? LQ_FACTOR : 1));
-
- int w2 = getWidth();
- int h2 = getHeight();
-
- int w;
- int h;
- if (autoFit) {
- if (w1 <= w2 && h1 <= h2) {
- w = w1;
- h = h1;
- } else {
-
- h = h1 * w2 / w1;
- if (h > h2) {
- w = w1 * h2 / h1;
- h = h2;
- } else {
- w = w2;
- }
- }
- } else {
- w = w1;
- h = h1;
- }
-
- setAllowMove(h > h2 || w > w2);
- _rect = new Rectangle(getWidth() / 2 - w / 2 + offsetPoint.x, getHeight() / 2 - h / 2 + offsetPoint.y, w, h);
- } else {
- _rect = null;
- }
- }
-
- @Override
- protected void paintComponent(Graphics g) {
- Graphics2D g2d = (Graphics2D) g;
-
- if (renderImage != null) {
- calcRect();
- if (renderImage.validate(View.getDefaultConfiguration()) != VolatileImage.IMAGE_OK) {
- renderImage = View.createRenderImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
- render();
- }
-
- if (renderImage != null) {
- g2d.drawImage(renderImage, 0, 0, null);
- }
- }
- g2d.setColor(Color.red);
-
- DecimalFormat df = new DecimalFormat();
- df.setMaximumFractionDigits(2);
- df.setMinimumFractionDigits(0);
- df.setGroupingUsed(false);
-
- float frameLoss = 100 - (getFpsIs() / fpsShouldBe * 100);
-
- if (Configuration._debugMode.get()) {
- g2d.drawString("frameLoss:" + df.format(frameLoss) + "%", 20, 20);
- }
- }
- }
-
- @Override
- public void setBackground(Color bg) {
- if (iconPanel != null) {
- iconPanel.setBackground(bg);
- }
- super.setBackground(bg);
- }
-
- @Override
- public synchronized void addMouseListener(MouseListener l) {
- iconPanel.addMouseListener(l);
- }
-
- @Override
- public synchronized void removeMouseListener(MouseListener l) {
- iconPanel.removeMouseListener(l);
- }
-
- @Override
- public synchronized void addMouseMotionListener(MouseMotionListener l) {
- iconPanel.addMouseMotionListener(l);
- }
-
- @Override
- public synchronized void removeMouseMotionListener(MouseMotionListener l) {
- iconPanel.removeMouseMotionListener(l);
- }
-
- private void updatePos(Timelined timelined, MouseEvent lastMouseEvent, Timer thisTimer) {
- if (timelined != null) {
-
- BoundedTag bounded = (BoundedTag) timelined;
- RECT rect = bounded.getRect();
- int width = rect.getWidth();
- double scale = 1.0;
- /*if (width > swf.displayRect.getWidth()) {
- scale = (double) swf.displayRect.getWidth() / (double) width;
- }*/
- Matrix m = Matrix.getTranslateInstance(-rect.Xmin, -rect.Ymin);
- m.scale(scale);
-
- Point p = lastMouseEvent == null ? null : lastMouseEvent.getPoint();
-
- synchronized (ImagePanel.class) {
- if (timer == thisTimer) {
- cursorPosition = p;
- }
- }
- }
- }
-
- private void showSelectedName() {
- if (selectedDepth > -1 && frame > -1) {
- DepthState ds = timelined.getTimeline().getFrame(frame).layers.get(selectedDepth);
- if (ds != null) {
- CharacterTag cht = timelined.getTimeline().swf.getCharacter(ds.characterId);
- if (cht != null) {
- debugLabel.setText(cht.getName());
- }
- }
- }
- }
-
- public void hideMouseSelection() {
- if (selectedDepth > -1) {
- showSelectedName();
- } else {
- debugLabel.setText(" - ");
- }
- }
-
- public ImagePanel() {
- super(new BorderLayout());
- //iconPanel.setHorizontalAlignment(JLabel.CENTER);
- setOpaque(true);
- setBackground(View.getDefaultBackgroundColor());
-
- loop = true;
- iconPanel = new IconPanel();
- //labelPan.add(label, new GridBagConstraints());
- add(iconPanel, BorderLayout.CENTER);
- add(debugLabel, BorderLayout.NORTH);
- iconPanel.addMouseListener(new MouseAdapter() {
-
- @Override
- public void mouseEntered(MouseEvent e) {
- synchronized (ImagePanel.class) {
- lastMouseEvent = e;
- redraw();
- }
- }
-
- @Override
- public void mouseExited(MouseEvent e) {
- synchronized (ImagePanel.class) {
- lastMouseEvent = null;
- hideMouseSelection();
- redraw();
- }
- }
-
- @Override
- public void mousePressed(MouseEvent e) {
- synchronized (ImagePanel.class) {
- mouseButton = e.getButton();
- lastMouseEvent = e;
- redraw();
- ButtonTag button = iconPanel.mouseOverButton;
- if (button != null) {
- DefineButtonSoundTag sounds = button.getSounds();
- if (sounds != null && sounds.buttonSoundChar2 != 0) { // OverUpToOverDown
- playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar2), sounds.buttonSoundInfo2, timer);
- }
- }
- }
- }
-
- @Override
- public void mouseReleased(MouseEvent e) {
- synchronized (ImagePanel.class) {
- mouseButton = 0;
- lastMouseEvent = e;
- redraw();
- ButtonTag button = iconPanel.mouseOverButton;
- if (button != null) {
- DefineButtonSoundTag sounds = button.getSounds();
- if (sounds != null && sounds.buttonSoundChar3 != 0) { // OverDownToOverUp
- playSound((SoundTag) swf.getCharacter(sounds.buttonSoundChar3), sounds.buttonSoundInfo3, timer);
- }
- }
- }
- }
-
- });
- iconPanel.addMouseMotionListener(new MouseMotionAdapter() {
- @Override
- public void mouseMoved(MouseEvent e) {
- synchronized (ImagePanel.class) {
- lastMouseEvent = e;
- redraw();
- }
- }
-
- @Override
- public void mouseDragged(MouseEvent e) {
- synchronized (ImagePanel.class) {
- lastMouseEvent = e;
- redraw();
- }
- }
-
- });
- }
-
- private synchronized void redraw() {
- if (timer == null && timelined != null) {
- startTimer(timelined.getTimeline(), false);
- }
- }
-
- @Override
- public synchronized void zoom(Zoom zoom) {
- boolean modified = this.zoom.value != zoom.value || this.zoom.fit != zoom.fit;
- if (modified) {
- this.zoom = zoom;
- displayObjectCache.clear();
- redraw();
- if (textTag != null) {
- setText(textTag, newTextTag);
- }
-
- fireMediaDisplayStateChanged();
- }
- }
-
- @Override
- public synchronized BufferedImage printScreen() {
- return iconPanel.getLastImage();
- }
-
- @Override
- public synchronized double getZoomToFit() {
- if (timelined != null) {
- RECT bounds = timelined.getRect();
- double w1 = bounds.getWidth() / SWF.unitDivisor;
- double h1 = bounds.getHeight() / SWF.unitDivisor;
-
- double w2 = getWidth();
- double h2 = getHeight();
-
- double w;
- double h;
- h = h1 * w2 / w1;
- if (h > h2) {
- w = w1 * h2 / h1;
- } else {
- w = w2;
- }
-
- if (w1 <= Double.MIN_NORMAL) {
- return 1.0;
- }
-
- return (double) w / (double) w1;
- }
-
- return 1;
- }
-
- @Override
- public synchronized boolean zoomAvailable() {
- return zoomAvailable;
- }
-
- public void setTimelined(final Timelined drawable, final SWF swf, int frame) {
- Stage stage = new Stage(drawable) {
- @Override
- public void callFrame(int frame) {
- executeFrame(frame);
- }
-
- @Override
- public Object callFunction(long functionAddress, long functionLength, List