Library preview

This commit is contained in:
Jindra Petřík
2024-10-05 10:45:55 +02:00
parent d08ce4d7a0
commit 86852fd898
9 changed files with 455 additions and 314 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -303,6 +303,12 @@ public final class ImagePanel extends JPanel implements MediaDisplay {
private List<java.awt.Point> showPoints2 = new ArrayList<>();
private int displayedFrame = 0;
private JPanel topPanel;
public void setTopPanelVisible(boolean visible) {
topPanel.setVisible(visible);
}
public void setShowPoints(List<java.awt.Point> showPoints1, List<java.awt.Point> 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;
}
}
}
}

View File

@@ -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<Integer> needed = new LinkedHashSet<>();
imageTag.getNeededCharacters(needed, swf);
List<CharacterTag> 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<SHAPERECORD> 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<BoundedTag> 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) {

View File

@@ -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);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Integer> needed = new LinkedHashSet<>();
imageTag.getNeededCharacters(needed, swf);
List<CharacterTag> 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<SHAPERECORD> 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<BoundedTag> 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();
}
};
}
}

View File

@@ -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();