diff --git a/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java b/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java index 19014492e..f4b4e7144 100644 --- a/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java +++ b/src/com/jpexs/decompiler/flash/easygui/EasyGuiMain.java @@ -17,6 +17,7 @@ package com.jpexs.decompiler.flash.easygui; import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; import java.awt.GraphicsEnvironment; import java.io.File; @@ -43,7 +44,7 @@ public class EasyGuiMain { System.setProperty("sun.java2d.d3d", "false"); System.setProperty("sun.java2d.noddraw", "true"); - System.setProperty("sun.java2d.uiScale", "1.0");; + System.setProperty("sun.java2d.uiScale", "1.0"); System.setProperty("sun.java2d.opengl", "false"); @@ -57,6 +58,9 @@ public class EasyGuiMain { UIManager.put("Tree.expandedIcon", View.getIcon("expand16")); UIManager.put("Tree.collapsedIcon", View.getIcon("collapse16")); + AppStrings.setResourceClass(com.jpexs.decompiler.flash.gui.MainFrame.class); + + mainFrame = new MainFrame(); mainFrame.setVisible(true); diff --git a/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java b/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java index 942c3e54a..5f23b720c 100644 --- a/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java +++ b/src/com/jpexs/decompiler/flash/easygui/LibraryTreeTable.java @@ -231,7 +231,7 @@ public class LibraryTreeTable extends JTreeTable { Object o = n.getUserObject(); switch (column) { case 0: - return node.toString(); + return node; case 1: if (o instanceof CharacterTag) { CharacterTag ct = (CharacterTag) o; diff --git a/src/com/jpexs/decompiler/flash/easygui/MainFrame.java b/src/com/jpexs/decompiler/flash/easygui/MainFrame.java index 5a1da132e..3c4efa4c6 100644 --- a/src/com/jpexs/decompiler/flash/easygui/MainFrame.java +++ b/src/com/jpexs/decompiler/flash/easygui/MainFrame.java @@ -17,6 +17,10 @@ package com.jpexs.decompiler.flash.easygui; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.gui.ImagePanel; +import com.jpexs.decompiler.flash.gui.TimelinedMaker; +import com.jpexs.decompiler.flash.gui.player.Zoom; +import com.jpexs.decompiler.flash.tags.Tag; import de.javagl.treetable.JTreeTable; import java.awt.BorderLayout; import java.awt.Color; @@ -25,11 +29,17 @@ import java.awt.Dimension; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import javax.swing.BorderFactory; import javax.swing.JFrame; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; /** * @@ -40,6 +50,7 @@ public class MainFrame extends JFrame { private SWF swf; private LibraryTreeTable libraryTreeTable; private JSplitPane splitPane; + private ImagePanel libraryPreviewPanel; public MainFrame() { setTitle("JPEXS FFDec Easy GUI"); setSize(1024, 768); @@ -49,10 +60,44 @@ public class MainFrame extends JFrame { cnt.setLayout(new BorderLayout()); libraryTreeTable = new LibraryTreeTable(); - JScrollPane scrollPane = new JScrollPane(libraryTreeTable); - splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), scrollPane); - scrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); - cnt.add(splitPane, BorderLayout.CENTER); + JScrollPane libraryScrollPane = new JScrollPane(libraryTreeTable); + + JPanel libraryPanel = new JPanel(new BorderLayout()); + libraryPanel.add(libraryScrollPane, BorderLayout.CENTER); + + libraryPreviewPanel = new ImagePanel(); + libraryPreviewPanel.setTopPanelVisible(false); + + JPanel topLibraryPanel = new JPanel(new BorderLayout()); + JLabel libraryLabel = new JLabel("Library"); + libraryLabel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + topLibraryPanel.add(libraryLabel, BorderLayout.NORTH); + topLibraryPanel.add(libraryPreviewPanel, BorderLayout.CENTER); + libraryPanel.add(topLibraryPanel, BorderLayout.NORTH); + + libraryPreviewPanel.setPreferredSize(new Dimension(200,200)); + + splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), libraryPanel); + libraryScrollPane.getViewport().setBackground(UIManager.getColor("Tree.background")); + cnt.add(splitPane, BorderLayout.CENTER); + + libraryTreeTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + int row = libraryTreeTable.getSelectedRow(); + if (row == -1) { + return; + } + DefaultMutableTreeNode n = (DefaultMutableTreeNode) libraryTreeTable.getModel().getValueAt(row, 0); + Object obj = n.getUserObject(); + if (obj instanceof Tag) { + Tag t = (Tag) obj; + libraryPreviewPanel.setTimelined(TimelinedMaker.makeTimelined(t), t.getSwf(), + -1, false, true, true, true, true, false, true); + libraryPreviewPanel.zoomFit(); + } + } + }); } public void open(File file) throws IOException, InterruptedException { diff --git a/src/com/jpexs/decompiler/flash/gui/AppStrings.java b/src/com/jpexs/decompiler/flash/gui/AppStrings.java index a91cd2221..473468104 100644 --- a/src/com/jpexs/decompiler/flash/gui/AppStrings.java +++ b/src/com/jpexs/decompiler/flash/gui/AppStrings.java @@ -41,7 +41,7 @@ public class AppStrings { return name; } - public static String translate(String key) { + public static String translate(String key) { return resourceBundle.getString(key); } diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index c4ef8a17f..298e4976a 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -303,6 +303,12 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private List showPoints2 = new ArrayList<>(); private int displayedFrame = 0; + + private JPanel topPanel; + + public void setTopPanelVisible(boolean visible) { + topPanel.setVisible(visible); + } public void setShowPoints(List showPoints1, List showPoints2) { this.showPoints1 = showPoints1; @@ -2221,7 +2227,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { //labelPan.add(label, new GridBagConstraints()); add(iconPanel, BorderLayout.CENTER); - JPanel topPanel = new JPanel(); + topPanel = new JPanel(); topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS)); debugLabel.setAlignmentX(JLabel.LEFT_ALIGNMENT); topPanel.add(debugLabel); @@ -2549,17 +2555,24 @@ public final class ImagePanel extends JPanel implements MediaDisplay { @Override public synchronized void zoom(Zoom zoom) { - zoom(zoom, false); + zoom(zoom, false, false); + } + + public void zoomFit() { + Zoom z = new Zoom(); + z.value = 1.0; + z.fit = true; + zoom(z, false, true); } - private synchronized void zoom(Zoom zoom, boolean useCursor) { + private synchronized void zoom(Zoom zoom, boolean useCursor, boolean forced) { if (!zoomAvailable) { return; } double zoomDoubleBefore = this.zoom.fit ? getZoomToFit() : this.zoom.value; boolean modified = this.zoom.value != zoom.value || this.zoom.fit != zoom.fit; - if (modified) { + if (modified || forced) { Point localCursorPosition = this.cursorPosition; if (!useCursor || localCursorPosition == null) { localCursorPosition = new Point(iconPanel.getWidth() / 2, iconPanel.getHeight() / 2); @@ -2613,7 +2626,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { double h1 = bounds.getHeight() / SWF.unitDivisor; double w2 = iconPanel.getWidth(); - double h2 = iconPanel.getHeight(); + double h2 = iconPanel.getHeight(); double w; double h; @@ -3894,14 +3907,14 @@ public final class ImagePanel extends JPanel implements MediaDisplay { Zoom newZoom = new Zoom(); newZoom.value = currentRealZoom * ZOOM_MULTIPLIER; newZoom.fit = false; - zoom(newZoom, true); + zoom(newZoom, true, false); } private synchronized void zoomOut() { Zoom newZoom = new Zoom(); newZoom.value = getRealZoom() / ZOOM_MULTIPLIER; newZoom.fit = false; - zoom(newZoom, true); + zoom(newZoom, true, false); } @Override @@ -4039,5 +4052,5 @@ public final class ImagePanel extends JPanel implements MediaDisplay { this.pathPosition = pathPosition; this.closestPoint = closestPoint; } - } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index d93586f83..a0b1fbaee 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -5851,7 +5851,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se } else if (treeItem instanceof ImageTag) { ImageTag imageTag = (ImageTag) treeItem; previewPanel.setImageReplaceButtonVisible(!((Tag) imageTag).isReadOnly() && imageTag.importSupported(), imageTag instanceof DefineBitsJPEG3Tag || imageTag instanceof DefineBitsJPEG4Tag, false, false, false, false, false); - SWF imageSWF = makeTimelinedImage(imageTag); + SWF imageSWF = TimelinedMaker.makeTimelinedImage(imageTag); previewPanel.showImagePanel(imageSWF, imageSWF, 0, false, true, true, true, true, false, false, true); } else if (!isVideoButNotDrawable && (treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { @@ -5861,7 +5861,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se if (treeItem instanceof Timelined && !(treeItem instanceof ButtonTag)) { timelined = (Timelined) tag; } else { - timelined = makeTimelined(tag); + timelined = TimelinedMaker.makeTimelined(tag); } previewPanel.setParametersPanelVisible(false); @@ -6462,293 +6462,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se public void setErrorState(ErrorState errorState) { statusPanel.setErrorState(errorState); - } - - public static SWF makeTimelinedImage(ImageTag imageTag) { - SWF swf = new SWF(); - swf.gfx = imageTag.getSwf().gfx; - swf.version = imageTag.getSwf().version; - int w = (int) (imageTag.getImageDimension().getWidth() * SWF.unitDivisor); - int h = (int) (imageTag.getImageDimension().getHeight() * SWF.unitDivisor); - swf.displayRect = new RECT(0, w, 0, h); - swf.frameCount = 1; - swf.frameRate = 1; - swf.setFile(imageTag.getSwf().getFile()); //DefineSubImage calculates relative paths from it - try { - - JPEGTablesTag jpegTablesTag = null; - if (imageTag instanceof DefineBitsTag) { - jpegTablesTag = imageTag.getSwf().getJtt(); - } - Set needed = new LinkedHashSet<>(); - imageTag.getNeededCharacters(needed, swf); - - List neededCopies = new ArrayList<>(); - for (int n : needed) { - CharacterTag ct = imageTag.getSwf().getCharacter(n); - if (ct != null) { - ct = (CharacterTag) ct.cloneTag(); - ct.setSwf(swf); - neededCopies.add(ct); - } - } - if (imageTag instanceof DefineSubImage) { - DefineExternalImage2 dei2 = (DefineExternalImage2) imageTag.getSwf().getExternalImage2(((DefineSubImage) imageTag).imageId); - if (dei2 != null) { - dei2 = (DefineExternalImage2) dei2.cloneTag(); - dei2.setSwf(swf); - neededCopies.add(dei2); - } - } - - ImageTag imageTagCopy = (ImageTag) imageTag.cloneTag(); - imageTagCopy.setSwf(swf); - int imageCharId = imageTag.getCharacterId(); - if ((imageTag instanceof DefineExternalImage2) && (((DefineExternalImage2) imageTag).idType != IdType.IDTYPE_NONE)) { - imageCharId = swf.getNextCharacterId(); - imageTagCopy.characterID = imageCharId; - } - DefineShape2Tag shapeTag = new DefineShape2Tag(swf); - int shapeCharId = imageCharId + 1; - shapeTag.shapeId = shapeCharId; - shapeTag.shapeBounds = new RECT(swf.displayRect); - - SHAPEWITHSTYLE shapeData = new SHAPEWITHSTYLE(); - FILLSTYLEARRAY fillStyleArray = new FILLSTYLEARRAY(); - FILLSTYLE[] fillStyles = new FILLSTYLE[1]; - FILLSTYLE fillStyle = new FILLSTYLE(); - fillStyle.bitmapId = imageCharId; - fillStyle.inShape3 = false; - fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP; - fillStyle.bitmapMatrix = Matrix.getScaleInstance(SWF.unitDivisor).toMATRIX(); - fillStyles[0] = fillStyle; - fillStyleArray.fillStyles = fillStyles; - shapeData.fillStyles = fillStyleArray; - shapeData.lineStyles = new LINESTYLEARRAY(); - - List shapeRecords = new ArrayList<>(); - - StyleChangeRecord scr = new StyleChangeRecord(); - scr.stateFillStyle0 = true; - scr.fillStyle0 = 1; - shapeRecords.add(scr); - - StyleChangeRecord scr2 = new StyleChangeRecord(); - scr2.stateMoveTo = true; - scr2.moveDeltaX = 0; - scr2.moveDeltaY = 0; - scr2.calculateBits(); - shapeRecords.add(scr2); - - StraightEdgeRecord ser1 = new StraightEdgeRecord(); - ser1.vertLineFlag = true; - ser1.deltaY = h; - ser1.calculateBits(); - shapeRecords.add(ser1); - - StraightEdgeRecord ser2 = new StraightEdgeRecord(); - ser2.deltaX = w; - shapeRecords.add(ser2); - - StraightEdgeRecord ser3 = new StraightEdgeRecord(); - ser3.vertLineFlag = true; - ser3.deltaY = -h; - shapeRecords.add(ser3); - - StraightEdgeRecord ser4 = new StraightEdgeRecord(); - ser4.deltaX = -w; - shapeRecords.add(ser4); - - shapeRecords.add(new EndShapeRecord()); - - shapeData.shapeRecords = shapeRecords; - - shapeData.numFillBits = 1; - shapeData.numLineBits = 0; - - shapeTag.shapes = shapeData; - - PlaceObjectTag placeTag = new PlaceObjectTag(swf, shapeCharId, 1, new Matrix().toMATRIX(), null); - - ShowFrameTag showFrameTag = new ShowFrameTag(swf); - - EndTag endTag = new EndTag(swf); - - if (jpegTablesTag != null) { - swf.addTag(jpegTablesTag); - } - for (CharacterTag neededCopy : neededCopies) { - swf.addTag(neededCopy); - } - swf.addTag(imageTagCopy); - swf.addTag(shapeTag); - swf.addTag(placeTag); - swf.addTag(showFrameTag); - swf.addTag(endTag); - - } catch (InterruptedException | IOException ex) { - //ignore - } - return swf; - } - - public static Timelined makeTimelined(final Tag tag) { - return makeTimelined(tag, -1); - } - - public static Timelined makeTimelined(final Tag tag, final int fontFrameNum) { - - return new Timelined() { - private Timeline tim; - - @Override - public Timeline getTimeline() { - if (tim == null) { - Timeline timeline = new Timeline(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); - initTimeline(timeline); - tim = timeline; - } - - return tim; - } - - @Override - public void resetTimeline() { - if (tim != null) { - tim.reset(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); - initTimeline(tim); - } - } - - private void initTimeline(Timeline timeline) { - if (tag instanceof MorphShapeTag) { - timeline.frameRate = PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE; - int framesCnt = (int) (timeline.frameRate * PreviewExporter.MORPH_SHAPE_ANIMATION_LENGTH); - for (int i = 0; i < framesCnt; i++) { - Frame f = new Frame(timeline, i); - DepthState ds = new DepthState(tag.getSwf(), f, f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - ds.ratio = i * 65535 / framesCnt; - f.layers.put(1, ds); - f.layersChanged = true; - timeline.addFrame(f); - } - Frame f = new Frame(timeline, framesCnt); - DepthState ds = new DepthState(tag.getSwf(), f, f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - ds.ratio = 65535; - f.layers.put(1, ds); - f.layersChanged = true; - timeline.addFrame(f); - } else if (tag instanceof FontTag) { - int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); - int frame = fontFrameNum; - if (frame < 0 || frame >= pageCount) { - frame = 0; - } - //TODO: make this static texts instead of FontTag as drawable. - //We do not want to draw fonts directly added to stage as - //Fonts are really added to stage in some corner cases like for vertical text. - Frame f = new Frame(timeline, 0); - DepthState ds = new DepthState(tag.getSwf(), f, f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - f.layersChanged = true; - timeline.addFrame(f); - timeline.fontFrameNum = frame; - } else { - Frame f = new Frame(timeline, 0); - DepthState ds = new DepthState(tag.getSwf(), f, f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - timeline.addFrame(f); - } - timeline.displayRect = getRect(); - } - - @Override - public RECT getRect() { - return getRect(new HashSet<>()); - } - - @Override - public RECT getRect(Set added) { - BoundedTag bt = (BoundedTag) tag; - if (!added.contains(bt)) { - return bt.getRect(added); - } - return new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - } - - @Override - public int hashCode() { - return tag.hashCode(); - } - - @Override - public void setModified(boolean value) { - } - - @Override - public ReadOnlyTagList getTags() { - return ReadOnlyTagList.EMPTY; - } - - @Override - public void removeTag(int index) { - } - - @Override - public void removeTag(Tag tag) { - } - - @Override - public void addTag(Tag tag) { - } - - @Override - public void addTag(int index, Tag tag) { - } - - @Override - public void replaceTag(int index, Tag newTag) { - } - - @Override - public void replaceTag(Tag oldTag, Tag newTag) { - } - - @Override - public int indexOfTag(Tag tag) { - return -1; - } - - @Override - public RECT getRectWithStrokes() { - return getRect(); - } - - @Override - public void setFrameCount(int frameCount) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public int getFrameCount() { - return getTimeline().getFrameCount(); - } - - @Override - public SWF getSwf() { - return tag.getSwf(); - } - }; - } - + } + private void disposeInner(Container container) { for (Component c : container.getComponents()) { if (c instanceof Container) { diff --git a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java index 394c75628..3c01f07d6 100644 --- a/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/PreviewPanel.java @@ -1564,7 +1564,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private void showFontPage(FontTag fontTag) { if (!MainPanel.isAdobeFlashPlayerEnabled() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum, true, true, true, true, true, false, false, false); + showImagePanel(TimelinedMaker.makeTimelined(fontTag), fontTag.getSwf(), fontPageNum, true, true, true, true, true, false, false, false); } } @@ -1583,7 +1583,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel public void showTextPanel(TextTag textTag) { if (!MainPanel.isAdobeFlashPlayerEnabled() /*|| ft instanceof GFxDefineCompactedFont*/) { - showImagePanel(MainPanel.makeTimelined(textTag), textTag.getSwf(), 0, true, true, true, true, true, false, false, true); + showImagePanel(TimelinedMaker.makeTimelined(textTag), textTag.getSwf(), 0, true, true, true, true, true, false, false, true); } showCardRight(CARDTEXTPANEL); @@ -1813,11 +1813,11 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel displayEditImagePanel.selectDepth(-1); if (tag instanceof ShapeTag) { - Timelined tim = MainPanel.makeTimelined(tag); + Timelined tim = TimelinedMaker.makeTimelined(tag); displayEditImagePanel.setTimelined(tim, ((Tag) tag).getSwf(), 0, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), false, true); } if (tag instanceof MorphShapeTag) { - Timelined tim = MainPanel.makeTimelined(tag); + Timelined tim = TimelinedMaker.makeTimelined(tag); displayEditImagePanel.setTimelined(tim, ((Tag) tag).getSwf(), -1, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), false, true); morphDisplayMode = MORPH_ANIMATE; displayEditShowAnimationButton.setSelected(true); @@ -2502,19 +2502,19 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel private void showAnimationDisplayEditTagButtonActionPerformed(ActionEvent evt) { morphDisplayMode = MORPH_ANIMATE; - Timelined tim = MainPanel.makeTimelined(displayEditTag); + Timelined tim = TimelinedMaker.makeTimelined(displayEditTag); displayEditImagePanel.setTimelined(tim, displayEditTag.getSwf(), -1, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), false, true); } private void showStartDisplayEditTagButtonActionPerformed(ActionEvent evt) { morphDisplayMode = MORPH_START; - Timelined tim = MainPanel.makeTimelined(displayEditTag); + Timelined tim = TimelinedMaker.makeTimelined(displayEditTag); displayEditImagePanel.setTimelined(tim, displayEditTag.getSwf(), 0, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), false, true); } private void showEndDisplayEditTagButtonActionPerformed(ActionEvent evt) { morphDisplayMode = MORPH_END; - Timelined tim = MainPanel.makeTimelined(displayEditTag); + Timelined tim = TimelinedMaker.makeTimelined(displayEditTag); displayEditImagePanel.setTimelined(tim, displayEditTag.getSwf(), tim.getFrameCount() - 1, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), false, true); } @@ -2964,7 +2964,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel int pageCount = getFontPageCount(fontTag); fontPageNum = (fontPageNum + pageCount - 1) % pageCount; if (!MainPanel.isAdobeFlashPlayerEnabled() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0, true, true, true, true, true, false, false); + imagePanel.setTimelined(TimelinedMaker.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0, true, true, true, true, true, false, false); } } @@ -2973,7 +2973,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel int pageCount = getFontPageCount(fontTag); fontPageNum = (fontPageNum + 1) % pageCount; if (!MainPanel.isAdobeFlashPlayerEnabled() /*|| ft instanceof GFxDefineCompactedFont*/) { - imagePanel.setTimelined(MainPanel.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0, true, true, true, true, true, false, false); + imagePanel.setTimelined(TimelinedMaker.makeTimelined(fontTag, fontPageNum), fontTag.getSwf(), 0, true, true, true, true, true, false, false); } } diff --git a/src/com/jpexs/decompiler/flash/gui/TimelinedMaker.java b/src/com/jpexs/decompiler/flash/gui/TimelinedMaker.java new file mode 100644 index 000000000..e571c0465 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/TimelinedMaker.java @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2024 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.ReadOnlyTagList; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.exporters.PreviewExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.PlaceObjectTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineExternalImage2; +import com.jpexs.decompiler.flash.tags.gfx.DefineSubImage; +import com.jpexs.decompiler.flash.tags.gfx.enums.IdType; +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.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * + * @author JPEXS + */ +public class TimelinedMaker { + + public static SWF makeTimelinedImage(ImageTag imageTag) { + SWF swf = new SWF(); + swf.gfx = imageTag.getSwf().gfx; + swf.version = imageTag.getSwf().version; + int w = (int) (imageTag.getImageDimension().getWidth() * SWF.unitDivisor); + int h = (int) (imageTag.getImageDimension().getHeight() * SWF.unitDivisor); + swf.displayRect = new RECT(0, w, 0, h); + swf.frameCount = 1; + swf.frameRate = 1; + swf.setFile(imageTag.getSwf().getFile()); //DefineSubImage calculates relative paths from it + try { + + JPEGTablesTag jpegTablesTag = null; + if (imageTag instanceof DefineBitsTag) { + jpegTablesTag = imageTag.getSwf().getJtt(); + } + Set needed = new LinkedHashSet<>(); + imageTag.getNeededCharacters(needed, swf); + + List neededCopies = new ArrayList<>(); + for (int n : needed) { + CharacterTag ct = imageTag.getSwf().getCharacter(n); + if (ct != null) { + ct = (CharacterTag) ct.cloneTag(); + ct.setSwf(swf); + neededCopies.add(ct); + } + } + if (imageTag instanceof DefineSubImage) { + DefineExternalImage2 dei2 = (DefineExternalImage2) imageTag.getSwf().getExternalImage2(((DefineSubImage) imageTag).imageId); + if (dei2 != null) { + dei2 = (DefineExternalImage2) dei2.cloneTag(); + dei2.setSwf(swf); + neededCopies.add(dei2); + } + } + + ImageTag imageTagCopy = (ImageTag) imageTag.cloneTag(); + imageTagCopy.setSwf(swf); + int imageCharId = imageTag.getCharacterId(); + if ((imageTag instanceof DefineExternalImage2) && (((DefineExternalImage2) imageTag).idType != IdType.IDTYPE_NONE)) { + imageCharId = swf.getNextCharacterId(); + imageTagCopy.characterID = imageCharId; + } + DefineShape2Tag shapeTag = new DefineShape2Tag(swf); + int shapeCharId = imageCharId + 1; + shapeTag.shapeId = shapeCharId; + shapeTag.shapeBounds = new RECT(swf.displayRect); + + SHAPEWITHSTYLE shapeData = new SHAPEWITHSTYLE(); + FILLSTYLEARRAY fillStyleArray = new FILLSTYLEARRAY(); + FILLSTYLE[] fillStyles = new FILLSTYLE[1]; + FILLSTYLE fillStyle = new FILLSTYLE(); + fillStyle.bitmapId = imageCharId; + fillStyle.inShape3 = false; + fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP; + fillStyle.bitmapMatrix = Matrix.getScaleInstance(SWF.unitDivisor).toMATRIX(); + fillStyles[0] = fillStyle; + fillStyleArray.fillStyles = fillStyles; + shapeData.fillStyles = fillStyleArray; + shapeData.lineStyles = new LINESTYLEARRAY(); + + List shapeRecords = new ArrayList<>(); + + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateFillStyle0 = true; + scr.fillStyle0 = 1; + shapeRecords.add(scr); + + StyleChangeRecord scr2 = new StyleChangeRecord(); + scr2.stateMoveTo = true; + scr2.moveDeltaX = 0; + scr2.moveDeltaY = 0; + scr2.calculateBits(); + shapeRecords.add(scr2); + + StraightEdgeRecord ser1 = new StraightEdgeRecord(); + ser1.vertLineFlag = true; + ser1.deltaY = h; + ser1.calculateBits(); + shapeRecords.add(ser1); + + StraightEdgeRecord ser2 = new StraightEdgeRecord(); + ser2.deltaX = w; + shapeRecords.add(ser2); + + StraightEdgeRecord ser3 = new StraightEdgeRecord(); + ser3.vertLineFlag = true; + ser3.deltaY = -h; + shapeRecords.add(ser3); + + StraightEdgeRecord ser4 = new StraightEdgeRecord(); + ser4.deltaX = -w; + shapeRecords.add(ser4); + + shapeRecords.add(new EndShapeRecord()); + + shapeData.shapeRecords = shapeRecords; + + shapeData.numFillBits = 1; + shapeData.numLineBits = 0; + + shapeTag.shapes = shapeData; + + PlaceObjectTag placeTag = new PlaceObjectTag(swf, shapeCharId, 1, new Matrix().toMATRIX(), null); + + ShowFrameTag showFrameTag = new ShowFrameTag(swf); + + EndTag endTag = new EndTag(swf); + + if (jpegTablesTag != null) { + swf.addTag(jpegTablesTag); + } + for (CharacterTag neededCopy : neededCopies) { + swf.addTag(neededCopy); + } + swf.addTag(imageTagCopy); + swf.addTag(shapeTag); + swf.addTag(placeTag); + swf.addTag(showFrameTag); + swf.addTag(endTag); + + } catch (InterruptedException | IOException ex) { + //ignore + } + return swf; + } + + public static Timelined makeTimelined(final Tag tag) { + if (tag instanceof ImageTag) { + return makeTimelinedImage((ImageTag) tag); + } + return makeTimelined(tag, -1); + } + + public static Timelined makeTimelined(final Tag tag, final int fontFrameNum) { + int chId = ((CharacterIdTag) tag).getCharacterId(); + if (chId == -1) { + chId = 0; + } + final int fChId = chId; + return new Timelined() { + private Timeline tim; + + @Override + public Timeline getTimeline() { + if (tim == null) { + Timeline timeline = new Timeline(tag.getSwf(), this, fChId, getRect()); + initTimeline(timeline); + tim = timeline; + } + + return tim; + } + + @Override + public void resetTimeline() { + if (tim != null) { + tim.reset(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); + initTimeline(tim); + } + } + + private void initTimeline(Timeline timeline) { + if (tag instanceof MorphShapeTag) { + timeline.frameRate = PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE; + int framesCnt = (int) (timeline.frameRate * PreviewExporter.MORPH_SHAPE_ANIMATION_LENGTH); + for (int i = 0; i < framesCnt; i++) { + Frame f = new Frame(timeline, i); + DepthState ds = new DepthState(tag.getSwf(), f, f); + ds.characterId = fChId; + ds.matrix = new MATRIX(); + ds.ratio = i * 65535 / framesCnt; + f.layers.put(1, ds); + f.layersChanged = true; + timeline.addFrame(f); + } + Frame f = new Frame(timeline, framesCnt); + DepthState ds = new DepthState(tag.getSwf(), f, f); + ds.characterId = fChId; + ds.matrix = new MATRIX(); + ds.ratio = 65535; + f.layers.put(1, ds); + f.layersChanged = true; + timeline.addFrame(f); + } else if (tag instanceof FontTag) { + int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); + int frame = fontFrameNum; + if (frame < 0 || frame >= pageCount) { + frame = 0; + } + //TODO: make this static texts instead of FontTag as drawable. + //We do not want to draw fonts directly added to stage as + //Fonts are really added to stage in some corner cases like for vertical text. + Frame f = new Frame(timeline, 0); + DepthState ds = new DepthState(tag.getSwf(), f, f); + ds.characterId = fChId; + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + f.layersChanged = true; + timeline.addFrame(f); + timeline.fontFrameNum = frame; + } else if (tag instanceof SoundTag) { + + } else { + Frame f = new Frame(timeline, 0); + DepthState ds = new DepthState(tag.getSwf(), f, f); + ds.characterId = fChId; + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + timeline.addFrame(f); + } + timeline.displayRect = getRect(); + } + + @Override + public RECT getRect() { + return getRect(new HashSet<>()); + } + + @Override + public RECT getRect(Set added) { + if (!(tag instanceof BoundedTag)) { + return new RECT(0,1,0,1); + } + BoundedTag bt = (BoundedTag) tag; + if (!added.contains(bt)) { + return bt.getRect(added); + } + return new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + + @Override + public void setModified(boolean value) { + } + + @Override + public ReadOnlyTagList getTags() { + return ReadOnlyTagList.EMPTY; + } + + @Override + public void removeTag(int index) { + } + + @Override + public void removeTag(Tag tag) { + } + + @Override + public void addTag(Tag tag) { + } + + @Override + public void addTag(int index, Tag tag) { + } + + @Override + public void replaceTag(int index, Tag newTag) { + } + + @Override + public void replaceTag(Tag oldTag, Tag newTag) { + } + + @Override + public int indexOfTag(Tag tag) { + return -1; + } + + @Override + public RECT getRectWithStrokes() { + return getRect(); + } + + @Override + public void setFrameCount(int frameCount) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getFrameCount() { + return getTimeline().getFrameCount(); + } + + @Override + public SWF getSwf() { + return tag.getSwf(); + } + }; + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java b/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java index daecd3582..76f5574ac 100644 --- a/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/action/AddScriptDialog.java @@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.gui.FasterScrollPane; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.MainPanel; import com.jpexs.decompiler.flash.gui.PreviewPanel; +import com.jpexs.decompiler.flash.gui.TimelinedMaker; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.tags.DefineButton2Tag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag; @@ -784,7 +785,7 @@ public class AddScriptDialog extends AppDialog { private void buttonValueChanged(ListSelectionEvent e) { buttonPreviewPanel.showEmpty(); if (buttonList.getSelectedIndex() >= 0) { - buttonPreviewPanel.showImagePanel(MainPanel.makeTimelined(buttonList.getSelectedValue()), swf, -1, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), true, false, true); + buttonPreviewPanel.showImagePanel(TimelinedMaker.makeTimelined(buttonList.getSelectedValue()), swf, -1, true, Configuration.autoPlayPreviews.get(), !Configuration.animateSubsprites.get(), false, !Configuration.playFrameSounds.get(), true, false, true); } checkEnabled();