From 4da6d7a023d7eb4aeb19a99e6010e7595e3def5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Wed, 14 Dec 2022 22:31:39 +0100 Subject: [PATCH] Transform panel with move, rotate, scale, shear, matrix transformations. WIP --- .../flash/configuration/Configuration.java | 5 + .../flash/gui/BoundsChangeListener.java | 27 + .../decompiler/flash/gui/ImagePanel.java | 47 +- .../decompiler/flash/gui/PreviewPanel.java | 134 +++-- .../decompiler/flash/gui/TransformPanel.java | 498 ++++++++++++++++++ .../gui/graphics/rotateanticlockwise16.png | Bin 0 -> 741 bytes .../flash/gui/graphics/rotateclockwise16.png | Bin 0 -> 741 bytes .../flash/gui/graphics/transformmove16.png | Bin 0 -> 575 bytes .../flash/gui/graphics/transformrotate16.png | Bin 0 -> 802 bytes .../flash/gui/graphics/transformscale16.png | Bin 0 -> 671 bytes .../flash/gui/graphics/transformskew16.png | Bin 0 -> 764 bytes .../locales/AdvancedSettingsDialog.properties | 3 + .../flash/gui/locales/MainFrame.properties | 4 +- 13 files changed, 656 insertions(+), 62 deletions(-) create mode 100644 src/com/jpexs/decompiler/flash/gui/BoundsChangeListener.java create mode 100644 src/com/jpexs/decompiler/flash/gui/TransformPanel.java create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/rotateanticlockwise16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/rotateclockwise16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/transformmove16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/transformrotate16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/transformscale16.png create mode 100644 src/com/jpexs/decompiler/flash/gui/graphics/transformskew16.png diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index 608a85163..145ec03cb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -839,6 +839,11 @@ public final class Configuration { @ConfigurationCategory("script") public static ConfigurationItem deobfuscateAs12RemoveInvalidNamesAssignments = null; + @ConfigurationDefaultDouble(0.6) + @ConfigurationName("gui.splitPanePlace.dividerLocationPercent") + @ConfigurationInternal + public static ConfigurationItem guiSplitPanePlaceDividerLocationPercent = null; + private enum OSId { WINDOWS, OSX, UNIX } diff --git a/src/com/jpexs/decompiler/flash/gui/BoundsChangeListener.java b/src/com/jpexs/decompiler/flash/gui/BoundsChangeListener.java new file mode 100644 index 000000000..4022a192b --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/BoundsChangeListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 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 java.awt.geom.Rectangle2D; + +/** + * + * @author JPEXS + */ +public interface BoundsChangeListener { + public void boundsChanged(Rectangle2D newBounds); +} diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 5dbb46e8e..2f2048963 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -253,7 +253,30 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } private SerializableImage imgPlay = null; + + private List boundsChangeListeners = new ArrayList<>(); + + public void addBoundsChangeListener(BoundsChangeListener listener) { + boundsChangeListeners.add(listener); + } + + public void removeBoundsChangeListener(BoundsChangeListener listener) { + boundsChangeListeners.remove(listener); + } + + private void fireBoundsChange(Rectangle2D bounds) { + for (BoundsChangeListener listener:boundsChangeListeners) { + listener.boundsChanged(bounds); + } + } + + public Rectangle2D getTransformBounds() { + return bounds; + } + + + private SerializableImage getImagePlay() { if (imgPlay != null) { return imgPlay; @@ -407,7 +430,8 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } registrationPoint = null; calculateFreeTransform(); - hideMouseSelection(); + hideMouseSelection(); + redraw(); } public void fireMediaDisplayStateChanged() { @@ -534,7 +558,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int y = rect.y < 0 ? 0 : rect.y; if (freeTransformDepth > -1) { - RECT timRect = timelined.getRectWithStrokes(); + RECT timRect = timelined.getRect(); double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; int offsetX = (int) (SWF.unitDivisor * iconPanel.getWidth() / zoomDouble / 2 - timRect.getWidth() / 2); int offsetY = (int) (SWF.unitDivisor * iconPanel.getHeight() / zoomDouble / 2 - timRect.getHeight() / 2); @@ -561,7 +585,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { int axisX = 0; int axisY = 0; double zoomDouble = zoom.fit ? getZoomToFit() : zoom.value; - RECT timRect = timelined.getRectWithStrokes(); + RECT timRect = timelined.getRect(); axisX = rect.x - (int) (timRect.Xmin * zoomDouble / SWF.unitDivisor); axisY = rect.y - (int) (timRect.Ymin * zoomDouble / SWF.unitDivisor); g2.setComposite(BlendComposite.Invert); @@ -1360,8 +1384,8 @@ public final class ImagePanel extends JPanel implements MediaDisplay { w1 = (int) (_img.getWidth() * (lowQuality ? LQ_FACTOR : 1)); h1 = (int) (_img.getHeight() * (lowQuality ? LQ_FACTOR : 1)); } else { - w1 = (int) (timelined.getRectWithStrokes().getWidth() * zoomDouble / SWF.unitDivisor); - h1 = (int) (timelined.getRectWithStrokes().getHeight() * zoomDouble / SWF.unitDivisor); + w1 = (int) (timelined.getRect().getWidth() * zoomDouble / SWF.unitDivisor); + h1 = (int) (timelined.getRect().getHeight() * zoomDouble / SWF.unitDivisor); } //HERE @@ -1718,7 +1742,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public synchronized double getZoomToFit() { if (timelined != null) { - RECT bounds = timelined.getRectWithStrokes(); + RECT bounds = timelined.getRect(); double w1 = bounds.getWidth() / SWF.unitDivisor; double h1 = bounds.getHeight() / SWF.unitDivisor; @@ -1812,6 +1836,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { frame = ButtonTag.FRAME_UP; } + bounds = null; displayObjectCache.clear(); this.timelined = drawable; this.swf = swf; @@ -2266,7 +2291,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { aRect = new ExportRectangle(_rect); ExportRectangle viewRect = new ExportRectangle(new RECT()); - RECT timRect = timelined.getRectWithStrokes(); + RECT timRect = timelined.getRect(); double w = timRect.getWidth() * zoomDouble / SWF.unitDivisor; @@ -2417,7 +2442,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { synchronized (lock) { Reference boundsRef = new Reference<>(null); - RECT rect = timelined.getRectWithStrokes(); + RECT rect = timelined.getRect(); _viewRect = getViewRect(); @@ -2425,7 +2450,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { AffineTransform tempTrans2 = transformUpdated == null ? null : new AffineTransform(transformUpdated); //HERE - RECT timRect = timelined.getRectWithStrokes(); + RECT timRect = timelined.getRect(); int offsetX = (int) (SWF.unitDivisor * iconPanel.getWidth() / zoomDouble / 2 - timRect.getWidth() / 2); int offsetY = (int) (SWF.unitDivisor * iconPanel.getHeight() / zoomDouble / 2 - timRect.getHeight() / 2); offsetX *= zoomDouble; @@ -2506,6 +2531,10 @@ public final class ImagePanel extends JPanel implements MediaDisplay { } if (newBounds != null) { bounds = newBounds; + + RECT timRectNoStrokes = timelined.getRect(); + Rectangle2D bounds1 = new Rectangle2D.Double((bounds.getX() - _rect.x)/zoomDouble + timRectNoStrokes.Xmin/SWF.unitDivisor, (bounds.getY() - _rect.y)/zoomDouble + timRectNoStrokes.Ymin/SWF.unitDivisor ,bounds.getWidth() / zoomDouble, bounds.getHeight() / zoomDouble); + fireBoundsChange(bounds1); } } } diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index eb18a3d0c..8fd40c44b 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -191,7 +191,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private JButton genericCancelButton; - private JButton placeFreeTransformButton; + private JButton placeTransformButton; private JButton placeEditButton; @@ -221,7 +221,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private HexView unknownHexView; - private final int PLACE_EDIT_FREETRANSFORM = 1; + private final int PLACE_EDIT_TRANSFORM = 1; private final int PLACE_EDIT_RAW = 2; private int placeEditMode = 0; @@ -234,11 +234,15 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private JLabel buildValueLabel = new JLabel(); private JLabel compileDateValueLabel = new JLabel(); - private JButton imageFreeTransformButton; + private JButton imageTransformButton; - private JButton imageFreeTransformSaveButton; - private JButton imageFreeTransformCancelButton; + private JButton imageTransformSaveButton; + private JButton imageTransformCancelButton; + private TransformPanel imageTransformPanel; + + private TransformPanel placeTransformPanel; + public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; setDividerSize(this.readOnly ? 0 : dividerSize); @@ -495,28 +499,32 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel JPanel previewCnt = new JPanel(new BorderLayout()); imagePanel = new ImagePanel(); imagePanel.setLoop(Configuration.loopMedia.get()); + + imageTransformPanel = new TransformPanel(imagePanel); + previewCnt.add(imageTransformPanel, BorderLayout.EAST); + previewCnt.add(imagePanel, BorderLayout.CENTER); JPanel buttonsPanel = new JPanel(new FlowLayout()); - imageFreeTransformButton = new JButton(mainPanel.translate("button.freetransform"), View.getIcon("freetransform16")); - imageFreeTransformButton.setMargin(new Insets(3, 3, 3, 10)); - imageFreeTransformButton.addActionListener(this::freeTransformImageButtonActionPerformed); + imageTransformButton = new JButton(mainPanel.translate("button.transform"), View.getIcon("freetransform16")); + imageTransformButton.setMargin(new Insets(3, 3, 3, 10)); + imageTransformButton.addActionListener(this::transformImageButtonActionPerformed); - imageFreeTransformSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); - imageFreeTransformSaveButton.setMargin(new Insets(3, 3, 3, 10)); - imageFreeTransformSaveButton.addActionListener(this::saveImageFreeTransformButtonActionPerformed); + imageTransformSaveButton = new JButton(mainPanel.translate("button.save"), View.getIcon("save16")); + imageTransformSaveButton.setMargin(new Insets(3, 3, 3, 10)); + imageTransformSaveButton.addActionListener(this::saveImageTransformButtonActionPerformed); - imageFreeTransformCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); - imageFreeTransformCancelButton.setMargin(new Insets(3, 3, 3, 10)); - imageFreeTransformCancelButton.addActionListener(this::cancelImageFreeTransformButtonActionPerformed); + imageTransformCancelButton = new JButton(mainPanel.translate("button.cancel"), View.getIcon("cancel16")); + imageTransformCancelButton.setMargin(new Insets(3, 3, 3, 10)); + imageTransformCancelButton.addActionListener(this::cancelImageTransformButtonActionPerformed); - buttonsPanel.add(imageFreeTransformButton); - buttonsPanel.add(imageFreeTransformSaveButton); - buttonsPanel.add(imageFreeTransformCancelButton); + buttonsPanel.add(imageTransformButton); + buttonsPanel.add(imageTransformSaveButton); + buttonsPanel.add(imageTransformCancelButton); - imageFreeTransformSaveButton.setVisible(false); - imageFreeTransformCancelButton.setVisible(false); + imageTransformSaveButton.setVisible(false); + imageTransformCancelButton.setVisible(false); previewCnt.add(imagePlayControls = new PlayerControls(mainPanel, imagePanel, buttonsPanel), BorderLayout.SOUTH); imagePlayControls.setMedia(imagePanel); @@ -658,10 +666,13 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel JPanel previewCnt = new JPanel(new BorderLayout()); placeImagePanel = new ImagePanel(); + + placeTransformPanel = new TransformPanel(placeImagePanel); //imagePanel.setLoop(Configuration.loopMedia.get()); previewCnt.add(placeImagePanel, BorderLayout.CENTER); PlayerControls placeImagePlayControls = new PlayerControls(mainPanel, placeImagePanel, null); previewCnt.add(placeImagePlayControls, BorderLayout.SOUTH); + previewCnt.add(placeTransformPanel, BorderLayout.EAST); placeImagePlayControls.setMedia(placeImagePanel); previewPanel.add(previewCnt, BorderLayout.CENTER); JLabel prevIntLabel = new HeaderLabel(mainPanel.translate("swfpreview.internal")); @@ -669,7 +680,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel previewPanel.add(prevIntLabel, BorderLayout.NORTH); placeGenericPanel = new GenericTagTreePanel(mainPanel); - placeSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, previewPanel, placeGenericPanel); + placeSplitPane = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, previewPanel, placeGenericPanel, Configuration.guiSplitPanePlaceDividerLocationPercent); placeTagCard.add(placeSplitPane, BorderLayout.CENTER); //placeSplitPane.setDividerLocation(800); @@ -679,9 +690,9 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } private JPanel createPlaceTagButtonsPanel() { - placeFreeTransformButton = new JButton(mainPanel.translate("button.freetransform"), View.getIcon("freetransform16")); - placeFreeTransformButton.setMargin(new Insets(3, 3, 3, 10)); - placeFreeTransformButton.addActionListener(this::freeTransformPlaceTagButtonActionPerformed); + placeTransformButton = new JButton(mainPanel.translate("button.transform"), View.getIcon("freetransform16")); + placeTransformButton.setMargin(new Insets(3, 3, 3, 10)); + placeTransformButton.addActionListener(this::transformPlaceTagButtonActionPerformed); placeEditButton = new JButton(mainPanel.translate("button.edit"), View.getIcon("edit16")); placeEditButton.setMargin(new Insets(3, 3, 3, 10)); placeEditButton.addActionListener(this::editPlaceTagButtonActionPerformed); @@ -695,7 +706,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeCancelButton.setVisible(false); ButtonsPanel placeTagButtonsPanel = new ButtonsPanel(); - placeTagButtonsPanel.add(placeFreeTransformButton); + placeTagButtonsPanel.add(placeTransformButton); placeTagButtonsPanel.add(placeEditButton); placeTagButtonsPanel.add(placeSaveButton); placeTagButtonsPanel.add(placeCancelButton); @@ -733,12 +744,12 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel showCardLeft(DRAW_PREVIEW_CARD); parametersPanel.setVisible(false); imagePlayControls.setMedia(imagePanel); - imageFreeTransformButton.setVisible(allowFreeTransform); + imageTransformButton.setVisible(allowFreeTransform); if ((timelined instanceof Tag) && ((Tag) timelined).isReadOnly()) { - imageFreeTransformButton.setVisible(false); + imageTransformButton.setVisible(false); } - imageFreeTransformSaveButton.setVisible(false); - imageFreeTransformCancelButton.setVisible(false); + imageTransformSaveButton.setVisible(false); + imageTransformCancelButton.setVisible(false); imagePanel.setTimelined(timelined, swf, frame, showObjectsUnderCursor, autoPlay, frozen, alwaysDisplay, muted, mutable); } @@ -947,7 +958,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeEditButton.setEnabled(true); placeSaveButton.setVisible(false); placeCancelButton.setVisible(false); - placeFreeTransformButton.setVisible(!tag.isReadOnly() && !readOnly); + placeTransformButton.setVisible(!tag.isReadOnly() && !readOnly); } public void setImageReplaceButtonVisible(boolean showImage, boolean showAlpha, boolean showShape, boolean showSound) { @@ -1164,7 +1175,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } private void savePlaceTagButtonActionPerformed(ActionEvent evt) { - if (placeEditMode == PLACE_EDIT_FREETRANSFORM) { + if (placeEditMode == PLACE_EDIT_TRANSFORM) { MATRIX matrix = placeImagePanel.getNewMatrix(); placeTag.setPlaceFlagHasMatrix(true); placeTag.setMatrix(matrix); @@ -1172,6 +1183,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeImagePanel.selectDepth(placeTag.getDepth()); placeImagePanel.freeTransformDepth(-1); placeTag.getTimelined().resetTimeline(); + placeTransformPanel.setVisible(false); + placeGenericPanel.setVisible(true); } Tag hilightTag = null; if (placeEditMode == PLACE_EDIT_RAW) { @@ -1184,7 +1197,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel } placeGenericPanel.setEditMode(false, null); } - placeFreeTransformButton.setVisible(true); + placeTransformButton.setVisible(true); placeEditButton.setVisible(true); placeSaveButton.setVisible(false); placeCancelButton.setVisible(false); @@ -1198,32 +1211,44 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel placeEditMode = PLACE_EDIT_RAW; placeGenericPanel.setEditMode(true, placeTag); placeEditButton.setVisible(false); - placeFreeTransformButton.setVisible(false); + placeTransformButton.setVisible(false); placeSaveButton.setVisible(true); placeCancelButton.setVisible(true); } - private void freeTransformPlaceTagButtonActionPerformed(ActionEvent evt) { + private void transformPlaceTagButtonActionPerformed(ActionEvent evt) { TreeItem item = mainPanel.getCurrentTree().getCurrentTreeItem(); if (item == null) { return; } - placeEditMode = PLACE_EDIT_FREETRANSFORM; + placeEditMode = PLACE_EDIT_TRANSFORM; + placeGenericPanel.setVisible(false); placeImagePanel.selectDepth(-1); - placeImagePanel.freeTransformDepth(placeTag.getDepth()); + + placeTransformPanel.setVisible(true); placeEditButton.setVisible(false); - placeFreeTransformButton.setVisible(false); + placeTransformButton.setVisible(false); placeSaveButton.setVisible(true); - placeCancelButton.setVisible(true); + placeCancelButton.setVisible(true); + + Timer t = new Timer(); + t.schedule(new TimerTask() { + @Override + public void run() { + placeImagePanel.freeTransformDepth(placeTag.getDepth()); + placeTransformPanel.load(); + } + }, 10); //add some delay before controls are hidden } - private void saveImageFreeTransformButtonActionPerformed(ActionEvent evt) { + private void saveImageTransformButtonActionPerformed(ActionEvent evt) { Matrix matrix = new Matrix(imagePanel.getNewMatrix()); + imageTransformPanel.setVisible(false); imagePanel.freeTransformDepth(-1); - imageFreeTransformButton.setVisible(true); - imageFreeTransformCancelButton.setVisible(false); - imageFreeTransformSaveButton.setVisible(false); + imageTransformButton.setVisible(true); + imageTransformCancelButton.setVisible(false); + imageTransformSaveButton.setVisible(false); DefineSpriteTag item = (DefineSpriteTag)mainPanel.getCurrentTree().getCurrentTreeItem(); for (Tag t:item.getTags()) { @@ -1241,15 +1266,16 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel mainPanel.reload(true); } - private void cancelImageFreeTransformButtonActionPerformed(ActionEvent evt) { + private void cancelImageTransformButtonActionPerformed(ActionEvent evt) { + imageTransformPanel.setVisible(false); imagePanel.freeTransformDepth(-1); - imageFreeTransformButton.setVisible(true); - imageFreeTransformCancelButton.setVisible(false); - imageFreeTransformSaveButton.setVisible(false); + imageTransformButton.setVisible(true); + imageTransformCancelButton.setVisible(false); + imageTransformSaveButton.setVisible(false); mainPanel.reload(true); } - private void freeTransformImageButtonActionPerformed(ActionEvent evt) { + private void transformImageButtonActionPerformed(ActionEvent evt) { TreeItem item = mainPanel.getCurrentTree().getCurrentTreeItem(); if (item == null) { return; @@ -1311,32 +1337,36 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel imagePanel.setTimelined(sprite2, fSwf, 0, true, true, true, true, true, false); imagePanel.selectDepth(-1); - imageFreeTransformButton.setVisible(false); - imageFreeTransformSaveButton.setVisible(true); - imageFreeTransformCancelButton.setVisible(true); + imageTransformButton.setVisible(false); + imageTransformSaveButton.setVisible(true); + imageTransformCancelButton.setVisible(true); + imageTransformPanel.setVisible(true); Timer t = new Timer(); t.schedule(new TimerTask() { @Override public void run() { imagePanel.freeTransformDepth(placeTag2.getDepth()); + imageTransformPanel.load(); } }, 10); //add some delay before controls are hidden } private void cancelPlaceTagButtonActionPerformed(ActionEvent evt) { - if (placeEditMode == PLACE_EDIT_FREETRANSFORM) { + if (placeEditMode == PLACE_EDIT_TRANSFORM) { placeImagePanel.selectDepth(placeTag.getDepth()); placeImagePanel.freeTransformDepth(-1); placeTag.setMatrix(oldMatrix); placeTag.getTimelined().resetTimeline(); + placeTransformPanel.setVisible(false); + placeGenericPanel.setVisible(true); } if (placeEditMode == PLACE_EDIT_RAW) { placeGenericPanel.setEditMode(false, null); } placeEditButton.setVisible(true); - placeFreeTransformButton.setVisible(true); + placeTransformButton.setVisible(true); placeSaveButton.setVisible(false); placeCancelButton.setVisible(false); } @@ -1372,7 +1402,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel || metadataSaveButton.isVisible() || placeSaveButton.isVisible() || fontPanel.isEditing() - || imageFreeTransformSaveButton.isVisible(); + || imageTransformSaveButton.isVisible(); } public void selectImageDepth(int depth) { diff --git a/src/com/jpexs/decompiler/flash/gui/TransformPanel.java b/src/com/jpexs/decompiler/flash/gui/TransformPanel.java new file mode 100644 index 000000000..069c9b64c --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/TransformPanel.java @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2022 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.helpers.Reference; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.geom.Rectangle2D; +import java.text.DecimalFormat; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.border.BevelBorder; + +/** + * + * @author JPEXS + */ +public class TransformPanel extends JPanel { + + private JTextField moveHorizontalTextField = new JTextField(8); + private JTextField moveVerticalTextField = new JTextField(8); + private JComboBox moveUnitComboBox = new JComboBox<>(); + private JCheckBox moveRelativeCheckBox = new JCheckBox("Relative move"); + + private JTextField scaleWidthTextField = new JTextField(formatDouble(100), 8); + private JTextField scaleHeightTextField = new JTextField(formatDouble(100), 8); + private JComboBox scaleUnitComboBox = new JComboBox<>(); + private JCheckBox scaleProportionallyCheckBox = new JCheckBox("Scale proportionally"); + + private JTextField rotateTextField = new JTextField(formatDouble(0), 8); + private JComboBox rotateUnitComboBox = new JComboBox<>(); + private JToggleButton rotateAntiClockwiseToggleButton = new JToggleButton(View.getIcon("rotateanticlockwise16")); + private JToggleButton rotateClockwiseToggleButton = new JToggleButton(View.getIcon("rotateclockwise16")); + + private JTextField skewHorizontalTextField = new JTextField(formatDouble(0), 8); + private JTextField skewVerticalTextField = new JTextField(formatDouble(0), 8); + private JComboBox skewUnitComboBox = new JComboBox<>(); + + private JTextField matrixATextField = new JTextField(formatDouble(1), 8); + private JTextField matrixBTextField = new JTextField(formatDouble(0), 8); + private JTextField matrixCTextField = new JTextField(formatDouble(0),8); + private JTextField matrixDTextField = new JTextField(formatDouble(1),8); + private JTextField matrixETextField = new JTextField(formatDouble(0), 8); + private JTextField matrixFTextField = new JTextField(formatDouble(0), 8); + private JCheckBox matrixEditCurrentCheckBox = new JCheckBox("Edit current matrix"); + + private ImagePanel imagePanel; + + private Rectangle2D bounds = new Rectangle2D.Double(0, 0, 1, 1); + + + public static enum UnitKind { + LENGTH, + ANGLE + } + public static enum Unit { + PX("px", 1.0, UnitKind.LENGTH), + TWIP("twip", 20.0, UnitKind.LENGTH), + PERCENT("%", 0.0, UnitKind.LENGTH), + TURN("turn", 1/360.0, UnitKind.ANGLE), + DEG("°", 1, UnitKind.ANGLE), + RAD("rad", Math.PI/180, UnitKind.ANGLE), + GRAD("grad", 1/0.9, UnitKind.ANGLE); + + private Unit(String name, double value, UnitKind kind) { + this.name = name; + this.value = value; + this.kind = kind; + } + + private final String name; + private final double value; + private final UnitKind kind; + + @Override + public String toString() { + return name; + } + + public double getValue() { + return value; + } + + public UnitKind getKind() { + return kind; + } + } + + private class UnitComboItem { + Unit unit; + String title; + } + + public TransformPanel(ImagePanel imagePanel) { + + imagePanel.addBoundsChangeListener(new BoundsChangeListener() { + @Override + public void boundsChanged(Rectangle2D newBounds) { + update(newBounds); + } + }); + setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + this.imagePanel = imagePanel; + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + + add(makeHeader("Move","transformmove16")); + JPanel movePanel = new JPanel(new GridBagLayout()); + addRow(movePanel, 0, new JLabel("Horizontal:"), moveHorizontalTextField, moveUnitComboBox); + addRow(movePanel, 1, new JLabel("Vertical:"), moveVerticalTextField); + addJoinedRow(movePanel, 2, moveRelativeCheckBox, 3); + addJoinedRow(movePanel, 3, makeClearApplyPanel(this::applyMoveActionPerformed, this::clearMoveActionPerformed), 3); + add(movePanel); + + moveUnitComboBox.addItem(Unit.PX); + moveUnitComboBox.addItem(Unit.TWIP); + + moveUnitComboBox.setSelectedItem(Unit.PX); + + addUnitChangeListener(moveUnitComboBox, new UnitChangedListener() { + @Override + public void unitChanged(Unit prevUnit, Unit newUnit) { + try{ + double moveHorizontal = Double.parseDouble(moveHorizontalTextField.getText()); + double moveVertical = Double.parseDouble(moveVerticalTextField.getText()); + moveHorizontalTextField.setText(formatDouble(convertUnit(moveHorizontal, prevUnit, newUnit))); + moveVerticalTextField.setText(formatDouble(convertUnit(moveVertical, prevUnit, newUnit))); + } catch(NumberFormatException nfe) { + return; + } + } + }); + + add(makeHeader("Scale", "transformscale16")); + + JPanel scalePanel = new JPanel(new GridBagLayout()); + addRow(scalePanel, 0, new JLabel("Width:"), scaleWidthTextField, scaleUnitComboBox); + addRow(scalePanel, 1, new JLabel("Height:"), scaleHeightTextField); + addJoinedRow(scalePanel, 2, scaleProportionallyCheckBox, 3); + addJoinedRow(scalePanel, 3, makeClearApplyPanel(this::applyScaleActionPerformed, this::clearScaleActionPerformed), 3); + add(scalePanel); + scaleUnitComboBox.addItem(Unit.PERCENT); + scaleUnitComboBox.addItem(Unit.PX); + scaleUnitComboBox.addItem(Unit.TWIP); + + scaleUnitComboBox.setSelectedItem(Unit.PERCENT); + + addUnitChangeListener(scaleUnitComboBox, new UnitChangedListener() { + @Override + public void unitChanged(Unit prevUnit, Unit newUnit) { + try{ + double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); + double scaleHeight = Double.parseDouble(scaleHeightTextField.getText()); + + if (prevUnit == Unit.PERCENT) { + scaleWidthTextField.setText(formatDouble(convertUnit(bounds.getWidth() * scaleWidth / 100, Unit.PX, newUnit))); + scaleHeightTextField.setText(formatDouble(convertUnit(bounds.getHeight() * scaleHeight / 100, Unit.PX, newUnit))); + return; + } + if (newUnit == Unit.PERCENT) { + double scaleWidthPx = convertUnit(scaleWidth, prevUnit, Unit.PX); + double scaleHeightPx = convertUnit(scaleHeight, prevUnit, Unit.PX); + scaleWidthTextField.setText(formatDouble((scaleWidthPx * 100 / bounds.getWidth()))); + scaleHeightTextField.setText(formatDouble((scaleHeightPx * 100 / bounds.getHeight()))); + return; + } + + scaleWidthTextField.setText(formatDouble(convertUnit(scaleWidth, prevUnit, newUnit))); + scaleHeightTextField.setText(formatDouble(convertUnit(scaleHeight, prevUnit, newUnit))); + + } catch(NumberFormatException nfe) { + return; + } + } + }); + + + scaleWidthTextField.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + if (scaleProportionallyCheckBox.isSelected() && scaleUnitComboBox.getSelectedItem() != Unit.PERCENT) { + double ratio = bounds.getHeight() / bounds.getWidth(); + try { + double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); + double scaleHeight = ratio * scaleWidth; + scaleHeightTextField.setText(formatDouble(scaleHeight)); + }catch(NumberFormatException nfe) { + + } + } + } + }); + + scaleHeightTextField.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + if (scaleProportionallyCheckBox.isSelected() && scaleUnitComboBox.getSelectedItem() != Unit.PERCENT) { + double ratio = bounds.getWidth() / bounds.getHeight(); + try { + double scaleHeight = Double.parseDouble(scaleHeightTextField.getText()); + double scaleWidth = ratio * scaleHeight; + scaleWidthTextField.setText(formatDouble(scaleWidth)); + }catch(NumberFormatException nfe) { + + } + } + } + }); + + + add(makeHeader("Rotate", "transformrotate16")); + + ButtonGroup clockGroup = new ButtonGroup(); + clockGroup.add(rotateClockwiseToggleButton); + clockGroup.add(rotateAntiClockwiseToggleButton); + JPanel rotatePanel = new JPanel(new GridBagLayout()); + addRow(rotatePanel, 0, new JLabel("Angle:"), rotateTextField, rotateUnitComboBox, rotateAntiClockwiseToggleButton, rotateClockwiseToggleButton); + addJoinedRow(rotatePanel, 1, makeClearApplyPanel(this::applyRotateActionPerformed, this::clearRotateActionPerformed), 5); + + add(rotatePanel); + + rotateUnitComboBox.addItem(Unit.TURN); + rotateUnitComboBox.addItem(Unit.DEG); + rotateUnitComboBox.addItem(Unit.RAD); + rotateUnitComboBox.addItem(Unit.GRAD); + + rotateUnitComboBox.setSelectedItem(Unit.TURN); + + rotateClockwiseToggleButton.setSelected(true); + + addUnitChangeListener(rotateUnitComboBox, new UnitChangedListener() { + @Override + public void unitChanged(Unit prevUnit, Unit newUnit) { + try{ + double rotate = Double.parseDouble(rotateTextField.getText()); + rotateTextField.setText(formatDouble(convertUnit(rotate, prevUnit, newUnit))); + + } catch(NumberFormatException nfe) { + return; + } + } + }); + + add(makeHeader("Skew", "transformskew16")); + + JPanel skewPanel = new JPanel(new GridBagLayout()); + addRow(skewPanel, 0, new JLabel("Horizontal:"), skewHorizontalTextField, skewUnitComboBox); + addRow(skewPanel, 1, new JLabel("Vertical:"), skewVerticalTextField); + addJoinedRow(skewPanel, 2, makeClearApplyPanel(this::applySkewActionPerformed, this::clearSkewActionPerformed), 3); + + add(skewPanel); + + skewUnitComboBox.addItem(Unit.PX); + skewUnitComboBox.addItem(Unit.TWIP); + skewUnitComboBox.addItem(Unit.TURN); + skewUnitComboBox.addItem(Unit.DEG); + skewUnitComboBox.addItem(Unit.RAD); + skewUnitComboBox.addItem(Unit.GRAD); + + skewUnitComboBox.setSelectedItem(Unit.DEG); + + addUnitChangeListener(skewUnitComboBox, new UnitChangedListener() { + @Override + public void unitChanged(Unit prevUnit, Unit newUnit) { + try{ + double skewHorizontal = Double.parseDouble(skewHorizontalTextField.getText()); + double skewVertical = Double.parseDouble(skewVerticalTextField.getText()); + skewHorizontalTextField.setText(formatDouble(convertUnit(skewHorizontal, prevUnit, newUnit))); + skewVerticalTextField.setText(formatDouble(convertUnit(skewVertical, prevUnit, newUnit))); + + } catch(NumberFormatException nfe) { + return; + } + } + }); + + + add(makeHeader("Matrix", null)); + + JPanel matrixPanel = new JPanel(new GridBagLayout()); + addRow(matrixPanel, 0, new JLabel("A"), matrixATextField, new JLabel("C"), matrixCTextField, new JLabel("E"), matrixETextField); + addRow(matrixPanel, 1, new JLabel("B"), matrixBTextField, new JLabel("D"), matrixDTextField, new JLabel("F"), matrixFTextField); + addJoinedRow(matrixPanel,2, matrixEditCurrentCheckBox, 6); + addJoinedRow(matrixPanel, 3, makeClearApplyPanel(this::applyMatrixActionPerformed, this::clearMatrixActionPerformed), 6); + + add(matrixPanel); + + setVisible(false); + } + + public void load() { + clearMoveActionPerformed(null); + clearScaleActionPerformed(null); + clearRotateActionPerformed(null); + clearSkewActionPerformed(null); + clearMatrixActionPerformed(null); + } + + private void update(Rectangle2D bounds) { + this.bounds = bounds; + if (!moveRelativeCheckBox.isSelected()) { + moveHorizontalTextField.setText(formatDouble(convertUnit(bounds.getX(), Unit.PX, (Unit)moveUnitComboBox.getSelectedItem()))); + moveVerticalTextField.setText(formatDouble(convertUnit(bounds.getY(), Unit.PX, (Unit)moveUnitComboBox.getSelectedItem()))); + } + if (scaleProportionallyCheckBox.isSelected() && scaleUnitComboBox.getSelectedItem() != Unit.PERCENT) { + try + { + double ratio = bounds.getHeight() / bounds.getWidth(); + + double scaleWidth = Double.parseDouble(scaleWidthTextField.getText()); + double scaleHeight = ratio * scaleWidth; + scaleHeightTextField.setText(formatDouble(scaleHeight)); + } + catch(NumberFormatException nfe) { + + } + } + } + + + private void clearMoveActionPerformed(ActionEvent e) { + Rectangle2D bounds = imagePanel.getTransformBounds(); + if (bounds != null) { + moveHorizontalTextField.setText(formatDouble(bounds.getX())); + moveVerticalTextField.setText(formatDouble(bounds.getY())); + } + moveRelativeCheckBox.setSelected(false); + } + + private void applyMoveActionPerformed(ActionEvent e) { + //TODO + } + + private void clearScaleActionPerformed(ActionEvent e) { + scaleWidthTextField.setText(formatDouble(100)); + scaleHeightTextField.setText(formatDouble(100)); + scaleUnitComboBox.setSelectedItem(Unit.PERCENT); + scaleProportionallyCheckBox.setSelected(true); + } + + private void applyScaleActionPerformed(ActionEvent e) { + + } + + private void clearRotateActionPerformed(ActionEvent e) { + rotateTextField.setText(formatDouble(0)); + rotateUnitComboBox.setSelectedItem(Unit.TURN); + rotateClockwiseToggleButton.setSelected(true); + } + + private void applyRotateActionPerformed(ActionEvent e) { + + } + + private void clearSkewActionPerformed(ActionEvent e) { + skewHorizontalTextField.setText(formatDouble(0)); + skewVerticalTextField.setText(formatDouble(0)); + skewUnitComboBox.setSelectedItem(Unit.DEG); + } + + private void applySkewActionPerformed(ActionEvent e) { + + } + + private void clearMatrixActionPerformed(ActionEvent e) { + matrixATextField.setText(formatDouble(1)); + matrixBTextField.setText(formatDouble(0)); + matrixCTextField.setText(formatDouble(0)); + matrixDTextField.setText(formatDouble(1)); + matrixETextField.setText(formatDouble(0)); + matrixFTextField.setText(formatDouble(0)); + matrixEditCurrentCheckBox.setSelected(false); + } + + private void applyMatrixActionPerformed(ActionEvent e) { + + } + + private void addJoinedRow(JPanel panel, int rownum, Component comp, int numCols) { + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.insets = new Insets(2, 2, 2, 2); + c.gridx = 0; + c.gridy = rownum; + c.gridwidth = numCols; + panel.add(comp, c); + } + + private void addRow(JPanel panel, int rownum, Component ...comp) { + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.insets = new Insets(2, 2, 2, 2); + for (int i = 0; i < comp.length; i++) { + c.gridx = i; + c.gridy = rownum; + panel.add(comp[i], c); + } + } + + private JPanel makeHeader(String title, String icon) { + JPanel headerPanel = new JPanel(new FlowLayout()); + JLabel label = new JLabel(title); + if (icon != null) { + label.setIcon(View.getIcon(icon)); + } + headerPanel.add(label); + headerPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + return headerPanel; + } + + private JPanel makeClearApplyPanel(ActionListener onApply, ActionListener onClear) { + JPanel buttonsPanel = new JPanel(new FlowLayout()); + JButton clearButton = new JButton("Clear"); + clearButton.addActionListener(onClear); + JButton applyButton = new JButton("Apply"); + applyButton.addActionListener(onApply); + buttonsPanel.add(clearButton); + buttonsPanel.add(applyButton); + return buttonsPanel; + } + + private void addUnitChangeListener(JComboBox unitComboBox, UnitChangedListener listener) { + final Reference previousValue = new Reference<>((Unit)unitComboBox.getSelectedItem()); + unitComboBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + Unit newValue = (Unit)e.getItem(); + listener.unitChanged(previousValue.getVal(), newValue); + previousValue.setVal(newValue); + } + } + }); + } + + + interface UnitChangedListener { + public void unitChanged(Unit prevUnit, Unit newUnit); + } + + private static double convertUnit(double value, Unit sourceUnit, Unit targetUnit) { + if (sourceUnit == targetUnit) { + return value; + } + if (sourceUnit.kind != targetUnit.kind) { + return value; //no conversion + } + //to px + return (value / sourceUnit.value) * targetUnit.value; + } + + private static String formatDouble(double value) { + DecimalFormat df = new DecimalFormat("0.000"); + return df.format(value); + } + + public static void main(String[] args) { + System.out.println("20 twip to px =" + convertUnit(20, Unit.TWIP, Unit.PX)); + System.out.println("1 turn to deg =" + convertUnit(1, Unit.TURN, Unit.DEG)); + System.out.println("1 deg to rad =" + convertUnit(1, Unit.DEG, Unit.RAD)); + System.out.println("1 deg to grad =" + convertUnit(1, Unit.DEG, Unit.GRAD)); + } +} + + diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/rotateanticlockwise16.png b/src/com/jpexs/decompiler/flash/gui/graphics/rotateanticlockwise16.png new file mode 100644 index 0000000000000000000000000000000000000000..7da5cfd01f64b548068b81fc3a826deab22df1d3 GIT binary patch literal 741 zcmV66l;*P8 zv=5ynC2f8Tx=@SKbg36XR|Q=abP?Ex6P2KgB%G-i1ztrF1fhr!nYl3SA_v|0G3!E$ z)|UBEm~*qaHa$B%@7B>%ql+GRIPgBt|975`_hphKfg58CvYl*4vnawUO4!Q7Jf!BX zMz_FlzON1ep2xN9Z$L#WW{sJ-`cwI*u`6{CPvcR{EP3!`q8F22rjaP%%CkD$i33!U z*kU@auQHv$tak)UfpL;DLP$tWDnN#&1kas=user2#Pal-^g@u!u4+SbZbp{AG;bfq z7Fsax^<(lQu(l2)P?pIshi~E6ICAI=X1s1p&AR$Xmbd|+p6kmB^YK2=g|9(BhQ@$} zcd)L^V@7L$anXn994*8MMjzrng+k=R z#kCLyL^MPsA5^TD57qw%fRAwH=O?g8v>J$E*y(8bcR1*{iw^qsVAIxNRRB046r_AH zwz6$|VWiU$Ib73$x3pH>fE7#ufl8qd`xg1XXrTGUNY`d|R^G-%IUZVruKnyIl(j%o z0yfrotdZTz*^diH4^VAXg)X@o zEcU~_+)rX+&~Q~Lhys>HPkalCw_q%}jC095QAMHY~isE00000NkvXXu0mjf+9pub literal 0 HcmV?d00001 diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/rotateclockwise16.png b/src/com/jpexs/decompiler/flash/gui/graphics/rotateclockwise16.png new file mode 100644 index 0000000000000000000000000000000000000000..11b6dd34e0f1bc24623adfa9185271c343ae02cc GIT binary patch literal 741 zcmVGaTv!xXHy1EQb%N{ zm1Gg2`L+u&N+d09X)kmk(H{^9(T$cMAyK3_tqi&nBj_S85`^AXVK>>0O1ednW-U#( zT)DZ)e3_f4-#PB&ETSIHbNKQ6p6~a0Io~76vWz52C|1>mF)Y&=iBKJpL0q9+=_MBR zdRm{>a*3!ZIK?E5pt6ngp)fad(sy$r?-W~2+m$pWg|FUmdWO4kOpdsoUnK1hf@9aE zasjqg@Tp{U%H+N5{)2Ed5Lx(W^LO+@!nkw zO*i582C#pE7kad~~uQOF|bzTuLbsXL5K*U!R=Hp!I!m|{hcL+v? zxi-*4_1o9f_P&8{3js!hU6dB&A$C-!?IJG961ef8d*h1eN+*^!^O5@g4m!nEP`II} zhGfSk8jAP%(Hp-g%QdVccvMHRc8=O-;lAj3VI_nuI8TGLA}TF9NieP@Y9PB7IATQ> zT}(f&Ws4R|%phljQZpo;$Si{Fm+4}vQrd)UJ#&|o#LN^eR?))Q%OF_?4FuHjT2SgewK&Qe1l ze4p6`A4M6LMU{x6A%Y<{A3PnZBHxM4IL7#Ht0!CFJnK5}E{`K6gwctr{oY1&`qeKY zi6Q-Bd5-rmitQ00000NkvXXu0mjf`lnDe literal 0 HcmV?d00001 diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/transformmove16.png b/src/com/jpexs/decompiler/flash/gui/graphics/transformmove16.png new file mode 100644 index 0000000000000000000000000000000000000000..6e13dd3d6f0400d367a6f55e559cea5173dd13c8 GIT binary patch literal 575 zcmV-F0>J%=P)v;Y0u{{LXTCnGj>SPgJ)+4(=y$B<#grFUR{uD>w@3-iwZdu!Y= z3}Hg%xVP^H^Z$JPuxskZGYrxaA`CJ>3}OSt_7DsMkj<{GJD&lWN1Qn z1;!vTpxi2u8f3jp$bK&HH)8nt>yIRmdk=`uG5{US1ab6PkQh)d6r={E79|XvTXy^p zbJAm2di5hilD|2FtkRAD%s`zW2y$Tjl3V}({Qbw^sw)Ci^MN7CRUa58;Mie=1|lQF zi%-88g!p(E&c6E3@ciLD1|}vZkQ*7~RMZ$A-@5~hiGK`d#f8CYUVQoi)`J`d#!gM! z|L1Rf@ZY&7Qhr!u>b)F{IuG|Fr`TuLEpU?jP{}1d6 z82|5&PkTUWKx(0442&RC7#ShXVPs}8;gwKiw#c4Z@aNB;2uW2Hu-vm-cNkb$m{;$r za;pF;6NG6-i3PC0-#=%>G%e5m`~N?}-$9Romxr5yhntJR-$9oFC>9Ej9VCF_0we$m zlQa7I8eq=Fi#Jf5jgp4&WF})5V4`t>BphR$iI6~cHpZnHW&l8d0RXn(^L@L_?79E| N002ovPDHLkV1h8O2$28) literal 0 HcmV?d00001 diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/transformrotate16.png b/src/com/jpexs/decompiler/flash/gui/graphics/transformrotate16.png new file mode 100644 index 0000000000000000000000000000000000000000..9a470f76dd56029b728da098761aeeefa76587e2 GIT binary patch literal 802 zcmV+-1Ks?IP)$w}>7(><4H6dw;)k{=ajs;A$IzTp%I?h4la_qZ#L#u0uMbA?H?$ zBuJuYa?A{M{hY399}0_$AfsxilNiuX*AY@{Dt0y>h7Q8=Y6dk;|4RTdZnN8O7M7Rp zu;=AL)3JS26-h*gwR8sW``pm=d=@&N&Opya4=krhMAuD$R*>X;?r(PTZEPWtfO`+e z;Bm9dxsp<`{el|?Uw#IgEP~A03h~%y$Q+00|^|>*hyK!~l>-?cm_+7+b=dASh zD&2lJJbLj3B=$oR)|=(3Lhjz&fgs2~UXKTe{H*@c(ECxJD@$&G;aA^4mPD{*vB4rk zZP_+B9vFZ~$n8|E5(qQ}44<#wTpt+z$@fa8(%93|0FlWAX#)smRRJoC^5Ar60G_mY zoHcDDF@DBy5IIJ^!wNav)#8EZ$L~gm0{o7Oom=2!uph>+G?--8mg|E=rpjzs2a%Pj zMWbDlOo_A54krSAFmd&WlRA#^zR-|dyelG;dA1$RZg?}Z1Y|Z?$uQa%u+kp~lY{Hz z>QGpreWBgZp`?8GUCLFF$IZ4d54UYr8A|du!l}#s2KiuXSZQk94}#SS#m+qkl1oWgOw1>uH_rH( z@7U|@#i=F4iK!Gu3DZf$N^c)#4L07X3J&4XI||la8=bB=d;2<_l^fp?nP`f9T1Eq$ z8)SOM$bN8+noyX}O9@-~j3HzNV9;OdsPYXcob!_KbaI2x$PW(M7l-FoG^VD~ps6bS gD;!z;cl;7y0LB$}9hrrEp8x;=07*qoM6N<$f~<6RdH?_b literal 0 HcmV?d00001 diff --git a/src/com/jpexs/decompiler/flash/gui/graphics/transformscale16.png b/src/com/jpexs/decompiler/flash/gui/graphics/transformscale16.png new file mode 100644 index 0000000000000000000000000000000000000000..cc12f14b97805c710da5caa9de0ca7104e70daee GIT binary patch literal 671 zcmV;Q0$}}#P)zK zMN6f%`Is5cn`EYK(BKW1m-ptL`_8-PUgg-}G^DwnH(sf_IoI>?Kc=Y)V!HN3$c=LYgACdQ^dAe}Jr@Z~2wEq%cRef-sDr1>pj#v+XBF^li}4Hm$~z8)Bwf(xg6 z5Z6^)?mq}_(V5LakXh0cNEjL#5U6`H9jGTEMJ6zX27|!3E|9Ve+`9J$sje>kTv&k1 zAd8Q;4Skb=%WE@CVlp$aNLw+a;|3hkzj5a^oNN}0-{w&~dl+lwD&j(grEBmS2?PtV zv|e$Y9qny6H#!TcqAZzYp^);Axp|BX97TnN8$Ev<%WJ=pG(=rGnP8*pI`;<-{8g^} z?Y9^%bYp2{9Z53F-<-hXODCn|Q4K`cIM2T>2F(#l+PiNT$_$j&MzEkn)7SGn^8$up zyG1hY=9{;A*f3Hd`ID$4*B%Ay)?SSosl$$KYnV+_5Ta%VPelxrW(JOJpq*5u%l|=R zu>mLu8;9CkDsshV!D@4F8ufV);k#{0(`**2-9P*y8lFz-f2KGY)F{AJew73^R!e;& z?+!Ijp$t&Ut2493-YYHUYDHeZm!A4Zz%qTV)nxt=U;wR<&vWN3o*@7L002ovPDHLk FV1lVTE>6_J^T#`OGbBFtLzH`3&UGDg`*BBj{2FIZ@p=u#bH?Ugpv_WM29VYN~ z9Q~(*>2#oAvlqj|6QNs8`w>q7E7n&K09WpZ5A_d@H;PQ?um~t)XDq3Pc#)wuJc&h) z;ahY*l`$QK3Ltu2#)7f@BY4g*6G-01m;vu!oVHS8+Q zrz;ie%^S1`vMIxQMMh;wE}nfD!OJEQG4*7c7F7 zu0iZM{}8=jreF~BY)r?IngALu-NPC;6*fy+0;!Vi0uBU=pu=clX@ZTuJe>0Tz|4-J z5{Dym_!?bA{l0S4H$MPN>KP3t2$VZM|I{Lwiu}O!H{T)Ab&*y0w+Eo)*l_pJTWs5s zi}qs`<*Rh~FRt!#pK<$iRmN<^xh@3v?nY1d6TEA#MPutrx&@IN8~F*I=QEV>0{?I0 zH&hp*@A320VKdC8(KQ_fo4n9i}0000