Added #2079 Bulk import sprites from GIFs

This commit is contained in:
Jindra Petřík
2023-10-03 22:58:40 +02:00
parent 92be6d231f
commit 63ff7c384b
11 changed files with 166 additions and 7 deletions

View File

@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
### Added
- [#2090] Support for Mochicrypt packed binarydata tags - loading SWF as subtree
- [#2079] Replace DefineSprite with GIF
- [#2079] Replace DefineSprite with GIF, Bulk import sprites from GIFs
## [19.0.0] - 2023-10-01
### Added

View File

@@ -924,6 +924,9 @@ public final class Configuration {
@ConfigurationDefaultBoolean(true)
public static ConfigurationItem<Boolean> lastFlaExportCompressed = null;
@ConfigurationDefaultBoolean(true)
@ConfigurationCategory("ui")
public static ConfigurationItem<Boolean> showImportSpriteInfo = null;
private enum OSId {
WINDOWS, OSX, UNIX
}

View File

@@ -22,20 +22,27 @@ import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.DefineShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.RemoveObjectTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.timeline.Timelined;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
/**
@@ -145,4 +152,70 @@ public class SpriteImporter {
return true;
}
public int bulkImport(File spritesDir, SWF swf, boolean printOut) {
Map<Integer, CharacterTag> characters = swf.getCharacters();
int spriteCount = 0;
List<String> extensions = Arrays.asList("gif");
File allFiles[] = spritesDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
String nameLower = name.toLowerCase();
for (String ext : extensions) {
if (nameLower.endsWith("." + ext)) {
return true;
}
}
return false;
}
});
for (int characterId : characters.keySet()) {
CharacterTag tag = characters.get(characterId);
if (tag instanceof DefineSpriteTag) {
DefineSpriteTag spriteTag = (DefineSpriteTag) tag;
List<File> existingFilesForSpriteTag = new ArrayList<>();
for (File f : allFiles) {
if (f.getName().startsWith("" + characterId + ".") || f.getName().startsWith("" + characterId + "_")) {
existingFilesForSpriteTag.add(f);
}
}
existingFilesForSpriteTag.sort(new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
String ext1 = o1.getName().substring(o1.getName().lastIndexOf(".") + 1);
String ext2 = o2.getName().substring(o2.getName().lastIndexOf(".") + 1);
int ret = extensions.indexOf(ext1) - extensions.indexOf(ext2);
if (ret == 0) {
return o1.getName().compareTo(o2.getName());
}
return ret;
}
});
if (existingFilesForSpriteTag.isEmpty()) {
continue;
}
if (existingFilesForSpriteTag.size() > 1) {
Logger.getLogger(SpriteImporter.class.getName()).log(Level.WARNING, "Multiple matching files for sprite tag {0} exists, {1} selected", new Object[]{characterId, existingFilesForSpriteTag.get(0).getName()});
}
File sourceFile = existingFilesForSpriteTag.get(0);
if (printOut) {
System.out.println("Importing character " + characterId + " from file " + sourceFile.getName());
}
try(FileInputStream fis = new FileInputStream(sourceFile.getAbsolutePath())) {
importSprite(spriteTag, fis);
spriteCount++;
} catch (IOException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.WARNING, "Cannot import sprite " + characterId + " from file " + sourceFile.getName(), ex);
}
if (Thread.currentThread().isInterrupted()) {
break;
}
}
}
return spriteCount;
}
}

View File

@@ -380,6 +380,16 @@ public abstract class MainFrameMenu implements MenuBuilder {
}
mainFrame.getPanel().importShape((SWF) openable, true);
}
protected void importSpritesActionPerformed(ActionEvent evt) {
if (Main.isWorking()) {
return;
}
if (mainFrame.getPanel().checkEdited()) {
return;
}
mainFrame.getPanel().importSprite((SWF) openable);
}
protected void importMoviesActionPerformed(ActionEvent evt) {
if (Main.isWorking()) {
@@ -1031,6 +1041,7 @@ public abstract class MainFrameMenu implements MenuBuilder {
setMenuEnabled("/import/importtab/importText", allSameSwf && swfSelected && !isWorking);
setMenuEnabled("/import/importtab/importScript", allSameOpenable && openableSelected && !isWorking);
setMenuEnabled("/import/importtab/importImages", allSameSwf && swfSelected && !isWorking);
setMenuEnabled("/import/importtab/importSprites", allSameSwf && swfSelected && !isWorking);
setMenuEnabled("/import/importtab/importShapes", allSameSwf && swfSelected && !isWorking);
setMenuEnabled("/import/importtab/importShapesNoFill", allSameSwf && swfSelected && !isWorking);
setMenuEnabled("/import/importtab/importMovies", allSameSwf && swfSelected && !isWorking);
@@ -1164,6 +1175,7 @@ public abstract class MainFrameMenu implements MenuBuilder {
addMenuItem("/import/importtab/importImages", translate("menu.file.import.image"), "importimage32", this::importImagesActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
addMenuItem("/import/importtab/importShapes", translate("menu.file.import.shape"), "importshape32", this::importShapesActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
addMenuItem("/import/importtab/importShapesNoFill", translate("menu.file.import.shapeNoFill"), "importshape32", this::importShapesNoFillActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
addMenuItem("/import/importtab/importSprites", translate("menu.file.import.sprite"), "importsprite32", this::importSpritesActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
addMenuItem("/import/importtab/importMovies", translate("menu.file.import.movie"), "importmovie32", this::importMoviesActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
addMenuItem("/import/importtab/importSounds", translate("menu.file.import.sound"), "importsound32", this::importSoundsActionPerformed, PRIORITY_MEDIUM, null, true, null, false);
addMenuItem("/import/importtab/importSymbolClass", translate("menu.file.import.symbolClass"), "importsymbolclass32", this::importSymbolClassActionPerformed, PRIORITY_MEDIUM, null, true, null, false);

View File

@@ -3389,6 +3389,65 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
}.execute();
}
}
public void importSprite(final SWF swf) {
ViewMessages.showMessageDialog(MainPanel.this, translate("message.info.importSprites"), translate("message.info"), JOptionPane.INFORMATION_MESSAGE, Configuration.showImportSpriteInfo);
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get()));
chooser.setDialogTitle(translate("import.select.directory"));
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath();
File spritesDir = new File(Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME));
if (!spritesDir.exists()) {
spritesDir = new File(selFile);
}
final File fSpritesDir = spritesDir;
SpriteImporter spriteImporter = new SpriteImporter();
final long timeBefore = System.currentTimeMillis();
new CancellableWorker<Void>() {
private int count = 0;
@Override
public Void doInBackground() throws Exception {
try {
count = spriteImporter.bulkImport(fSpritesDir, swf, false);
swf.clearImageCache();
swf.clearShapeCache();
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error during import", ex);
ViewMessages.showMessageDialog(null, translate("error.import") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage());
}
return null;
}
@Override
protected void onStart() {
Main.startWork(translate("work.importing") + "...", this);
}
@Override
protected void done() {
Main.stopWork();
long timeAfter = System.currentTimeMillis();
final long timeMs = timeAfter - timeBefore;
View.execInEventDispatch(() -> {
refreshTree(swf);
setStatus(translate("import.finishedin").replace("%time%", Helper.formatTimeSec(timeMs)));
ViewMessages.showMessageDialog(MainPanel.this, translate("import.sprite.result").replace("%count%", Integer.toString(count)));
if (count != 0) {
reload(true);
}
});
}
}.execute();
}
}
public void importShape(final SWF swf, boolean noFill) {
ViewMessages.showMessageDialog(MainPanel.this, translate("message.info.importShapes2"), translate("message.info"), JOptionPane.INFORMATION_MESSAGE, Configuration.showImportShapeInfo);
@@ -4439,7 +4498,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
}
}
public void replaceWithGifButtonActionPerformed(TreeItem item) {
public void replaceSpriteWithGifButtonActionPerformed(TreeItem item) {
if (item == null) {
return;
}

View File

@@ -403,7 +403,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
replaceSpriteButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainPanel.replaceWithGifButtonActionPerformed(mainPanel.getCurrentTree().getCurrentTreeItem());
mainPanel.replaceSpriteWithGifButtonActionPerformed(mainPanel.getCurrentTree().getCurrentTreeItem());
}
});
replaceSpriteButton.setVisible(false);

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -704,3 +704,7 @@ config.description.lastFlaExportVersion = Last exported FLA version
config.name.lastFlaExportCompressed = Last FLA export compressed
config.description.lastFlaExportCompressed = Last exported FLA version compressed
#after 19.0.0
config.name.showImportSpriteInfo = Show information before importing sprites
config.description.showImportSpriteInfo = Displays some info about how importing sprites works after clicking Import sprites in the menu.

View File

@@ -1162,4 +1162,12 @@ button.abcexploretrait = Show trait in ABC Explorer
#after 19.0.0
binarydata.swfInside.packer = It looks like there is SWF inside this binary data tag packed with %packer%. Click here to unpack the SWF and load it as subtree.
button.replaceWithGif = Replace with GIF...
button.replaceWithGif = Replace with GIF...
message.info.importSprites = During importing sprites, you need to select a FOLDER.\r\n \
The folder must contain "sprites" subfolder and filenames inside must match existing sprites in current selected SWF.\r\n \
Each file must have ".gif" extension.
import.sprite.result = %count% sprites imported.
menu.file.import.sprite = Import sprites from GIF

View File

@@ -363,7 +363,7 @@ public class TagTreeContextMenu extends JPopupMenu {
replaceWithGifMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainPanel.replaceWithGifButtonActionPerformed(getCurrentItem());
mainPanel.replaceSpriteWithGifButtonActionPerformed(getCurrentItem());
}
});
replaceWithGifMenuItem.setIcon(View.getIcon("replacesprite16"));